| /* |
| * Copyright (c) 2024 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @brief File containing WiFi management operation implementations |
| * for the Zephyr OS. |
| */ |
| |
| #include <stdlib.h> |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/logging/log.h> |
| |
| #include "util.h" |
| #include "fmac_api.h" |
| #include "fmac_tx.h" |
| #include "fmac_util.h" |
| #include "fmac_main.h" |
| #include "wifi_mgmt.h" |
| |
| LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); |
| |
| extern struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep; |
| |
| int nrf_wifi_set_power_save(const struct device *dev, |
| struct wifi_ps_params *params) |
| { |
| enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; |
| struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| int ret = -1; |
| unsigned int uapsd_queue = UAPSD_Q_MIN; /* Legacy mode */ |
| |
| if (!dev || !params) { |
| LOG_ERR("%s: dev or params is NULL", __func__); |
| return ret; |
| } |
| |
| vif_ctx_zep = dev->data; |
| |
| if (!vif_ctx_zep) { |
| LOG_ERR("%s: vif_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; |
| |
| if (!rpu_ctx_zep) { |
| LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); |
| if (!rpu_ctx_zep->rpu_ctx) { |
| LOG_DBG("%s: RPU context not initialized", __func__); |
| goto out; |
| } |
| |
| switch (params->type) { |
| case WIFI_PS_PARAM_LISTEN_INTERVAL: |
| if ((params->listen_interval < |
| NRF_WIFI_LISTEN_INTERVAL_MIN) || |
| (params->listen_interval > |
| WIFI_LISTEN_INTERVAL_MAX)) { |
| params->fail_reason = |
| WIFI_PS_PARAM_LISTEN_INTERVAL_RANGE_INVALID; |
| return -EINVAL; |
| } |
| status = nrf_wifi_fmac_set_listen_interval( |
| rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx, |
| params->listen_interval); |
| break; |
| case WIFI_PS_PARAM_TIMEOUT: |
| if ((vif_ctx_zep->if_type != NRF_WIFI_IFTYPE_STATION) |
| #ifdef CONFIG_NRF70_RAW_DATA_TX |
| && (vif_ctx_zep->if_type != NRF_WIFI_STA_TX_INJECTOR) |
| #endif /* CONFIG_NRF70_RAW_DATA_TX */ |
| #ifdef CONFIG_NRF70_PROMISC_DATA_RX |
| && (vif_ctx_zep->if_type != NRF_WIFI_STA_PROMISC_TX_INJECTOR) |
| #endif /* CONFIG_NRF70_PROMISC_DATA_RX */ |
| ) { |
| LOG_ERR("%s: Operation supported only in STA enabled mode", |
| __func__); |
| params->fail_reason = |
| WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; |
| goto out; |
| } |
| |
| status = nrf_wifi_fmac_set_power_save_timeout( |
| rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx, |
| params->timeout_ms); |
| break; |
| case WIFI_PS_PARAM_MODE: |
| if (params->mode == WIFI_PS_MODE_WMM) { |
| uapsd_queue = UAPSD_Q_MAX; /* WMM mode */ |
| } |
| |
| status = nrf_wifi_fmac_set_uapsd_queue(rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx, |
| uapsd_queue); |
| break; |
| case WIFI_PS_PARAM_STATE: |
| status = nrf_wifi_fmac_set_power_save(rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx, |
| params->enabled); |
| break; |
| case WIFI_PS_PARAM_WAKEUP_MODE: |
| status = nrf_wifi_fmac_set_ps_wakeup_mode( |
| rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx, |
| params->wakeup_mode); |
| break; |
| default: |
| params->fail_reason = |
| WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; |
| return -ENOTSUP; |
| } |
| |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: Confiuring PS param %d failed", |
| __func__, params->type); |
| params->fail_reason = |
| WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; |
| goto out; |
| } |
| |
| |
| ret = 0; |
| out: |
| k_mutex_unlock(&vif_ctx_zep->vif_lock); |
| return ret; |
| } |
| |
| int nrf_wifi_get_power_save_config(const struct device *dev, |
| struct wifi_ps_config *ps_config) |
| { |
| enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; |
| struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; |
| int ret = -1; |
| int count = 0; |
| |
| if (!dev || !ps_config) { |
| return ret; |
| } |
| |
| vif_ctx_zep = dev->data; |
| |
| if (!vif_ctx_zep) { |
| LOG_ERR("%s: vif_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| if ((vif_ctx_zep->if_type != NRF_WIFI_IFTYPE_STATION) |
| #ifdef CONFIG_NRF70_RAW_DATA_TX |
| && (vif_ctx_zep->if_type != NRF_WIFI_STA_TX_INJECTOR) |
| #endif /* CONFIG_NRF70_RAW_DATA_TX */ |
| #ifdef CONFIG_NRF70_PROMISC_DATA_RX |
| && (vif_ctx_zep->if_type != NRF_WIFI_STA_PROMISC_TX_INJECTOR) |
| #endif /* CONFIG_NRF70_PROMISC_DATA_RX */ |
| ) { |
| LOG_ERR("%s: Operation supported only in STA enabled mode", |
| __func__); |
| return ret; |
| } |
| |
| rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; |
| if (!rpu_ctx_zep) { |
| LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); |
| if (!rpu_ctx_zep->rpu_ctx) { |
| LOG_DBG("%s: RPU context not initialized", __func__); |
| goto out; |
| } |
| |
| fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; |
| |
| if (!rpu_ctx_zep) { |
| LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); |
| goto out; |
| } |
| |
| vif_ctx_zep->ps_info = ps_config; |
| |
| vif_ctx_zep->ps_config_info_evnt = false; |
| |
| status = nrf_wifi_fmac_get_power_save_info(rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx); |
| |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: nrf_wifi_fmac_get_power_save_info failed", |
| __func__); |
| goto out; |
| } |
| |
| do { |
| nrf_wifi_osal_sleep_ms(1); |
| count++; |
| } while ((vif_ctx_zep->ps_config_info_evnt == false) && |
| (count < NRF_WIFI_FMAC_PS_CONF_EVNT_RECV_TIMEOUT)); |
| |
| if (count == NRF_WIFI_FMAC_PS_CONF_EVNT_RECV_TIMEOUT) { |
| nrf_wifi_osal_log_err("%s: Timed out", |
| __func__); |
| goto out; |
| } |
| |
| ret = 0; |
| out: |
| k_mutex_unlock(&vif_ctx_zep->vif_lock); |
| return ret; |
| } |
| |
| /* TWT interval conversion helpers: User <-> Protocol */ |
| static struct twt_interval_float nrf_wifi_twt_us_to_float(uint32_t twt_interval) |
| { |
| double mantissa = 0.0; |
| int exponent = 0; |
| struct twt_interval_float twt_interval_fp; |
| |
| double twt_interval_ms = twt_interval / 1000.0; |
| |
| mantissa = frexp(twt_interval_ms, &exponent); |
| /* Ceiling and conversion to milli seconds */ |
| twt_interval_fp.mantissa = ceil(mantissa * 1000); |
| twt_interval_fp.exponent = exponent; |
| |
| return twt_interval_fp; |
| } |
| |
| static uint64_t nrf_wifi_twt_float_to_us(struct twt_interval_float twt_interval_fp) |
| { |
| /* Conversion to micro-seconds */ |
| return floor(ldexp(twt_interval_fp.mantissa, twt_interval_fp.exponent) / (1000)) * |
| 1000; |
| } |
| |
| static unsigned char twt_wifi_mgmt_to_rpu_neg_type(enum wifi_twt_negotiation_type neg_type) |
| { |
| unsigned char rpu_neg_type = 0; |
| |
| switch (neg_type) { |
| case WIFI_TWT_INDIVIDUAL: |
| rpu_neg_type = NRF_WIFI_TWT_NEGOTIATION_TYPE_INDIVIDUAL; |
| break; |
| case WIFI_TWT_BROADCAST: |
| rpu_neg_type = NRF_WIFI_TWT_NEGOTIATION_TYPE_BROADCAST; |
| break; |
| default: |
| LOG_ERR("%s: Invalid negotiation type: %d", |
| __func__, neg_type); |
| break; |
| } |
| |
| return rpu_neg_type; |
| } |
| |
| static enum wifi_twt_negotiation_type twt_rpu_to_wifi_mgmt_neg_type(unsigned char neg_type) |
| { |
| enum wifi_twt_negotiation_type wifi_neg_type = WIFI_TWT_INDIVIDUAL; |
| |
| switch (neg_type) { |
| case NRF_WIFI_TWT_NEGOTIATION_TYPE_INDIVIDUAL: |
| wifi_neg_type = WIFI_TWT_INDIVIDUAL; |
| break; |
| case NRF_WIFI_TWT_NEGOTIATION_TYPE_BROADCAST: |
| wifi_neg_type = WIFI_TWT_BROADCAST; |
| break; |
| default: |
| LOG_ERR("%s: Invalid negotiation type: %d", |
| __func__, neg_type); |
| break; |
| } |
| |
| return wifi_neg_type; |
| } |
| |
| /* Though setup_cmd enums have 1-1 mapping but due to data type different need these */ |
| static enum wifi_twt_setup_cmd twt_rpu_to_wifi_mgmt_setup_cmd(signed int setup_cmd) |
| { |
| enum wifi_twt_setup_cmd wifi_setup_cmd = WIFI_TWT_SETUP_CMD_REQUEST; |
| |
| switch (setup_cmd) { |
| case NRF_WIFI_REQUEST_TWT: |
| wifi_setup_cmd = WIFI_TWT_SETUP_CMD_REQUEST; |
| break; |
| case NRF_WIFI_SUGGEST_TWT: |
| wifi_setup_cmd = WIFI_TWT_SETUP_CMD_SUGGEST; |
| break; |
| case NRF_WIFI_DEMAND_TWT: |
| wifi_setup_cmd = WIFI_TWT_SETUP_CMD_DEMAND; |
| break; |
| case NRF_WIFI_GROUPING_TWT: |
| wifi_setup_cmd = WIFI_TWT_SETUP_CMD_GROUPING; |
| break; |
| case NRF_WIFI_ACCEPT_TWT: |
| wifi_setup_cmd = WIFI_TWT_SETUP_CMD_ACCEPT; |
| break; |
| case NRF_WIFI_ALTERNATE_TWT: |
| wifi_setup_cmd = WIFI_TWT_SETUP_CMD_ALTERNATE; |
| break; |
| case NRF_WIFI_DICTATE_TWT: |
| wifi_setup_cmd = WIFI_TWT_SETUP_CMD_DICTATE; |
| break; |
| case NRF_WIFI_REJECT_TWT: |
| wifi_setup_cmd = WIFI_TWT_SETUP_CMD_REJECT; |
| break; |
| default: |
| LOG_ERR("%s: Invalid setup command: %d", |
| __func__, setup_cmd); |
| break; |
| } |
| |
| return wifi_setup_cmd; |
| } |
| |
| static signed int twt_wifi_mgmt_to_rpu_setup_cmd(enum wifi_twt_setup_cmd setup_cmd) |
| { |
| signed int rpu_setup_cmd = NRF_WIFI_REQUEST_TWT; |
| |
| switch (setup_cmd) { |
| case WIFI_TWT_SETUP_CMD_REQUEST: |
| rpu_setup_cmd = NRF_WIFI_REQUEST_TWT; |
| break; |
| case WIFI_TWT_SETUP_CMD_SUGGEST: |
| rpu_setup_cmd = NRF_WIFI_SUGGEST_TWT; |
| break; |
| case WIFI_TWT_SETUP_CMD_DEMAND: |
| rpu_setup_cmd = NRF_WIFI_DEMAND_TWT; |
| break; |
| case WIFI_TWT_SETUP_CMD_GROUPING: |
| rpu_setup_cmd = NRF_WIFI_GROUPING_TWT; |
| break; |
| case WIFI_TWT_SETUP_CMD_ACCEPT: |
| rpu_setup_cmd = NRF_WIFI_ACCEPT_TWT; |
| break; |
| case WIFI_TWT_SETUP_CMD_ALTERNATE: |
| rpu_setup_cmd = NRF_WIFI_ALTERNATE_TWT; |
| break; |
| case WIFI_TWT_SETUP_CMD_DICTATE: |
| rpu_setup_cmd = NRF_WIFI_DICTATE_TWT; |
| break; |
| case WIFI_TWT_SETUP_CMD_REJECT: |
| rpu_setup_cmd = NRF_WIFI_REJECT_TWT; |
| break; |
| default: |
| LOG_ERR("%s: Invalid setup command: %d", |
| __func__, setup_cmd); |
| break; |
| } |
| |
| return rpu_setup_cmd; |
| } |
| |
| void 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) |
| { |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| |
| if (!vif_ctx || !ps_info) { |
| return; |
| } |
| |
| vif_ctx_zep = vif_ctx; |
| |
| vif_ctx_zep->ps_info->ps_params.mode = ps_info->ps_mode; |
| vif_ctx_zep->ps_info->ps_params.enabled = ps_info->enabled; |
| vif_ctx_zep->ps_info->num_twt_flows = ps_info->num_twt_flows; |
| vif_ctx_zep->ps_info->ps_params.timeout_ms = ps_info->ps_timeout; |
| vif_ctx_zep->ps_info->ps_params.listen_interval = ps_info->listen_interval; |
| vif_ctx_zep->ps_info->ps_params.wakeup_mode = ps_info->extended_ps; |
| |
| for (int i = 0; i < ps_info->num_twt_flows; i++) { |
| struct twt_interval_float twt_interval_fp; |
| struct wifi_twt_flow_info *twt_zep = &vif_ctx_zep->ps_info->twt_flows[i]; |
| struct nrf_wifi_umac_config_twt_info *twt_rpu = &ps_info->twt_flow_info[i]; |
| |
| memset(twt_zep, 0, sizeof(struct wifi_twt_flow_info)); |
| |
| twt_zep->flow_id = twt_rpu->twt_flow_id; |
| twt_zep->implicit = twt_rpu->is_implicit ? 1 : 0; |
| twt_zep->trigger = twt_rpu->ap_trigger_frame ? 1 : 0; |
| twt_zep->announce = twt_rpu->twt_flow_type == NRF_WIFI_TWT_FLOW_TYPE_ANNOUNCED; |
| twt_zep->negotiation_type = twt_rpu_to_wifi_mgmt_neg_type(twt_rpu->neg_type); |
| twt_zep->dialog_token = twt_rpu->dialog_token; |
| twt_interval_fp.mantissa = twt_rpu->twt_target_wake_interval_mantissa; |
| twt_interval_fp.exponent = twt_rpu->twt_target_wake_interval_exponent; |
| twt_zep->twt_interval = nrf_wifi_twt_float_to_us(twt_interval_fp); |
| twt_zep->twt_wake_interval = twt_rpu->nominal_min_twt_wake_duration; |
| } |
| |
| vif_ctx_zep->ps_config_info_evnt = true; |
| } |
| |
| static void nrf_wifi_twt_update_internal_state(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, |
| bool setup, unsigned char flow_id) |
| { |
| if (setup) { |
| vif_ctx_zep->twt_flows_map |= BIT(flow_id); |
| vif_ctx_zep->twt_flow_in_progress_map &= ~BIT(flow_id); |
| } else { |
| vif_ctx_zep->twt_flows_map &= ~BIT(flow_id); |
| } |
| } |
| |
| int nrf_wifi_twt_teardown_flows(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, |
| unsigned char start_flow_id, unsigned char end_flow_id) |
| { |
| struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; |
| struct nrf_wifi_umac_config_twt_info twt_info = {0}; |
| enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; |
| int ret = 0; |
| struct wifi_twt_params twt_params = {0}; |
| |
| if (!vif_ctx_zep) { |
| LOG_ERR("%s: vif_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; |
| |
| if (!rpu_ctx_zep) { |
| LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); |
| if (!rpu_ctx_zep->rpu_ctx) { |
| LOG_DBG("%s: RPU context not initialized", __func__); |
| goto out; |
| } |
| |
| for (int flow_id = start_flow_id; flow_id < end_flow_id; flow_id++) { |
| if (!(vif_ctx_zep->twt_flows_map & BIT(flow_id))) { |
| continue; |
| } |
| twt_info.twt_flow_id = flow_id; |
| status = nrf_wifi_fmac_twt_teardown(rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx, |
| &twt_info); |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: TWT teardown for flow id %d failed", |
| __func__, flow_id); |
| ret = -1; |
| continue; |
| } |
| /* UMAC doesn't send TWT teardown event for host initiated teardown */ |
| nrf_wifi_twt_update_internal_state(vif_ctx_zep, false, flow_id); |
| /* TODO: Remove this once UMAC sends the status */ |
| twt_params.operation = WIFI_TWT_TEARDOWN; |
| twt_params.flow_id = flow_id; |
| twt_params.teardown_status = WIFI_TWT_TEARDOWN_SUCCESS; |
| wifi_mgmt_raise_twt_event(vif_ctx_zep->zep_net_if_ctx, &twt_params); |
| } |
| |
| out: |
| k_mutex_unlock(&vif_ctx_zep->vif_lock); |
| return ret; |
| } |
| |
| int nrf_wifi_set_twt(const struct device *dev, |
| struct wifi_twt_params *twt_params) |
| { |
| enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; |
| struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| struct nrf_wifi_umac_config_twt_info twt_info = {0}; |
| int ret = -1; |
| |
| if (!dev || !twt_params) { |
| LOG_ERR("%s: dev or twt_params is NULL", __func__); |
| return ret; |
| } |
| |
| vif_ctx_zep = dev->data; |
| |
| if (!vif_ctx_zep) { |
| LOG_ERR("%s: vif_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; |
| |
| if (!rpu_ctx_zep) { |
| LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); |
| if (!rpu_ctx_zep->rpu_ctx) { |
| LOG_DBG("%s: RPU context not initialized", __func__); |
| goto out; |
| } |
| |
| if (!(twt_params->operation == WIFI_TWT_TEARDOWN && twt_params->teardown.teardown_all) && |
| twt_params->flow_id >= WIFI_MAX_TWT_FLOWS) { |
| LOG_ERR("%s: Invalid flow id: %d", |
| __func__, twt_params->flow_id); |
| twt_params->fail_reason = WIFI_TWT_FAIL_INVALID_FLOW_ID; |
| goto out; |
| } |
| |
| switch (twt_params->operation) { |
| case WIFI_TWT_SETUP: |
| if (vif_ctx_zep->twt_flow_in_progress_map & BIT(twt_params->flow_id)) { |
| twt_params->fail_reason = WIFI_TWT_FAIL_OPERATION_IN_PROGRESS; |
| goto out; |
| } |
| |
| if (twt_params->setup_cmd == WIFI_TWT_SETUP_CMD_REQUEST) { |
| if (vif_ctx_zep->twt_flows_map & BIT(twt_params->flow_id)) { |
| twt_params->fail_reason = WIFI_TWT_FAIL_FLOW_ALREADY_EXISTS; |
| goto out; |
| } |
| } |
| |
| struct twt_interval_float twt_interval_fp = |
| nrf_wifi_twt_us_to_float(twt_params->setup.twt_interval); |
| |
| twt_info.twt_flow_id = twt_params->flow_id; |
| twt_info.neg_type = twt_wifi_mgmt_to_rpu_neg_type(twt_params->negotiation_type); |
| twt_info.setup_cmd = twt_wifi_mgmt_to_rpu_setup_cmd(twt_params->setup_cmd); |
| twt_info.ap_trigger_frame = twt_params->setup.trigger; |
| twt_info.is_implicit = twt_params->setup.implicit; |
| if (twt_params->setup.announce) { |
| twt_info.twt_flow_type = NRF_WIFI_TWT_FLOW_TYPE_ANNOUNCED; |
| } else { |
| twt_info.twt_flow_type = NRF_WIFI_TWT_FLOW_TYPE_UNANNOUNCED; |
| } |
| |
| twt_info.nominal_min_twt_wake_duration = |
| twt_params->setup.twt_wake_interval; |
| twt_info.twt_target_wake_interval_mantissa = twt_interval_fp.mantissa; |
| twt_info.twt_target_wake_interval_exponent = twt_interval_fp.exponent; |
| |
| twt_info.dialog_token = twt_params->dialog_token; |
| twt_info.twt_wake_ahead_duration = twt_params->setup.twt_wake_ahead_duration; |
| |
| status = nrf_wifi_fmac_twt_setup(rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx, |
| &twt_info); |
| |
| break; |
| case WIFI_TWT_TEARDOWN: |
| unsigned char start_flow_id = 0; |
| unsigned char end_flow_id = WIFI_MAX_TWT_FLOWS; |
| |
| if (!twt_params->teardown.teardown_all) { |
| if (!(vif_ctx_zep->twt_flows_map & BIT(twt_params->flow_id))) { |
| twt_params->fail_reason = WIFI_TWT_FAIL_INVALID_FLOW_ID; |
| goto out; |
| } |
| start_flow_id = twt_params->flow_id; |
| end_flow_id = twt_params->flow_id + 1; |
| twt_info.twt_flow_id = twt_params->flow_id; |
| } |
| |
| status = nrf_wifi_twt_teardown_flows(vif_ctx_zep, |
| start_flow_id, |
| end_flow_id); |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: TWT teardown failed: start_flow_id: %d, end_flow_id: %d", |
| __func__, start_flow_id, end_flow_id); |
| goto out; |
| } |
| break; |
| |
| default: |
| LOG_ERR("Unknown TWT operation"); |
| status = NRF_WIFI_STATUS_FAIL; |
| break; |
| } |
| |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: Failed", __func__); |
| goto out; |
| } |
| |
| ret = 0; |
| out: |
| k_mutex_unlock(&vif_ctx_zep->vif_lock); |
| return ret; |
| } |
| |
| void nrf_wifi_event_proc_twt_setup_zep(void *vif_ctx, |
| struct nrf_wifi_umac_cmd_config_twt *twt_setup_info, |
| unsigned int event_len) |
| { |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| struct wifi_twt_params twt_params; |
| struct twt_interval_float twt_interval_fp; |
| |
| if (!vif_ctx || !twt_setup_info) { |
| return; |
| } |
| |
| vif_ctx_zep = vif_ctx; |
| |
| twt_params.operation = WIFI_TWT_SETUP; |
| twt_params.flow_id = twt_setup_info->info.twt_flow_id; |
| twt_params.negotiation_type = twt_rpu_to_wifi_mgmt_neg_type(twt_setup_info->info.neg_type); |
| twt_params.setup_cmd = twt_rpu_to_wifi_mgmt_setup_cmd(twt_setup_info->info.setup_cmd); |
| twt_params.setup.trigger = twt_setup_info->info.ap_trigger_frame ? 1 : 0; |
| twt_params.setup.implicit = twt_setup_info->info.is_implicit ? 1 : 0; |
| twt_params.setup.announce = |
| twt_setup_info->info.twt_flow_type == NRF_WIFI_TWT_FLOW_TYPE_ANNOUNCED; |
| twt_params.setup.twt_wake_interval = |
| twt_setup_info->info.nominal_min_twt_wake_duration; |
| twt_interval_fp.mantissa = twt_setup_info->info.twt_target_wake_interval_mantissa; |
| twt_interval_fp.exponent = twt_setup_info->info.twt_target_wake_interval_exponent; |
| twt_params.setup.twt_interval = nrf_wifi_twt_float_to_us(twt_interval_fp); |
| twt_params.dialog_token = twt_setup_info->info.dialog_token; |
| twt_params.resp_status = twt_setup_info->info.twt_resp_status; |
| |
| if ((twt_setup_info->info.twt_resp_status == 0) || |
| (twt_setup_info->info.neg_type == NRF_WIFI_ACCEPT_TWT)) { |
| nrf_wifi_twt_update_internal_state(vif_ctx_zep, true, twt_params.flow_id); |
| } |
| |
| wifi_mgmt_raise_twt_event(vif_ctx_zep->zep_net_if_ctx, &twt_params); |
| } |
| |
| |
| void nrf_wifi_event_proc_twt_teardown_zep(void *vif_ctx, |
| struct nrf_wifi_umac_cmd_teardown_twt *twt_teardown_info, |
| unsigned int event_len) |
| { |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| struct wifi_twt_params twt_params = {0}; |
| |
| if (!vif_ctx || !twt_teardown_info) { |
| return; |
| } |
| |
| vif_ctx_zep = vif_ctx; |
| |
| twt_params.operation = WIFI_TWT_TEARDOWN; |
| twt_params.flow_id = twt_teardown_info->info.twt_flow_id; |
| /* TODO: ADD reason code in the twt_params structure */ |
| nrf_wifi_twt_update_internal_state(vif_ctx_zep, false, twt_params.flow_id); |
| |
| wifi_mgmt_raise_twt_event(vif_ctx_zep->zep_net_if_ctx, &twt_params); |
| } |
| |
| void nrf_wifi_event_proc_twt_sleep_zep(void *vif_ctx, |
| struct nrf_wifi_umac_event_twt_sleep *sleep_evnt, |
| unsigned int event_len) |
| { |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; |
| struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; |
| struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; |
| struct nrf_wifi_fmac_priv_def *def_priv = NULL; |
| #ifdef CONFIG_NRF70_DATA_TX |
| int desc = 0; |
| int ac = 0; |
| #endif |
| vif_ctx_zep = vif_ctx; |
| |
| if (!vif_ctx_zep) { |
| LOG_ERR("%s: vif_ctx_zep is NULL", __func__); |
| return; |
| } |
| |
| rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; |
| if (!rpu_ctx_zep) { |
| LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); |
| return; |
| } |
| |
| k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); |
| if (!rpu_ctx_zep->rpu_ctx) { |
| LOG_DBG("%s: RPU context not initialized", __func__); |
| goto out; |
| } |
| |
| fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; |
| def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); |
| def_priv = wifi_fmac_priv(fmac_dev_ctx->fpriv); |
| |
| if (!sleep_evnt) { |
| LOG_ERR("%s: sleep_evnt is NULL", __func__); |
| return; |
| } |
| |
| switch (sleep_evnt->info.type) { |
| case TWT_BLOCK_TX: |
| nrf_wifi_osal_spinlock_take(def_dev_ctx->tx_config.tx_lock); |
| |
| def_dev_ctx->twt_sleep_status = NRF_WIFI_FMAC_TWT_STATE_SLEEP; |
| |
| wifi_mgmt_raise_twt_sleep_state(vif_ctx_zep->zep_net_if_ctx, |
| WIFI_TWT_STATE_SLEEP); |
| nrf_wifi_osal_spinlock_rel(def_dev_ctx->tx_config.tx_lock); |
| break; |
| case TWT_UNBLOCK_TX: |
| nrf_wifi_osal_spinlock_take(def_dev_ctx->tx_config.tx_lock); |
| def_dev_ctx->twt_sleep_status = NRF_WIFI_FMAC_TWT_STATE_AWAKE; |
| wifi_mgmt_raise_twt_sleep_state(vif_ctx_zep->zep_net_if_ctx, |
| WIFI_TWT_STATE_AWAKE); |
| #ifdef CONFIG_NRF70_DATA_TX |
| for (ac = NRF_WIFI_FMAC_AC_BE; |
| ac <= NRF_WIFI_FMAC_AC_MAX; ++ac) { |
| desc = tx_desc_get(fmac_dev_ctx, ac); |
| if (desc < def_priv->num_tx_tokens) { |
| tx_pending_process(fmac_dev_ctx, desc, ac); |
| } |
| } |
| #endif |
| nrf_wifi_osal_spinlock_rel(def_dev_ctx->tx_config.tx_lock); |
| break; |
| default: |
| break; |
| } |
| out: |
| k_mutex_unlock(&vif_ctx_zep->vif_lock); |
| } |
| |
| #ifdef CONFIG_NRF70_SYSTEM_WITH_RAW_MODES |
| int nrf_wifi_mode(const struct device *dev, |
| struct wifi_mode_info *mode) |
| { |
| enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; |
| struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; |
| struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; |
| int ret = -1; |
| |
| if (!dev || !mode) { |
| LOG_ERR("%s: illegal input parameters", __func__); |
| return ret; |
| } |
| |
| vif_ctx_zep = dev->data; |
| if (!vif_ctx_zep) { |
| LOG_ERR("%s: vif_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; |
| if (!rpu_ctx_zep) { |
| LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); |
| if (!rpu_ctx_zep->rpu_ctx) { |
| LOG_DBG("%s: RPU context not initialized", __func__); |
| goto out; |
| } |
| |
| fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; |
| def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); |
| |
| if (!device_is_ready(dev)) { |
| LOG_ERR("%s: Device %s is not ready", |
| __func__, dev->name); |
| goto out; |
| } |
| |
| if (mode->oper == WIFI_MGMT_SET) { |
| status = nrf_wifi_check_mode_validity(mode->mode); |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: mode setting is not valid", __func__); |
| goto out; |
| } |
| |
| if (vif_ctx_zep->authorized && (mode->mode == NRF_WIFI_MONITOR_MODE)) { |
| LOG_ERR("%s: Cannot set monitor mode when station is connected", |
| __func__); |
| goto out; |
| } |
| |
| /** |
| * Send the driver vif_idx instead of upper layer sent if_index. |
| * we map network if_index 1 to vif_idx of 0 and so on. The vif_ctx_zep |
| * context maps the correct network interface index to current driver |
| * interface index. |
| */ |
| status = nrf_wifi_fmac_set_mode(rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx, mode->mode); |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: mode set operation failed", __func__); |
| goto out; |
| } |
| |
| } else { |
| mode->mode = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode; |
| /** |
| * This is a work-around to handle current UMAC mode handling. |
| * This might be removed in future versions when UMAC has more space. |
| */ |
| #ifdef CONFIG_NRF70_RAW_DATA_TX |
| if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->txinjection_mode == true) { |
| mode->mode ^= NRF_WIFI_TX_INJECTION_MODE; |
| } |
| #endif /* CONFIG_NRF70_RAW_DATA_TX */ |
| #ifdef CONFIG_NRF70_PROMISC_DATA_RX |
| if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->promisc_mode == true) { |
| mode->mode ^= NRF_WIFI_PROMISCUOUS_MODE; |
| } |
| #endif /* CONFIG_NRF70_PROMISC_DATA_RX */ |
| } |
| ret = 0; |
| out: |
| k_mutex_unlock(&vif_ctx_zep->vif_lock); |
| return ret; |
| } |
| #endif /* CONFIG_NRF70_SYSTEM_WITH_RAW_MODES */ |
| |
| #if defined(CONFIG_NRF70_RAW_DATA_TX) || defined(CONFIG_NRF70_RAW_DATA_RX) |
| int nrf_wifi_channel(const struct device *dev, |
| struct wifi_channel_info *channel) |
| { |
| enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; |
| struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; |
| struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; |
| int ret = -1; |
| |
| if (!dev || !channel) { |
| LOG_ERR("%s: illegal input parameters", __func__); |
| return ret; |
| } |
| |
| vif_ctx_zep = dev->data; |
| if (!vif_ctx_zep) { |
| LOG_ERR("%s: vif_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| if (vif_ctx_zep->authorized) { |
| LOG_ERR("%s: Cannot change channel when in station connected mode", __func__); |
| return ret; |
| } |
| |
| rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; |
| if (!rpu_ctx_zep) { |
| LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); |
| if (!rpu_ctx_zep->rpu_ctx) { |
| LOG_DBG("%s: RPU context not initialized", __func__); |
| goto out; |
| } |
| |
| fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; |
| def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); |
| |
| if (channel->oper == WIFI_MGMT_SET) { |
| /** |
| * Send the driver vif_idx instead of upper layer sent if_index. |
| * we map network if_index 1 to vif_idx of 0 and so on. The vif_ctx_zep |
| * context maps the correct network interface index to current driver |
| * interface index. |
| */ |
| status = nrf_wifi_fmac_set_channel(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, |
| channel->channel); |
| |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: set channel failed", __func__); |
| goto out; |
| } |
| } else { |
| channel->channel = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->channel; |
| } |
| ret = 0; |
| out: |
| k_mutex_unlock(&vif_ctx_zep->vif_lock); |
| return ret; |
| } |
| #endif /* CONFIG_NRF70_RAW_DATA_TX || CONFIG_NRF70_RAW_DATA_RX */ |
| |
| #if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX) |
| int nrf_wifi_filter(const struct device *dev, |
| struct wifi_filter_info *filter) |
| { |
| enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; |
| struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; |
| struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; |
| int ret = -1; |
| |
| if (!dev || !filter) { |
| LOG_ERR("%s: Illegal input parameters", __func__); |
| goto out; |
| } |
| |
| vif_ctx_zep = dev->data; |
| if (!vif_ctx_zep) { |
| LOG_ERR("%s: vif_ctx_zep is NULL\n", __func__); |
| goto out; |
| } |
| |
| rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; |
| fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; |
| def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); |
| |
| if (filter->oper == WIFI_MGMT_SET) { |
| /** |
| * In case a user sets data + management + ctrl bits |
| * or all the filter bits. Map it to bit 0 set to |
| * enable "all" packet filter bit setting. |
| * In case only filter packet size is configured and filter |
| * setting is sent as zero, set the filter value to |
| * previously configured value. |
| */ |
| if (filter->filter == WIFI_MGMT_DATA_CTRL_FILTER_SETTING |
| || filter->filter == WIFI_ALL_FILTER_SETTING) { |
| filter->filter = 1; |
| } else if (filter->filter == 0) { |
| filter->filter = |
| def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->packet_filter; |
| } |
| |
| /** |
| * Send the driver vif_idx instead of upper layer sent if_index. |
| * we map network if_index 1 to vif_idx of 0 and so on. The vif_ctx_zep |
| * context maps the correct network interface index to current driver |
| * interface index |
| */ |
| status = nrf_wifi_fmac_set_packet_filter(rpu_ctx_zep->rpu_ctx, filter->filter, |
| vif_ctx_zep->vif_idx, filter->buffer_size); |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: Set filter operation failed\n", __func__); |
| goto out; |
| } |
| } else { |
| filter->filter = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->packet_filter; |
| } |
| ret = 0; |
| out: |
| return ret; |
| } |
| #endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */ |
| |
| int nrf_wifi_set_rts_threshold(const struct device *dev, |
| unsigned int rts_threshold) |
| { |
| enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; |
| struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; |
| struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; |
| struct nrf_wifi_umac_set_wiphy_info wiphy_info; |
| int ret = -1; |
| |
| if (!dev) { |
| LOG_ERR("%s: dev is NULL", __func__); |
| return ret; |
| } |
| |
| vif_ctx_zep = dev->data; |
| |
| if (!vif_ctx_zep) { |
| LOG_ERR("%s: vif_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; |
| |
| if (!rpu_ctx_zep) { |
| LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); |
| return ret; |
| } |
| |
| |
| if (!rpu_ctx_zep->rpu_ctx) { |
| LOG_ERR("%s: RPU context not initialized", __func__); |
| return ret; |
| } |
| |
| if ((int)rts_threshold < -1) { |
| /* 0 or any positive value is passed to f/w. |
| * For RTS off, -1 is passed to f/w. |
| * All other negative values considered as invalid. |
| */ |
| LOG_ERR("%s: Invalid threshold value : %d", __func__, (int)rts_threshold); |
| return ret; |
| } |
| |
| memset(&wiphy_info, 0, sizeof(struct nrf_wifi_umac_set_wiphy_info)); |
| |
| wiphy_info.rts_threshold = (int)rts_threshold; |
| |
| k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); |
| |
| status = nrf_wifi_fmac_set_wiphy_params(rpu_ctx_zep->rpu_ctx, |
| vif_ctx_zep->vif_idx, |
| &wiphy_info); |
| |
| if (status != NRF_WIFI_STATUS_SUCCESS) { |
| LOG_ERR("%s: Configuring rts threshold failed\n", __func__); |
| goto out; |
| } |
| |
| ret = 0; |
| out: |
| k_mutex_unlock(&vif_ctx_zep->vif_lock); |
| |
| return ret; |
| } |