| /* |
| * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: wlan_policy_mgr_action.c |
| * |
| * WLAN Concurrenct Connection Management APIs |
| * |
| */ |
| |
| /* Include files */ |
| |
| #include "wlan_policy_mgr_api.h" |
| #include "wlan_policy_mgr_tables_no_dbs_i.h" |
| #include "wlan_policy_mgr_i.h" |
| #include "qdf_types.h" |
| #include "qdf_trace.h" |
| #include "wlan_objmgr_global_obj.h" |
| #include "qdf_platform.h" |
| |
| enum policy_mgr_conc_next_action (*policy_mgr_get_current_pref_hw_mode_ptr) |
| (struct wlan_objmgr_psoc *psoc); |
| |
| void policy_mgr_hw_mode_transition_cb(uint32_t old_hw_mode_index, |
| uint32_t new_hw_mode_index, |
| uint32_t num_vdev_mac_entries, |
| struct policy_mgr_vdev_mac_map *vdev_mac_map, |
| struct wlan_objmgr_psoc *context) |
| { |
| QDF_STATUS status; |
| struct policy_mgr_hw_mode_params hw_mode; |
| uint32_t i; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(context); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| return; |
| } |
| |
| if (!vdev_mac_map) { |
| policy_mgr_err("vdev_mac_map is NULL"); |
| return; |
| } |
| |
| policy_mgr_debug("old_hw_mode_index=%d, new_hw_mode_index=%d", |
| old_hw_mode_index, new_hw_mode_index); |
| |
| for (i = 0; i < num_vdev_mac_entries; i++) |
| policy_mgr_debug("vdev_id:%d mac_id:%d", |
| vdev_mac_map[i].vdev_id, |
| vdev_mac_map[i].mac_id); |
| |
| status = policy_mgr_get_hw_mode_from_idx(context, |
| new_hw_mode_index, &hw_mode); |
| if (status != QDF_STATUS_SUCCESS) { |
| policy_mgr_err("Get HW mode failed: %d", status); |
| return; |
| } |
| |
| policy_mgr_debug("MAC0: TxSS:%d, RxSS:%d, Bw:%d band_cap:%d", |
| hw_mode.mac0_tx_ss, hw_mode.mac0_rx_ss, |
| hw_mode.mac0_bw, hw_mode.mac0_band_cap); |
| policy_mgr_debug("MAC1: TxSS:%d, RxSS:%d, Bw:%d", |
| hw_mode.mac1_tx_ss, hw_mode.mac1_rx_ss, |
| hw_mode.mac1_bw); |
| policy_mgr_debug("DBS:%d, Agile DFS:%d, SBS:%d", |
| hw_mode.dbs_cap, hw_mode.agile_dfs_cap, |
| hw_mode.sbs_cap); |
| |
| /* update pm_conc_connection_list */ |
| policy_mgr_update_hw_mode_conn_info(context, num_vdev_mac_entries, |
| vdev_mac_map, |
| hw_mode); |
| |
| if (pm_ctx->mode_change_cb) |
| pm_ctx->mode_change_cb(); |
| |
| return; |
| } |
| |
| QDF_STATUS policy_mgr_check_n_start_opportunistic_timer( |
| struct wlan_objmgr_psoc *psoc) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("PM ctx not valid. Oppurtunistic timer cannot start"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| if (policy_mgr_need_opportunistic_upgrade(psoc, NULL)) { |
| /* let's start the timer */ |
| qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); |
| status = qdf_mc_timer_start( |
| &pm_ctx->dbs_opportunistic_timer, |
| DBS_OPPORTUNISTIC_TIME * 1000); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| policy_mgr_err("Failed to start dbs opportunistic timer"); |
| } |
| return status; |
| } |
| |
| QDF_STATUS policy_mgr_pdev_set_hw_mode(struct wlan_objmgr_psoc *psoc, |
| uint32_t session_id, |
| enum hw_mode_ss_config mac0_ss, |
| enum hw_mode_bandwidth mac0_bw, |
| enum hw_mode_ss_config mac1_ss, |
| enum hw_mode_bandwidth mac1_bw, |
| enum hw_mode_mac_band_cap mac0_band_cap, |
| enum hw_mode_dbs_capab dbs, |
| enum hw_mode_agile_dfs_capab dfs, |
| enum hw_mode_sbs_capab sbs, |
| enum policy_mgr_conn_update_reason reason, |
| uint8_t next_action) |
| { |
| int8_t hw_mode_index; |
| struct policy_mgr_hw_mode msg; |
| QDF_STATUS status; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| /* |
| * if HW is not capable of doing 2x2 or ini config disabled 2x2, don't |
| * allow to request FW for 2x2 |
| */ |
| if ((HW_MODE_SS_2x2 == mac0_ss) && (!pm_ctx->user_cfg.enable2x2)) { |
| policy_mgr_debug("2x2 is not allowed downgrading to 1x1 for mac0"); |
| mac0_ss = HW_MODE_SS_1x1; |
| } |
| if ((HW_MODE_SS_2x2 == mac1_ss) && (!pm_ctx->user_cfg.enable2x2)) { |
| policy_mgr_debug("2x2 is not allowed downgrading to 1x1 for mac1"); |
| mac1_ss = HW_MODE_SS_1x1; |
| } |
| |
| hw_mode_index = policy_mgr_get_hw_mode_idx_from_dbs_hw_list(psoc, |
| mac0_ss, mac0_bw, mac1_ss, mac1_bw, mac0_band_cap, |
| dbs, dfs, sbs); |
| if (hw_mode_index < 0) { |
| policy_mgr_err("Invalid HW mode index obtained"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| msg.hw_mode_index = hw_mode_index; |
| msg.set_hw_mode_cb = (void *)policy_mgr_pdev_set_hw_mode_cb; |
| msg.reason = reason; |
| msg.session_id = session_id; |
| msg.next_action = next_action; |
| msg.context = psoc; |
| |
| policy_mgr_debug("set hw mode to sme: hw_mode_index: %d session:%d reason:%d", |
| msg.hw_mode_index, msg.session_id, msg.reason); |
| |
| status = pm_ctx->sme_cbacks.sme_pdev_set_hw_mode(msg); |
| if (status != QDF_STATUS_SUCCESS) { |
| policy_mgr_err("Failed to set hw mode to SME"); |
| return status; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| enum policy_mgr_conc_next_action policy_mgr_need_opportunistic_upgrade( |
| struct wlan_objmgr_psoc *psoc, |
| enum policy_mgr_conn_update_reason *reason) |
| { |
| uint32_t conn_index; |
| enum policy_mgr_conc_next_action upgrade = PM_NOP; |
| enum policy_mgr_conc_next_action preferred_dbs_action; |
| uint8_t mac = 0; |
| struct policy_mgr_hw_mode_params hw_mode; |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid Context"); |
| goto exit; |
| } |
| |
| if (policy_mgr_is_hw_dbs_capable(psoc) == false) { |
| policy_mgr_err("driver isn't dbs capable, no further action needed"); |
| goto exit; |
| } |
| |
| status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("policy_mgr_get_current_hw_mode failed"); |
| goto exit; |
| } |
| if (!hw_mode.dbs_cap) { |
| policy_mgr_debug("current HW mode is non-DBS capable"); |
| goto exit; |
| } |
| |
| qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); |
| /* Are both mac's still in use */ |
| for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; |
| conn_index++) { |
| policy_mgr_debug("index:%d mac:%d in_use:%d chan:%d org_nss:%d", |
| conn_index, |
| pm_conc_connection_list[conn_index].mac, |
| pm_conc_connection_list[conn_index].in_use, |
| pm_conc_connection_list[conn_index].chan, |
| pm_conc_connection_list[conn_index].original_nss); |
| if ((pm_conc_connection_list[conn_index].mac == 0) && |
| pm_conc_connection_list[conn_index].in_use) { |
| mac |= POLICY_MGR_MAC0; |
| if (POLICY_MGR_MAC0_AND_MAC1 == mac) { |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| goto done; |
| } |
| } else if ((pm_conc_connection_list[conn_index].mac == 1) && |
| pm_conc_connection_list[conn_index].in_use) { |
| mac |= POLICY_MGR_MAC1; |
| if (policy_mgr_is_hw_dbs_2x2_capable(psoc) && |
| WLAN_REG_IS_24GHZ_CH( |
| pm_conc_connection_list[conn_index].chan) |
| ) { |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| policy_mgr_debug("2X2 DBS capable with 2.4 GHZ connection"); |
| goto done; |
| } |
| if (POLICY_MGR_MAC0_AND_MAC1 == mac) { |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| goto done; |
| } |
| } |
| } |
| /* Let's request for single MAC mode */ |
| upgrade = PM_SINGLE_MAC; |
| if (reason) |
| *reason = POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC; |
| /* Is there any connection had an initial connection with 2x2 */ |
| for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; |
| conn_index++) { |
| if ((pm_conc_connection_list[conn_index].original_nss == 2) && |
| pm_conc_connection_list[conn_index].in_use) { |
| upgrade = PM_SINGLE_MAC_UPGRADE; |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| goto done; |
| } |
| } |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| |
| done: |
| if (upgrade == PM_NOP && hw_mode.dbs_cap && |
| policy_mgr_is_2x2_1x1_dbs_capable(psoc)) { |
| preferred_dbs_action = |
| policy_mgr_get_preferred_dbs_action_table( |
| psoc, INVALID_VDEV_ID, 0, 0); |
| if (hw_mode.action_type == PM_DBS1 && |
| preferred_dbs_action == PM_DBS2) { |
| upgrade = PM_DBS2_DOWNGRADE; |
| if (reason) |
| *reason = |
| POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE; |
| } else if (hw_mode.action_type == PM_DBS2 && |
| preferred_dbs_action == PM_DBS1) { |
| upgrade = PM_DBS1_DOWNGRADE; |
| if (reason) |
| *reason = |
| POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE; |
| } |
| } |
| exit: |
| return upgrade; |
| } |
| |
| QDF_STATUS policy_mgr_update_connection_info(struct wlan_objmgr_psoc *psoc, |
| uint32_t vdev_id) |
| { |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| uint32_t conn_index = 0; |
| bool found = false; |
| struct policy_mgr_vdev_entry_info conn_table_entry; |
| enum policy_mgr_chain_mode chain_mask = POLICY_MGR_ONE_ONE; |
| uint8_t nss_2g, nss_5g; |
| enum policy_mgr_con_mode mode; |
| uint8_t chan; |
| uint32_t nss = 0; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid Context"); |
| return status; |
| } |
| |
| qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); |
| while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { |
| if (vdev_id == pm_conc_connection_list[conn_index].vdev_id) { |
| /* debug msg */ |
| found = true; |
| break; |
| } |
| conn_index++; |
| } |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| if (!found) { |
| /* err msg */ |
| policy_mgr_err("can't find vdev_id %d in pm_conc_connection_list", |
| vdev_id); |
| return status; |
| } |
| if (pm_ctx->wma_cbacks.wma_get_connection_info) { |
| status = pm_ctx->wma_cbacks.wma_get_connection_info( |
| vdev_id, &conn_table_entry); |
| if (QDF_STATUS_SUCCESS != status) { |
| policy_mgr_err("can't find vdev_id %d in connection table", |
| vdev_id); |
| return status; |
| } |
| } else { |
| policy_mgr_err("wma_get_connection_info is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| mode = policy_mgr_get_mode(conn_table_entry.type, |
| conn_table_entry.sub_type); |
| chan = wlan_reg_freq_to_chan(pm_ctx->pdev, conn_table_entry.mhz); |
| status = policy_mgr_get_nss_for_vdev(psoc, mode, &nss_2g, &nss_5g); |
| if (QDF_IS_STATUS_SUCCESS(status)) { |
| if ((WLAN_REG_IS_24GHZ_CH(chan) && (nss_2g > 1)) || |
| (WLAN_REG_IS_5GHZ_CH(chan) && (nss_5g > 1))) |
| chain_mask = POLICY_MGR_TWO_TWO; |
| else |
| chain_mask = POLICY_MGR_ONE_ONE; |
| nss = (WLAN_REG_IS_24GHZ_CH(chan)) ? nss_2g : nss_5g; |
| } else { |
| policy_mgr_err("Error in getting nss"); |
| } |
| |
| policy_mgr_debug("update PM connection table for vdev:%d", vdev_id); |
| |
| /* add the entry */ |
| policy_mgr_update_conc_list(psoc, conn_index, |
| mode, |
| chan, |
| policy_mgr_get_bw(conn_table_entry.chan_width), |
| conn_table_entry.mac_id, |
| chain_mask, |
| nss, vdev_id, true, true); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_update_and_wait_for_connection_update( |
| struct wlan_objmgr_psoc *psoc, |
| uint8_t session_id, |
| uint8_t channel, |
| enum policy_mgr_conn_update_reason reason) |
| { |
| QDF_STATUS status; |
| |
| policy_mgr_debug("session:%d channel:%d reason:%d", |
| session_id, channel, reason); |
| |
| status = policy_mgr_reset_connection_update(psoc); |
| if (QDF_IS_STATUS_ERROR(status)) |
| policy_mgr_err("clearing event failed"); |
| |
| status = policy_mgr_current_connections_update(psoc, |
| session_id, channel, reason); |
| if (QDF_STATUS_E_FAILURE == status) { |
| policy_mgr_err("connections update failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| /* Wait only when status is success */ |
| if (QDF_IS_STATUS_SUCCESS(status)) { |
| status = policy_mgr_wait_for_connection_update(psoc); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| policy_mgr_err("qdf wait for event failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * policy_mgr_is_dbs_allowed_for_concurrency() - If dbs is allowed for current |
| * concurreny |
| * @new_conn_mode: new connection mode |
| * |
| * When a new connection is about to come up, check if dbs is allowed for |
| * STA+STA or STA+P2P |
| * |
| * Return: true if dbs is allowed for STA+STA or STA+P2P else false |
| */ |
| bool policy_mgr_is_dbs_allowed_for_concurrency( |
| struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE new_conn_mode) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| uint32_t count, dbs_for_sta_sta, dbs_for_sta_p2p; |
| bool ret = true; |
| uint32_t ch_sel_plcy; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| return ret; |
| } |
| |
| count = policy_mgr_get_connection_count(psoc); |
| |
| if (count != 1 || new_conn_mode == QDF_MAX_NO_OF_MODE) |
| return ret; |
| |
| ch_sel_plcy = pm_ctx->cfg.chnl_select_plcy; |
| dbs_for_sta_sta = PM_CHANNEL_SELECT_LOGIC_STA_STA_GET(ch_sel_plcy); |
| dbs_for_sta_p2p = PM_CHANNEL_SELECT_LOGIC_STA_P2P_GET(ch_sel_plcy); |
| |
| switch (pm_conc_connection_list[0].mode) { |
| case PM_STA_MODE: |
| switch (new_conn_mode) { |
| case QDF_STA_MODE: |
| if (!dbs_for_sta_sta) |
| return false; |
| break; |
| case QDF_P2P_DEVICE_MODE: |
| case QDF_P2P_CLIENT_MODE: |
| case QDF_P2P_GO_MODE: |
| if (!dbs_for_sta_p2p) |
| return false; |
| break; |
| default: |
| break; |
| } |
| break; |
| case PM_P2P_CLIENT_MODE: |
| case PM_P2P_GO_MODE: |
| switch (new_conn_mode) { |
| case QDF_STA_MODE: |
| if (!dbs_for_sta_p2p) |
| return false; |
| break; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| bool policy_mgr_is_chnl_in_diff_band(struct wlan_objmgr_psoc *psoc, |
| uint8_t channel) |
| { |
| uint8_t i, pm_chnl; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid Context"); |
| return false; |
| } |
| |
| /* |
| * check given channel against already existing connections' |
| * channels. if they differ then channels are in different bands |
| */ |
| qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); |
| for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { |
| pm_chnl = pm_conc_connection_list[i].chan; |
| if (pm_conc_connection_list[i].in_use) |
| if (!WLAN_REG_IS_SAME_BAND_CHANNELS(channel, pm_chnl)) { |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| policy_mgr_debug("channel is in diff band"); |
| return true; |
| } |
| } |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| |
| return false; |
| } |
| |
| bool policy_mgr_is_hwmode_set_for_given_chnl(struct wlan_objmgr_psoc *psoc, |
| uint8_t channel) |
| { |
| enum policy_mgr_band band; |
| bool is_hwmode_dbs, is_2x2_dbs; |
| |
| if (policy_mgr_is_hw_dbs_capable(psoc) == false) |
| return true; |
| |
| if (WLAN_REG_IS_24GHZ_CH(channel)) |
| band = POLICY_MGR_BAND_24; |
| else |
| band = POLICY_MGR_BAND_5; |
| |
| is_hwmode_dbs = policy_mgr_is_current_hwmode_dbs(psoc); |
| is_2x2_dbs = policy_mgr_is_hw_dbs_2x2_capable(psoc); |
| /* |
| * If HW supports 2x2 chains in DBS HW mode and if DBS HW mode is not |
| * yet set then this is the right time to block the connection. |
| */ |
| if ((band == POLICY_MGR_BAND_24) && is_2x2_dbs && !is_hwmode_dbs) { |
| policy_mgr_err("HW mode is not yet in DBS!!!!!"); |
| return false; |
| } |
| |
| /* |
| * If HW supports 1x1 chains DBS HW mode and if first connection is |
| * 2G or 5G band and if second connection is coming up in diffrent |
| * band than the first connection and if current HW mode is not yet |
| * set in DBS then this is the right time to block the connection. |
| */ |
| if (policy_mgr_is_chnl_in_diff_band(psoc, channel) && !is_hwmode_dbs) { |
| policy_mgr_err("Given channel & existing conn is diff band & HW mode is not yet in DBS !!!!"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * policy_mgr_pri_id_to_con_mode() - convert policy_mgr_pri_id to |
| * policy_mgr_con_mode |
| * @pri_id: policy_mgr_pri_id |
| * |
| * The help function converts policy_mgr_pri_id type to policy_mgr_con_mode |
| * type. |
| * |
| * Return: policy_mgr_con_mode type. |
| */ |
| static |
| enum policy_mgr_con_mode policy_mgr_pri_id_to_con_mode( |
| enum policy_mgr_pri_id pri_id) |
| { |
| switch (pri_id) { |
| case PM_STA_PRI_ID: |
| return PM_STA_MODE; |
| case PM_SAP_PRI_ID: |
| return PM_SAP_MODE; |
| case PM_P2P_GO_PRI_ID: |
| return PM_P2P_GO_MODE; |
| case PM_P2P_CLI_PRI_ID: |
| return PM_P2P_CLIENT_MODE; |
| default: |
| return PM_MAX_NUM_OF_MODE; |
| } |
| } |
| |
| enum policy_mgr_conc_next_action |
| policy_mgr_get_preferred_dbs_action_table( |
| struct wlan_objmgr_psoc *psoc, |
| uint32_t vdev_id, |
| uint8_t channel, |
| enum policy_mgr_conn_update_reason reason) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| enum policy_mgr_con_mode pri_conn_mode = PM_MAX_NUM_OF_MODE; |
| enum policy_mgr_con_mode new_conn_mode = PM_MAX_NUM_OF_MODE; |
| enum QDF_OPMODE new_conn_op_mode = QDF_MAX_NO_OF_MODE; |
| bool band_pref_5g = true; |
| bool vdev_priority_enabled = false; |
| bool dbs_2x2_5g_1x1_2g_supported; |
| bool dbs_2x2_2g_1x1_5g_supported; |
| uint32_t vdev_pri_list, vdev_pri_id; |
| uint8_t chan_list[MAX_NUMBER_OF_CONC_CONNECTIONS + 1]; |
| uint8_t vdev_list[MAX_NUMBER_OF_CONC_CONNECTIONS + 1]; |
| uint32_t vdev_count = 0; |
| uint32_t i; |
| bool found; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| return PM_NOP; |
| } |
| dbs_2x2_5g_1x1_2g_supported = |
| policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(psoc); |
| dbs_2x2_2g_1x1_5g_supported = |
| policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(psoc); |
| policy_mgr_debug("target support DBS1 %d DBS2 %d", |
| dbs_2x2_5g_1x1_2g_supported, |
| dbs_2x2_2g_1x1_5g_supported); |
| /* |
| * If both DBS1 and DBS2 not supported, this should be Legacy Single |
| * DBS mode HW. The policy_mgr_psoc_enable has setup the correct |
| * action tables. |
| */ |
| if (!dbs_2x2_5g_1x1_2g_supported && !dbs_2x2_2g_1x1_5g_supported) |
| return PM_NOP; |
| if (!dbs_2x2_5g_1x1_2g_supported) { |
| band_pref_5g = false; |
| policy_mgr_debug("target only supports DBS2!"); |
| goto DONE; |
| } |
| if (!dbs_2x2_2g_1x1_5g_supported) { |
| policy_mgr_debug("target only supports DBS1!"); |
| goto DONE; |
| } |
| if (PM_GET_BAND_PREFERRED(pm_ctx->cfg.dbs_selection_plcy) == 1) |
| band_pref_5g = false; |
| |
| if (PM_GET_VDEV_PRIORITY_ENABLED( |
| pm_ctx->cfg.dbs_selection_plcy) == 1 && |
| pm_ctx->cfg.vdev_priority_list) |
| vdev_priority_enabled = true; |
| |
| if (!vdev_priority_enabled) |
| goto DONE; |
| |
| if (vdev_id != INVALID_VDEV_ID && channel) { |
| if (pm_ctx->hdd_cbacks.hdd_get_device_mode) |
| new_conn_op_mode = pm_ctx->hdd_cbacks. |
| hdd_get_device_mode(vdev_id); |
| |
| new_conn_mode = policy_mgr_convert_device_mode_to_qdf_type( |
| new_conn_op_mode); |
| if (new_conn_mode == PM_MAX_NUM_OF_MODE) |
| policy_mgr_debug("new vdev %d op_mode %d chan %d reason %d: not prioritized", |
| vdev_id, new_conn_op_mode, |
| channel, reason); |
| else |
| policy_mgr_debug("new vdev %d op_mode %d chan %d : reason %d", |
| vdev_id, new_conn_op_mode, channel, |
| reason); |
| } |
| vdev_pri_list = pm_ctx->cfg.vdev_priority_list; |
| while (vdev_pri_list) { |
| vdev_pri_id = vdev_pri_list & 0xF; |
| pri_conn_mode = policy_mgr_pri_id_to_con_mode(vdev_pri_id); |
| if (pri_conn_mode == PM_MAX_NUM_OF_MODE) { |
| policy_mgr_debug("vdev_pri_id %d prioritization not supported", |
| vdev_pri_id); |
| goto NEXT; |
| } |
| vdev_count = policy_mgr_get_mode_specific_conn_info( |
| psoc, chan_list, vdev_list, pri_conn_mode); |
| /** |
| * Take care of duplication case, the vdev id may |
| * exist in the conn list already with old chan. |
| * Replace with new chan before make decision. |
| */ |
| found = false; |
| for (i = 0; i < vdev_count; i++) { |
| policy_mgr_debug("[%d] vdev %d chan %d conn_mode %d", |
| i, vdev_list[i], chan_list[i], |
| pri_conn_mode); |
| |
| if (new_conn_mode == pri_conn_mode && |
| vdev_list[i] == vdev_id) { |
| chan_list[i] = channel; |
| found = true; |
| } |
| } |
| /** |
| * The new coming vdev should be added to the list to |
| * make decision if it is prioritized. |
| */ |
| if (!found && new_conn_mode == pri_conn_mode) { |
| chan_list[vdev_count] = channel; |
| vdev_list[vdev_count++] = vdev_id; |
| } |
| /** |
| * if more than one vdev has same priority, keep "band_pref_5g" |
| * value as default band preference setting. |
| */ |
| if (vdev_count > 1) |
| break; |
| /** |
| * select the only active vdev (or new coming vdev) chan as |
| * preferred band. |
| */ |
| if (vdev_count > 0) { |
| band_pref_5g = WLAN_REG_IS_5GHZ_CH(chan_list[0]); |
| break; |
| } |
| NEXT: |
| vdev_pri_list >>= 4; |
| } |
| DONE: |
| policy_mgr_debug("band_pref_5g %d", band_pref_5g); |
| if (band_pref_5g) |
| return PM_DBS1; |
| else |
| return PM_DBS2; |
| } |
| |
| /** |
| * policy_mgr_get_second_conn_action_table() - get second conn action table |
| * @psoc: Pointer to psoc |
| * @vdev_id: vdev Id |
| * @channel: channel of vdev. |
| * @reason: reason of request |
| * |
| * Get the action table based on current HW Caps and INI user preference. |
| * This function will be called by policy_mgr_current_connections_update during |
| * DBS action decision. |
| * |
| * return : action table address |
| */ |
| static policy_mgr_next_action_two_connection_table_type * |
| policy_mgr_get_second_conn_action_table( |
| struct wlan_objmgr_psoc *psoc, |
| uint32_t vdev_id, |
| uint8_t channel, |
| enum policy_mgr_conn_update_reason reason) |
| { |
| enum policy_mgr_conc_next_action preferred_action; |
| |
| if (!policy_mgr_is_2x2_1x1_dbs_capable(psoc)) |
| return next_action_two_connection_table; |
| |
| preferred_action = policy_mgr_get_preferred_dbs_action_table( |
| psoc, vdev_id, channel, reason); |
| switch (preferred_action) { |
| case PM_DBS2: |
| return next_action_two_connection_2x2_2g_1x1_5g_table; |
| default: |
| return next_action_two_connection_table; |
| } |
| } |
| |
| /** |
| * policy_mgr_get_third_conn_action_table() - get third connection action table |
| * @psoc: Pointer to psoc |
| * @vdev_id: vdev Id |
| * @channel: channel of vdev. |
| * @reason: reason of request |
| * |
| * Get the action table based on current HW Caps and INI user preference. |
| * This function will be called by policy_mgr_current_connections_update during |
| * DBS action decision. |
| * |
| * return : action table address |
| */ |
| static policy_mgr_next_action_three_connection_table_type * |
| policy_mgr_get_third_conn_action_table( |
| struct wlan_objmgr_psoc *psoc, |
| uint32_t vdev_id, |
| uint8_t channel, |
| enum policy_mgr_conn_update_reason reason) |
| { |
| enum policy_mgr_conc_next_action preferred_action; |
| |
| if (!policy_mgr_is_2x2_1x1_dbs_capable(psoc)) |
| return next_action_three_connection_table; |
| |
| preferred_action = policy_mgr_get_preferred_dbs_action_table( |
| psoc, vdev_id, channel, reason); |
| switch (preferred_action) { |
| case PM_DBS2: |
| return next_action_three_connection_2x2_2g_1x1_5g_table; |
| default: |
| return next_action_three_connection_table; |
| } |
| } |
| |
| QDF_STATUS policy_mgr_current_connections_update(struct wlan_objmgr_psoc *psoc, |
| uint32_t session_id, |
| uint8_t channel, |
| enum policy_mgr_conn_update_reason reason) |
| { |
| enum policy_mgr_conc_next_action next_action = PM_NOP; |
| uint32_t num_connections = 0; |
| enum policy_mgr_one_connection_mode second_index = 0; |
| enum policy_mgr_two_connection_mode third_index = 0; |
| policy_mgr_next_action_two_connection_table_type *second_conn_table; |
| policy_mgr_next_action_three_connection_table_type *third_conn_table; |
| enum policy_mgr_band band; |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| enum QDF_OPMODE new_conn_mode = QDF_MAX_NO_OF_MODE; |
| |
| if (policy_mgr_is_hw_dbs_capable(psoc) == false) { |
| policy_mgr_err("driver isn't dbs capable, no further action needed"); |
| return QDF_STATUS_E_NOSUPPORT; |
| } |
| if (WLAN_REG_IS_24GHZ_CH(channel)) |
| band = POLICY_MGR_BAND_24; |
| else |
| band = POLICY_MGR_BAND_5; |
| |
| num_connections = policy_mgr_get_connection_count(psoc); |
| |
| policy_mgr_debug("num_connections=%d channel=%d", |
| num_connections, channel); |
| |
| switch (num_connections) { |
| case 0: |
| if (band == POLICY_MGR_BAND_24) |
| if (policy_mgr_is_hw_dbs_2x2_capable(psoc)) |
| next_action = PM_DBS; |
| else |
| next_action = PM_NOP; |
| else |
| next_action = PM_NOP; |
| break; |
| case 1: |
| second_index = |
| policy_mgr_get_second_connection_pcl_table_index(psoc); |
| if (PM_MAX_ONE_CONNECTION_MODE == second_index) { |
| policy_mgr_err( |
| "couldn't find index for 2nd connection next action table"); |
| goto done; |
| } |
| second_conn_table = policy_mgr_get_second_conn_action_table( |
| psoc, session_id, channel, reason); |
| next_action = (*second_conn_table)[second_index][band]; |
| break; |
| case 2: |
| third_index = |
| policy_mgr_get_third_connection_pcl_table_index(psoc); |
| if (PM_MAX_TWO_CONNECTION_MODE == third_index) { |
| policy_mgr_err( |
| "couldn't find index for 3rd connection next action table"); |
| goto done; |
| } |
| third_conn_table = policy_mgr_get_third_conn_action_table( |
| psoc, session_id, channel, reason); |
| next_action = (*third_conn_table)[third_index][band]; |
| break; |
| default: |
| policy_mgr_err("unexpected num_connections value %d", |
| num_connections); |
| break; |
| } |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| goto done; |
| } |
| |
| /* |
| * There is no adapter associated with NAN Discovery, hence skip the |
| * HDD callback and fill separately. |
| */ |
| if (reason == POLICY_MGR_UPDATE_REASON_NAN_DISCOVERY) |
| new_conn_mode = QDF_NAN_DISC_MODE; |
| else if (pm_ctx->hdd_cbacks.hdd_get_device_mode) |
| new_conn_mode = pm_ctx->hdd_cbacks. |
| hdd_get_device_mode(session_id); |
| |
| /* |
| * Based on channel_select_logic_conc ini, hw mode is set |
| * when second connection is about to come up that results |
| * in STA+STA and STA+P2P concurrency. |
| * 1) If MCC is set and if current hw mode is dbs, hw mode |
| * should be set to single mac for above concurrency. |
| * 2) If MCC is set and if current hw mode is not dbs, hw |
| * mode change is not required. |
| */ |
| if (policy_mgr_is_current_hwmode_dbs(psoc) && |
| !policy_mgr_is_dbs_allowed_for_concurrency(psoc, new_conn_mode)) |
| next_action = PM_SINGLE_MAC; |
| else if (!policy_mgr_is_current_hwmode_dbs(psoc) && |
| !policy_mgr_is_dbs_allowed_for_concurrency(psoc, new_conn_mode)) |
| next_action = PM_NOP; |
| |
| if (PM_NOP != next_action) |
| status = policy_mgr_next_actions(psoc, session_id, |
| next_action, reason); |
| else |
| status = QDF_STATUS_E_NOSUPPORT; |
| |
| policy_mgr_debug( |
| "idx2=%d idx3=%d next_action=%d, band=%d status=%d reason=%d session_id=%d", |
| second_index, third_index, next_action, band, status, |
| reason, session_id); |
| |
| done: |
| return status; |
| } |
| |
| /** |
| * policy_mgr_validate_dbs_switch() - Check DBS action valid or not |
| * @psoc: Pointer to psoc |
| * @session_id: vdev id |
| * @action: action requested |
| * @reason: reason of hw mode change |
| * |
| * This routine will check the current hw mode with requested action. |
| * If we are already in the mode, the caller will do nothing. |
| * This will be called by policy_mgr_next_actions to check the action needed |
| * or not. |
| * |
| * return : QDF_STATUS_SUCCESS, action is allowed. |
| * QDF_STATUS_E_ALREADY, action is not needed. |
| * QDF_STATUS_E_FAILURE, error happens. |
| * QDF_STATUS_E_NOSUPPORT, the requested mode not supported. |
| */ |
| static |
| QDF_STATUS policy_mgr_validate_dbs_switch( |
| struct wlan_objmgr_psoc *psoc, |
| uint32_t session_id, |
| enum policy_mgr_conc_next_action action, |
| enum policy_mgr_conn_update_reason reason) |
| { |
| QDF_STATUS status; |
| struct policy_mgr_hw_mode_params hw_mode; |
| |
| /* check for the current HW index to see if really need any action */ |
| status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("policy_mgr_get_current_hw_mode failed"); |
| return status; |
| } |
| |
| if ((action == PM_SBS) || (action == PM_SBS_DOWNGRADE)) { |
| if (!policy_mgr_is_hw_sbs_capable(psoc)) { |
| /* No action */ |
| policy_mgr_notice("firmware is not sbs capable"); |
| return QDF_STATUS_E_NOSUPPORT; |
| } |
| /* current mode is already SBS nothing to be |
| * done |
| */ |
| if (hw_mode.sbs_cap) { |
| policy_mgr_notice("current mode is already SBS"); |
| return QDF_STATUS_E_ALREADY; |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| if (!hw_mode.dbs_cap) { |
| if (action == PM_SINGLE_MAC || |
| action == PM_SINGLE_MAC_UPGRADE) { |
| policy_mgr_notice("current mode is already single MAC"); |
| return QDF_STATUS_E_ALREADY; |
| } else { |
| return QDF_STATUS_SUCCESS; |
| } |
| } |
| /** |
| * If already in DBS, no need to request DBS again (HL, Napier). |
| * For dual DBS HW, in case DBS1 -> DBS2 or DBS2 -> DBS1 |
| * switching, we need to check the current DBS mode is same as |
| * requested or not. |
| */ |
| if (policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(psoc) || |
| policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(psoc)) { |
| policy_mgr_info("curr dbs action %d new action %d", |
| hw_mode.action_type, action); |
| if (hw_mode.action_type == PM_DBS1 && |
| ((action == PM_DBS1 || |
| action == PM_DBS1_DOWNGRADE))) { |
| policy_mgr_err("driver is already in DBS_5G_2x2_24G_1x1 (%d), no further action %d needed", |
| hw_mode.action_type, action); |
| return QDF_STATUS_E_ALREADY; |
| } else if (hw_mode.action_type == PM_DBS2 && |
| ((action == PM_DBS2 || |
| action == PM_DBS2_DOWNGRADE))) { |
| policy_mgr_err("driver is already in DBS_24G_2x2_5G_1x1 (%d), no further action %d needed", |
| hw_mode.action_type, action); |
| return QDF_STATUS_E_ALREADY; |
| } |
| } else if ((action == PM_DBS_DOWNGRADE) || (action == PM_DBS) || |
| (action == PM_DBS_UPGRADE)) { |
| policy_mgr_err("driver is already in %s mode, no further action needed", |
| (hw_mode.dbs_cap) ? "dbs" : "non dbs"); |
| return QDF_STATUS_E_ALREADY; |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_next_actions( |
| struct wlan_objmgr_psoc *psoc, |
| uint32_t session_id, |
| enum policy_mgr_conc_next_action action, |
| enum policy_mgr_conn_update_reason reason) |
| { |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| struct dbs_nss nss_dbs = {0}; |
| struct policy_mgr_hw_mode_params hw_mode; |
| enum policy_mgr_conc_next_action next_action; |
| |
| if (policy_mgr_is_hw_dbs_capable(psoc) == false) { |
| policy_mgr_err("driver isn't dbs capable, no further action needed"); |
| return QDF_STATUS_E_NOSUPPORT; |
| } |
| |
| /* check for the current HW index to see if really need any action */ |
| status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("policy_mgr_get_current_hw_mode failed"); |
| return status; |
| } |
| |
| /* check for the current HW index to see if really need any action */ |
| status = policy_mgr_validate_dbs_switch(psoc, session_id, action, |
| reason); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err(" not take action %d reason %d session %d status %d", |
| action, reason, session_id, status); |
| return status; |
| } |
| |
| switch (action) { |
| case PM_DBS_DOWNGRADE: |
| /* |
| * check if we have a beaconing entity that is using 2x2. If yes, |
| * update the beacon template & notify FW. Once FW confirms |
| * beacon updated, send down the HW mode change req |
| */ |
| status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, |
| PM_DBS, reason, session_id); |
| break; |
| case PM_DBS: |
| (void)policy_mgr_get_hw_dbs_nss(psoc, &nss_dbs); |
| |
| status = policy_mgr_pdev_set_hw_mode(psoc, session_id, |
| nss_dbs.mac0_ss, |
| HW_MODE_80_MHZ, |
| nss_dbs.mac1_ss, |
| HW_MODE_40_MHZ, |
| HW_MODE_MAC_BAND_NONE, |
| HW_MODE_DBS, |
| HW_MODE_AGILE_DFS_NONE, |
| HW_MODE_SBS_NONE, |
| reason, PM_NOP); |
| break; |
| case PM_SINGLE_MAC_UPGRADE: |
| /* |
| * change the HW mode first before the NSS upgrade |
| */ |
| status = policy_mgr_pdev_set_hw_mode(psoc, session_id, |
| HW_MODE_SS_2x2, |
| HW_MODE_80_MHZ, |
| HW_MODE_SS_0x0, HW_MODE_BW_NONE, |
| HW_MODE_MAC_BAND_NONE, |
| HW_MODE_DBS_NONE, |
| HW_MODE_AGILE_DFS_NONE, |
| HW_MODE_SBS_NONE, |
| reason, PM_UPGRADE); |
| break; |
| case PM_SINGLE_MAC: |
| status = policy_mgr_pdev_set_hw_mode(psoc, session_id, |
| HW_MODE_SS_2x2, |
| HW_MODE_80_MHZ, |
| HW_MODE_SS_0x0, HW_MODE_BW_NONE, |
| HW_MODE_MAC_BAND_NONE, |
| HW_MODE_DBS_NONE, |
| HW_MODE_AGILE_DFS_NONE, |
| HW_MODE_SBS_NONE, |
| reason, PM_NOP); |
| break; |
| case PM_DBS_UPGRADE: |
| status = policy_mgr_pdev_set_hw_mode(psoc, session_id, |
| HW_MODE_SS_2x2, |
| HW_MODE_80_MHZ, |
| HW_MODE_SS_2x2, HW_MODE_80_MHZ, |
| HW_MODE_MAC_BAND_NONE, |
| HW_MODE_DBS, |
| HW_MODE_AGILE_DFS_NONE, |
| HW_MODE_SBS_NONE, |
| reason, PM_UPGRADE); |
| break; |
| case PM_SBS_DOWNGRADE: |
| status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, |
| PM_SBS, reason, session_id); |
| break; |
| case PM_SBS: |
| status = policy_mgr_pdev_set_hw_mode(psoc, session_id, |
| HW_MODE_SS_1x1, |
| HW_MODE_80_MHZ, |
| HW_MODE_SS_1x1, HW_MODE_80_MHZ, |
| HW_MODE_MAC_BAND_NONE, |
| HW_MODE_DBS, |
| HW_MODE_AGILE_DFS_NONE, |
| HW_MODE_SBS, |
| reason, PM_NOP); |
| break; |
| case PM_DOWNGRADE: |
| /* |
| * check if we have a beaconing entity that advertised 2x2 |
| * intially. If yes, update the beacon template & notify FW. |
| */ |
| status = policy_mgr_nss_update(psoc, POLICY_MGR_RX_NSS_1, |
| PM_NOP, POLICY_MGR_ANY, reason, |
| session_id); |
| break; |
| case PM_UPGRADE: |
| /* |
| * check if we have a beaconing entity that advertised 2x2 |
| * intially. If yes, update the beacon template & notify FW. |
| */ |
| status = policy_mgr_nss_update(psoc, POLICY_MGR_RX_NSS_2, |
| PM_NOP, POLICY_MGR_ANY, reason, |
| session_id); |
| break; |
| case PM_DBS1_DOWNGRADE: |
| status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, |
| PM_DBS1, reason, |
| session_id); |
| break; |
| case PM_DBS2_DOWNGRADE: |
| status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, |
| PM_DBS2, reason, |
| session_id); |
| break; |
| case PM_DBS1: |
| /* |
| * PM_DBS1 (2x2 5G + 1x1 2G) will support 5G 2x2. If previous |
| * mode is DBS, that should be 2x2 2G + 1x1 5G mode and |
| * the 5G band was downgraded to 1x1. So, we need to |
| * upgrade 5G vdevs after hw mode change. |
| */ |
| if (hw_mode.dbs_cap) |
| next_action = PM_UPGRADE_5G; |
| else |
| next_action = PM_NOP; |
| status = policy_mgr_pdev_set_hw_mode( |
| psoc, session_id, |
| HW_MODE_SS_2x2, |
| HW_MODE_80_MHZ, |
| HW_MODE_SS_1x1, HW_MODE_40_MHZ, |
| HW_MODE_MAC_BAND_5G, |
| HW_MODE_DBS, |
| HW_MODE_AGILE_DFS_NONE, |
| HW_MODE_SBS_NONE, |
| reason, next_action); |
| break; |
| case PM_DBS2: |
| /* |
| * PM_DBS2 (2x2 2G + 1x1 5G) will support 2G 2x2. If previous |
| * mode is DBS, that should be 2x2 5G + 1x1 2G mode and |
| * the 2G band was downgraded to 1x1. So, we need to |
| * upgrade 5G vdevs after hw mode change. |
| */ |
| if (hw_mode.dbs_cap) |
| next_action = PM_UPGRADE_2G; |
| else |
| next_action = PM_NOP; |
| status = policy_mgr_pdev_set_hw_mode( |
| psoc, session_id, |
| HW_MODE_SS_2x2, |
| HW_MODE_40_MHZ, |
| HW_MODE_SS_1x1, HW_MODE_40_MHZ, |
| HW_MODE_MAC_BAND_2G, |
| HW_MODE_DBS, |
| HW_MODE_AGILE_DFS_NONE, |
| HW_MODE_SBS_NONE, |
| reason, next_action); |
| break; |
| case PM_UPGRADE_5G: |
| status = policy_mgr_nss_update( |
| psoc, POLICY_MGR_RX_NSS_2, |
| PM_NOP, POLICY_MGR_BAND_5, reason, |
| session_id); |
| break; |
| case PM_UPGRADE_2G: |
| status = policy_mgr_nss_update( |
| psoc, POLICY_MGR_RX_NSS_2, |
| PM_NOP, POLICY_MGR_BAND_24, reason, |
| session_id); |
| break; |
| default: |
| policy_mgr_err("unexpected action value %d", action); |
| status = QDF_STATUS_E_FAILURE; |
| break; |
| } |
| |
| return status; |
| } |
| |
| QDF_STATUS policy_mgr_handle_conc_multiport(struct wlan_objmgr_psoc *psoc, |
| uint8_t session_id, uint8_t channel) |
| { |
| QDF_STATUS status; |
| |
| if (!policy_mgr_check_for_session_conc(psoc, session_id, channel)) { |
| policy_mgr_err("Conc not allowed for the session %d", |
| session_id); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| status = policy_mgr_reset_connection_update(psoc); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| policy_mgr_err("clearing event failed"); |
| |
| status = policy_mgr_current_connections_update(psoc, session_id, |
| channel, |
| POLICY_MGR_UPDATE_REASON_NORMAL_STA); |
| if (QDF_STATUS_E_FAILURE == status) { |
| policy_mgr_err("connections update failed"); |
| return status; |
| } |
| |
| return status; |
| } |
| |
| #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH |
| void policy_mgr_update_user_config_sap_chan( |
| struct wlan_objmgr_psoc *psoc, uint32_t channel) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid pm context and failed to update the user config sap channel"); |
| return; |
| } |
| pm_ctx->user_config_sap_channel = channel; |
| } |
| |
| /** |
| * policy_mgr_is_restart_sap_allowed() - Check if restart SAP |
| * allowed during SCC -> MCC switch |
| * @psoc: PSOC object data |
| * @mcc_to_scc_switch: MCC to SCC switch enabled user config |
| * |
| * Check if restart SAP allowed during SCC->MCC switch |
| * |
| * Restart: true or false |
| */ |
| static bool policy_mgr_is_restart_sap_allowed( |
| struct wlan_objmgr_psoc *psoc, |
| uint32_t mcc_to_scc_switch) |
| { |
| uint32_t sta_ap_bit_mask = QDF_STA_MASK | QDF_SAP_MASK; |
| uint32_t sta_go_bit_mask = QDF_STA_MASK | QDF_P2P_GO_MASK; |
| |
| if ((mcc_to_scc_switch == QDF_MCC_TO_SCC_SWITCH_DISABLE) || |
| !policy_mgr_concurrent_open_sessions_running(psoc) || |
| !(((policy_mgr_get_concurrency_mode(psoc) & sta_ap_bit_mask) |
| == sta_ap_bit_mask) || |
| ((mcc_to_scc_switch == |
| QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) && |
| ((policy_mgr_get_concurrency_mode(psoc) & sta_go_bit_mask) |
| == sta_go_bit_mask)))) { |
| policy_mgr_debug("MCC switch disabled or not concurrent STA/SAP, STA/GO"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool policy_mgr_is_safe_channel(struct wlan_objmgr_psoc *psoc, |
| uint8_t channel) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| bool is_safe = true; |
| uint8_t j; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| return is_safe; |
| } |
| |
| |
| if (pm_ctx->unsafe_channel_count == 0) { |
| policy_mgr_debug("There are no unsafe channels"); |
| return is_safe; |
| } |
| |
| for (j = 0; j < pm_ctx->unsafe_channel_count; j++) { |
| if (channel == pm_ctx->unsafe_channel_list[j]) { |
| is_safe = false; |
| policy_mgr_warn("CH %d is not safe", channel); |
| break; |
| } |
| } |
| |
| return is_safe; |
| } |
| |
| bool policy_mgr_is_sap_restart_required_after_sta_disconnect( |
| struct wlan_objmgr_psoc *psoc, uint8_t *intf_ch) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| uint8_t sap_chan = policy_mgr_mode_specific_get_channel(psoc, |
| PM_SAP_MODE); |
| bool sta_sap_scc_on_dfs_chan = |
| policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); |
| |
| *intf_ch = 0; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid pm context"); |
| return false; |
| } |
| |
| policy_mgr_debug("sta_sap_scc_on_dfs_chan %u, sap_chan %u", |
| sta_sap_scc_on_dfs_chan, sap_chan); |
| |
| if ((!sta_sap_scc_on_dfs_chan || |
| !(sap_chan && WLAN_REG_IS_5GHZ_CH(sap_chan) && |
| (wlan_reg_get_channel_state(pm_ctx->pdev, sap_chan) == |
| CHANNEL_STATE_DFS))) && |
| (!policy_mgr_sta_sap_scc_on_lte_coex_chan(psoc) || |
| policy_mgr_is_safe_channel(psoc, sap_chan))) { |
| return false; |
| } |
| |
| *intf_ch = pm_ctx->user_config_sap_channel; |
| policy_mgr_debug("Standalone SAP is not allowed on DFS channel, Move it to channel %u", |
| *intf_ch); |
| |
| return true; |
| } |
| |
| static void __policy_mgr_check_sta_ap_concurrent_ch_intf(void *data) |
| { |
| struct wlan_objmgr_psoc *psoc; |
| struct policy_mgr_psoc_priv_obj *pm_ctx = NULL; |
| struct sta_ap_intf_check_work_ctx *work_info = NULL; |
| uint32_t mcc_to_scc_switch, cc_count = 0, i; |
| QDF_STATUS status; |
| uint8_t channel, sec_ch; |
| uint8_t operating_channel[MAX_NUMBER_OF_CONC_CONNECTIONS]; |
| uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS]; |
| |
| if (qdf_is_module_state_transitioning()) { |
| policy_mgr_err("Module transition in progress"); |
| goto end; |
| } |
| |
| work_info = (struct sta_ap_intf_check_work_ctx *) data; |
| if (!work_info) { |
| policy_mgr_err("Invalid work_info"); |
| goto end; |
| } |
| |
| psoc = work_info->psoc; |
| if (!psoc) { |
| policy_mgr_err("Invalid psoc"); |
| goto end; |
| } |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| goto end; |
| } |
| mcc_to_scc_switch = |
| policy_mgr_get_mcc_to_scc_switch_mode(psoc); |
| |
| policy_mgr_info("Concurrent open sessions running: %d", |
| policy_mgr_concurrent_open_sessions_running(psoc)); |
| |
| if (!policy_mgr_is_restart_sap_allowed(psoc, mcc_to_scc_switch)) |
| goto end; |
| |
| cc_count = policy_mgr_get_mode_specific_conn_info(psoc, |
| &operating_channel[cc_count], |
| &vdev_id[cc_count], |
| PM_SAP_MODE); |
| policy_mgr_debug("Number of concurrent SAP: %d", cc_count); |
| if (cc_count < MAX_NUMBER_OF_CONC_CONNECTIONS) |
| cc_count = cc_count + |
| policy_mgr_get_mode_specific_conn_info |
| (psoc, |
| &operating_channel[cc_count], |
| &vdev_id[cc_count], |
| PM_P2P_GO_MODE); |
| policy_mgr_debug("Number of beaconing entities (SAP + GO):%d", |
| cc_count); |
| if (!cc_count) { |
| policy_mgr_err("Could not retrieve SAP/GO operating channel&vdevid"); |
| goto end; |
| } |
| |
| policy_mgr_debug("wait if channel switch is already in progress"); |
| status = qdf_wait_single_event( |
| &pm_ctx->channel_switch_complete_evt, |
| CHANNEL_SWITCH_COMPLETE_TIMEOUT); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("wait for event failed, still continue with channel switch"); |
| } |
| if (!pm_ctx->hdd_cbacks.wlan_hdd_get_channel_for_sap_restart) { |
| policy_mgr_err("SAP restart get channel callback in NULL"); |
| goto end; |
| } |
| if (cc_count < MAX_NUMBER_OF_CONC_CONNECTIONS) |
| for (i = 0; i < cc_count; i++) { |
| status = pm_ctx->hdd_cbacks. |
| wlan_hdd_get_channel_for_sap_restart |
| (psoc, |
| vdev_id[i], &channel, &sec_ch); |
| if (status == QDF_STATUS_SUCCESS) { |
| policy_mgr_info("SAP restarts due to MCC->SCC switch, old chan :%d new chan: %d" |
| , operating_channel[i], channel); |
| break; |
| } |
| } |
| if (status != QDF_STATUS_SUCCESS) |
| policy_mgr_err("Failed to switch SAP channel"); |
| end: |
| if (work_info) { |
| qdf_mem_free(work_info); |
| if (pm_ctx) |
| pm_ctx->sta_ap_intf_check_work_info = NULL; |
| } |
| } |
| |
| void policy_mgr_check_sta_ap_concurrent_ch_intf(void *data) |
| { |
| qdf_ssr_protect(__func__); |
| __policy_mgr_check_sta_ap_concurrent_ch_intf(data); |
| qdf_ssr_unprotect(__func__); |
| } |
| |
| static bool policy_mgr_valid_sta_channel_check(struct wlan_objmgr_psoc *psoc, |
| uint8_t sta_channel) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| return false; |
| } |
| |
| if ((wlan_reg_is_dfs_ch(pm_ctx->pdev, sta_channel) && |
| (!policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc))) || |
| wlan_reg_is_passive_or_disable_ch(pm_ctx->pdev, sta_channel) || |
| !policy_mgr_is_safe_channel(psoc, sta_channel)) { |
| if (policy_mgr_is_hw_dbs_capable(psoc)) |
| return true; |
| else |
| return false; |
| } |
| else |
| return true; |
| } |
| |
| QDF_STATUS policy_mgr_valid_sap_conc_channel_check( |
| struct wlan_objmgr_psoc *psoc, uint8_t *con_ch, uint8_t sap_ch) |
| { |
| uint8_t channel = *con_ch; |
| uint8_t temp_channel = 0; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| bool sta_sap_scc_on_dfs_chan; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| /* |
| * if force SCC is set, Check if conc channel is DFS |
| * or passive or part of LTE avoided channel list. |
| * In that case move SAP to other band if DBS is supported, |
| * return otherwise |
| */ |
| if (!policy_mgr_is_force_scc(psoc)) |
| return QDF_STATUS_SUCCESS; |
| |
| /* |
| * if interference is 0, check if it is DBS case. If DBS case |
| * return from here. If SCC, check further if SAP can move to |
| * STA's channel. |
| */ |
| if (!channel && |
| (sap_ch != policy_mgr_mode_specific_get_channel( |
| psoc, PM_STA_MODE))) |
| return QDF_STATUS_SUCCESS; |
| else if (!channel) |
| channel = sap_ch; |
| |
| sta_sap_scc_on_dfs_chan = |
| policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); |
| |
| if (policy_mgr_valid_sta_channel_check(psoc, channel)) { |
| if (wlan_reg_is_dfs_ch(pm_ctx->pdev, channel) || |
| wlan_reg_is_passive_or_disable_ch(pm_ctx->pdev, channel) || |
| !(policy_mgr_sta_sap_scc_on_lte_coex_chan(psoc) || |
| policy_mgr_is_safe_channel(psoc, channel)) || |
| (!wlan_reg_is_etsi13_srd_chan_allowed_master_mode( |
| pm_ctx->pdev) && |
| wlan_reg_is_etsi13_srd_chan(pm_ctx->pdev, channel))) { |
| if (wlan_reg_is_dfs_ch(pm_ctx->pdev, channel) && |
| sta_sap_scc_on_dfs_chan) { |
| policy_mgr_debug("STA SAP SCC is allowed on DFS channel"); |
| goto update_chan; |
| } |
| |
| if (policy_mgr_is_hw_dbs_capable(psoc)) { |
| temp_channel = |
| policy_mgr_get_alternate_channel_for_sap(psoc); |
| policy_mgr_debug("temp_channel is %d", |
| temp_channel); |
| if (temp_channel) { |
| channel = temp_channel; |
| } else { |
| if (WLAN_REG_IS_5GHZ_CH(channel)) |
| channel = PM_24_GHZ_CHANNEL_6; |
| else |
| channel = PM_5_GHZ_CHANNEL_36; |
| } |
| if (!policy_mgr_is_safe_channel( |
| psoc, channel)) { |
| policy_mgr_warn( |
| "Can't have concurrency on %d as it is not safe", |
| channel); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } else { |
| policy_mgr_warn("Can't have concurrency on %d", |
| channel); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } |
| } |
| |
| update_chan: |
| if (channel != sap_ch) |
| *con_ch = channel; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * policy_mgr_check_concurrent_intf_and_restart_sap() - Check |
| * concurrent change intf |
| * @psoc: PSOC object information |
| * @operation_channel: operation channel |
| * @vdev_id: vdev id of SAP |
| * |
| * Checks the concurrent change interface and restarts SAP |
| * |
| * Return: None |
| */ |
| void policy_mgr_check_concurrent_intf_and_restart_sap( |
| struct wlan_objmgr_psoc *psoc) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| uint32_t mcc_to_scc_switch; |
| uint8_t operating_channel[MAX_NUMBER_OF_CONC_CONNECTIONS] = {0}; |
| uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS] = {0}; |
| uint32_t cc_count = 0; |
| bool restart_sap = false; |
| uint8_t sap_ch; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| return; |
| } |
| if (policy_mgr_get_connection_count(psoc) == 1) { |
| /* |
| * If STA+SAP sessions are on DFS channel and STA+SAP SCC is |
| * enabled on DFS channel then move the SAP out of DFS channel |
| * as soon as STA gets disconnect. |
| * If STA+SAP sessions are on unsafe channel and STA+SAP SCC is |
| * enabled on unsafe channel then move the SAP to safe channel |
| * as soon as STA disconnected. |
| */ |
| if (policy_mgr_is_sap_restart_required_after_sta_disconnect( |
| psoc, &sap_ch)) { |
| policy_mgr_debug("move the SAP to configured channel %u", |
| sap_ch); |
| restart_sap = true; |
| goto sap_restart; |
| } |
| } |
| |
| /* |
| * force SCC with STA+STA+SAP will need some additional logic |
| */ |
| cc_count = policy_mgr_get_mode_specific_conn_info(psoc, |
| &operating_channel[cc_count], |
| &vdev_id[cc_count], PM_STA_MODE); |
| if (!cc_count) { |
| policy_mgr_debug("Could not get STA operating channel&vdevid"); |
| return; |
| } |
| |
| mcc_to_scc_switch = |
| policy_mgr_get_mcc_to_scc_switch_mode(psoc); |
| policy_mgr_info("MCC to SCC switch: %d chan: %d", |
| mcc_to_scc_switch, operating_channel[0]); |
| |
| if (!policy_mgr_is_restart_sap_allowed(psoc, mcc_to_scc_switch)) { |
| policy_mgr_debug( |
| "No action taken at check_concurrent_intf_and_restart_sap"); |
| return; |
| } |
| |
| sap_restart: |
| /* |
| * If sta_sap_scc_on_dfs_chan is true then standalone SAP is not |
| * allowed on DFS channel. SAP is allowed on DFS channel only when STA |
| * is already connected on that channel. |
| * In following condition restart_sap will be true if |
| * sta_sap_scc_on_dfs_chan is true and SAP is on DFS channel. |
| * This scenario can come if STA+SAP are operating on DFS channel and |
| * STA gets disconnected. |
| */ |
| if (restart_sap || |
| ((mcc_to_scc_switch != QDF_MCC_TO_SCC_SWITCH_DISABLE) && |
| policy_mgr_valid_sta_channel_check(psoc, operating_channel[0]) && |
| !pm_ctx->sta_ap_intf_check_work_info)) { |
| struct sta_ap_intf_check_work_ctx *work_info; |
| work_info = qdf_mem_malloc( |
| sizeof(struct sta_ap_intf_check_work_ctx)); |
| pm_ctx->sta_ap_intf_check_work_info = work_info; |
| if (work_info) { |
| work_info->psoc = psoc; |
| qdf_create_work(0, &pm_ctx->sta_ap_intf_check_work, |
| policy_mgr_check_sta_ap_concurrent_ch_intf, |
| work_info); |
| qdf_sched_work(0, &pm_ctx->sta_ap_intf_check_work); |
| policy_mgr_info( |
| "Checking for Concurrent Change interference"); |
| } |
| } |
| } |
| #endif /* FEATURE_WLAN_MCC_TO_SCC_SWITCH */ |
| |
| #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH |
| /** |
| * policy_mgr_change_sap_channel_with_csa() - Move SAP channel using (E)CSA |
| * @psoc: PSOC object information |
| * @vdev_id: Vdev id |
| * @channel: Channel to change |
| * @ch_width: channel width to change |
| * @forced: Force to switch channel, ignore SCC/MCC check |
| * |
| * Invoke the callback function to change SAP channel using (E)CSA |
| * |
| * Return: None |
| */ |
| void policy_mgr_change_sap_channel_with_csa(struct wlan_objmgr_psoc *psoc, |
| uint8_t vdev_id, uint32_t channel, |
| uint32_t ch_width, |
| bool forced) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid context"); |
| return; |
| } |
| |
| if (pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb) { |
| policy_mgr_info("SAP change change without restart"); |
| pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb(psoc, |
| vdev_id, channel, ch_width, forced); |
| } |
| } |
| #endif |
| |
| QDF_STATUS policy_mgr_wait_for_connection_update(struct wlan_objmgr_psoc *psoc) |
| { |
| QDF_STATUS status; |
| struct policy_mgr_psoc_priv_obj *policy_mgr_context; |
| |
| policy_mgr_context = policy_mgr_get_context(psoc); |
| if (!policy_mgr_context) { |
| policy_mgr_err("Invalid context"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| status = qdf_wait_single_event( |
| &policy_mgr_context->connection_update_done_evt, |
| CONNECTION_UPDATE_TIMEOUT); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("wait for event failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_reset_connection_update(struct wlan_objmgr_psoc *psoc) |
| { |
| QDF_STATUS status; |
| struct policy_mgr_psoc_priv_obj *policy_mgr_context; |
| |
| policy_mgr_context = policy_mgr_get_context(psoc); |
| if (!policy_mgr_context) { |
| policy_mgr_err("Invalid context"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| status = qdf_event_reset( |
| &policy_mgr_context->connection_update_done_evt); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("clear event failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_set_connection_update(struct wlan_objmgr_psoc *psoc) |
| { |
| QDF_STATUS status; |
| struct policy_mgr_psoc_priv_obj *policy_mgr_context; |
| |
| policy_mgr_context = policy_mgr_get_context(psoc); |
| if (!policy_mgr_context) { |
| policy_mgr_err("Invalid context"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| status = qdf_event_set(&policy_mgr_context->connection_update_done_evt); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("set event failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_set_chan_switch_complete_evt( |
| struct wlan_objmgr_psoc *psoc) |
| { |
| QDF_STATUS status; |
| struct policy_mgr_psoc_priv_obj *policy_mgr_context; |
| |
| policy_mgr_context = policy_mgr_get_context(psoc); |
| |
| if (!policy_mgr_context) { |
| policy_mgr_err("Invalid context"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| status = qdf_event_set( |
| &policy_mgr_context->channel_switch_complete_evt); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("set event failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_reset_chan_switch_complete_evt( |
| struct wlan_objmgr_psoc *psoc) |
| { |
| QDF_STATUS status; |
| struct policy_mgr_psoc_priv_obj *policy_mgr_context; |
| |
| policy_mgr_context = policy_mgr_get_context(psoc); |
| |
| if (!policy_mgr_context) { |
| policy_mgr_err("Invalid context"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| status = qdf_event_reset( |
| &policy_mgr_context->channel_switch_complete_evt); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("reset event failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_set_opportunistic_update(struct wlan_objmgr_psoc *psoc) |
| { |
| QDF_STATUS status; |
| struct policy_mgr_psoc_priv_obj *policy_mgr_context; |
| |
| policy_mgr_context = policy_mgr_get_context(psoc); |
| if (!policy_mgr_context) { |
| policy_mgr_err("Invalid context"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| status = qdf_event_set( |
| &policy_mgr_context->opportunistic_update_done_evt); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("set event failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_stop_opportunistic_timer(struct wlan_objmgr_psoc *psoc) |
| { |
| struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; |
| |
| policy_mgr_ctx = policy_mgr_get_context(psoc); |
| if (!policy_mgr_ctx) { |
| policy_mgr_err("Invalid context"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (policy_mgr_ctx->dbs_opportunistic_timer.state != |
| QDF_TIMER_STATE_RUNNING) |
| return QDF_STATUS_SUCCESS; |
| |
| qdf_mc_timer_stop(&policy_mgr_ctx->dbs_opportunistic_timer); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_restart_opportunistic_timer( |
| struct wlan_objmgr_psoc *psoc, bool check_state) |
| { |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; |
| |
| policy_mgr_ctx = policy_mgr_get_context(psoc); |
| if (!policy_mgr_ctx) { |
| policy_mgr_err("Invalid context"); |
| return status; |
| } |
| |
| if (check_state && |
| QDF_TIMER_STATE_RUNNING != |
| policy_mgr_ctx->dbs_opportunistic_timer.state) |
| return status; |
| |
| qdf_mc_timer_stop(&policy_mgr_ctx->dbs_opportunistic_timer); |
| |
| status = qdf_mc_timer_start( |
| &policy_mgr_ctx->dbs_opportunistic_timer, |
| DBS_OPPORTUNISTIC_TIME * 1000); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("failed to start opportunistic timer"); |
| return status; |
| } |
| |
| return status; |
| } |
| |
| QDF_STATUS policy_mgr_set_hw_mode_on_channel_switch( |
| struct wlan_objmgr_psoc *psoc, uint8_t session_id) |
| { |
| QDF_STATUS status = QDF_STATUS_E_FAILURE, qdf_status; |
| enum policy_mgr_conc_next_action action; |
| |
| if (!policy_mgr_is_hw_dbs_capable(psoc)) { |
| policy_mgr_err("PM/DBS is disabled"); |
| return status; |
| } |
| |
| action = (*policy_mgr_get_current_pref_hw_mode_ptr)(psoc); |
| if ((action != PM_DBS_DOWNGRADE) && |
| (action != PM_SINGLE_MAC_UPGRADE) && |
| (action != PM_DBS1_DOWNGRADE) && |
| (action != PM_DBS2_DOWNGRADE)) { |
| policy_mgr_err("Invalid action: %d", action); |
| status = QDF_STATUS_SUCCESS; |
| goto done; |
| } |
| |
| policy_mgr_debug("action:%d session id:%d", action, session_id); |
| |
| /* Opportunistic timer is started, PM will check if MCC upgrade can be |
| * done on timer expiry. This avoids any possible ping pong effect |
| * as well. |
| */ |
| if (action == PM_SINGLE_MAC_UPGRADE) { |
| qdf_status = policy_mgr_restart_opportunistic_timer( |
| psoc, false); |
| if (QDF_IS_STATUS_SUCCESS(qdf_status)) |
| policy_mgr_debug("opportunistic timer for MCC upgrade"); |
| goto done; |
| } |
| |
| /* For DBS, we want to move right away to DBS mode */ |
| status = policy_mgr_next_actions(psoc, session_id, action, |
| POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("no set hw mode command was issued"); |
| goto done; |
| } |
| done: |
| /* success must be returned only when a set hw mode was done */ |
| return status; |
| } |
| |
| void policy_mgr_checkn_update_hw_mode_single_mac_mode( |
| struct wlan_objmgr_psoc *psoc, uint8_t channel) |
| { |
| uint8_t i; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid Context"); |
| return; |
| } |
| |
| if (QDF_TIMER_STATE_RUNNING == |
| pm_ctx->dbs_opportunistic_timer.state) |
| qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); |
| |
| qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); |
| for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { |
| if (pm_conc_connection_list[i].in_use) { |
| if (!WLAN_REG_IS_SAME_BAND_CHANNELS(channel, |
| pm_conc_connection_list[i].chan)) { |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| policy_mgr_debug("DBS required"); |
| return; |
| } |
| if (policy_mgr_is_hw_dbs_2x2_capable(psoc) && |
| (WLAN_REG_IS_24GHZ_CH(channel) || |
| WLAN_REG_IS_24GHZ_CH |
| (pm_conc_connection_list[i].chan))) { |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| policy_mgr_debug("DBS required"); |
| return; |
| } |
| } |
| } |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| pm_dbs_opportunistic_timer_handler((void *)psoc); |
| } |
| |
| void policy_mgr_check_and_stop_opportunistic_timer( |
| struct wlan_objmgr_psoc *psoc, uint8_t id) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| enum policy_mgr_conc_next_action action = PM_NOP; |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| enum policy_mgr_conn_update_reason reason; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid Context"); |
| return; |
| } |
| if (QDF_TIMER_STATE_RUNNING == |
| pm_ctx->dbs_opportunistic_timer.state) { |
| qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); |
| action = policy_mgr_need_opportunistic_upgrade(psoc, &reason); |
| if (action) { |
| qdf_event_reset(&pm_ctx->opportunistic_update_done_evt); |
| status = policy_mgr_next_actions(psoc, id, action, |
| reason); |
| if (status != QDF_STATUS_SUCCESS) { |
| policy_mgr_err("Failed in policy_mgr_next_actions"); |
| return; |
| } |
| status = qdf_wait_single_event( |
| &pm_ctx->opportunistic_update_done_evt, |
| CONNECTION_UPDATE_TIMEOUT); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("wait for event failed"); |
| return; |
| } |
| } |
| } |
| } |
| |
| void policy_mgr_set_hw_mode_change_in_progress( |
| struct wlan_objmgr_psoc *psoc, enum policy_mgr_hw_mode_change value) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid Context"); |
| return; |
| } |
| |
| qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); |
| pm_ctx->hw_mode_change_in_progress = value; |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| |
| policy_mgr_debug("hw_mode_change_in_progress:%d", value); |
| } |
| |
| enum policy_mgr_hw_mode_change policy_mgr_is_hw_mode_change_in_progress( |
| struct wlan_objmgr_psoc *psoc) |
| { |
| enum policy_mgr_hw_mode_change value; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| value = POLICY_MGR_HW_MODE_NOT_IN_PROGRESS; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid Context"); |
| return value; |
| } |
| qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); |
| value = pm_ctx->hw_mode_change_in_progress; |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| |
| return value; |
| } |
| |
| enum policy_mgr_hw_mode_change policy_mgr_get_hw_mode_change_from_hw_mode_index( |
| struct wlan_objmgr_psoc *psoc, uint32_t hw_mode_index) |
| { |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| struct policy_mgr_hw_mode_params hw_mode; |
| enum policy_mgr_hw_mode_change value |
| = POLICY_MGR_HW_MODE_NOT_IN_PROGRESS; |
| QDF_STATUS status; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid Context"); |
| return value; |
| } |
| |
| status = policy_mgr_get_hw_mode_from_idx(psoc, hw_mode_index, &hw_mode); |
| if (status != QDF_STATUS_SUCCESS) { |
| policy_mgr_err("Failed to get HW mode index"); |
| return value; |
| } |
| |
| if (hw_mode.dbs_cap) { |
| policy_mgr_info("DBS is requested with HW (%d)", |
| hw_mode_index); |
| value = POLICY_MGR_DBS_IN_PROGRESS; |
| goto ret_value; |
| } |
| |
| if (hw_mode.sbs_cap) { |
| policy_mgr_info("SBS is requested with HW (%d)", |
| hw_mode_index); |
| value = POLICY_MGR_SBS_IN_PROGRESS; |
| goto ret_value; |
| } |
| |
| value = POLICY_MGR_SMM_IN_PROGRESS; |
| policy_mgr_info("SMM is requested with HW (%d)", hw_mode_index); |
| |
| ret_value: |
| return value; |
| } |
| |
| #ifdef MPC_UT_FRAMEWORK |
| QDF_STATUS policy_mgr_update_connection_info_utfw( |
| struct wlan_objmgr_psoc *psoc, |
| uint32_t vdev_id, uint32_t tx_streams, uint32_t rx_streams, |
| uint32_t chain_mask, uint32_t type, uint32_t sub_type, |
| uint32_t channelid, uint32_t mac_id) |
| { |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| uint32_t conn_index = 0, found = 0; |
| struct policy_mgr_psoc_priv_obj *pm_ctx; |
| |
| pm_ctx = policy_mgr_get_context(psoc); |
| if (!pm_ctx) { |
| policy_mgr_err("Invalid Context"); |
| return status; |
| } |
| |
| qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); |
| while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { |
| if (vdev_id == pm_conc_connection_list[conn_index].vdev_id) { |
| /* debug msg */ |
| found = 1; |
| break; |
| } |
| conn_index++; |
| } |
| qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); |
| if (!found) { |
| /* err msg */ |
| policy_mgr_err("can't find vdev_id %d in pm_conc_connection_list", |
| vdev_id); |
| return status; |
| } |
| policy_mgr_debug("--> updating entry at index[%d]", conn_index); |
| |
| policy_mgr_update_conc_list(psoc, conn_index, |
| policy_mgr_get_mode(type, sub_type), |
| channelid, HW_MODE_20_MHZ, |
| mac_id, chain_mask, 0, vdev_id, true, true); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_incr_connection_count_utfw(struct wlan_objmgr_psoc *psoc, |
| uint32_t vdev_id, uint32_t tx_streams, uint32_t rx_streams, |
| uint32_t chain_mask, uint32_t type, uint32_t sub_type, |
| uint32_t channelid, uint32_t mac_id) |
| { |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| uint32_t conn_index = 0; |
| bool update_conn = true; |
| enum policy_mgr_con_mode mode; |
| |
| conn_index = policy_mgr_get_connection_count(psoc); |
| if (MAX_NUMBER_OF_CONC_CONNECTIONS <= conn_index) { |
| /* err msg */ |
| policy_mgr_err("exceeded max connection limit %d", |
| MAX_NUMBER_OF_CONC_CONNECTIONS); |
| return status; |
| } |
| policy_mgr_debug("--> filling entry at index[%d]", conn_index); |
| |
| mode = policy_mgr_get_mode(type, sub_type); |
| if (mode == PM_STA_MODE || mode == PM_P2P_CLIENT_MODE) |
| update_conn = false; |
| |
| policy_mgr_update_conc_list(psoc, conn_index, |
| mode, |
| channelid, HW_MODE_20_MHZ, |
| mac_id, chain_mask, 0, vdev_id, true, |
| update_conn); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS policy_mgr_decr_connection_count_utfw(struct wlan_objmgr_psoc *psoc, |
| uint32_t del_all, uint32_t vdev_id) |
| { |
| QDF_STATUS status; |
| |
| if (del_all) { |
| status = policy_mgr_psoc_disable(psoc); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("Policy manager initialization failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| status = policy_mgr_psoc_enable(psoc); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| policy_mgr_err("Policy manager initialization failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } else { |
| policy_mgr_decr_connection_count(psoc, vdev_id); |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| enum policy_mgr_pcl_type policy_mgr_get_pcl_from_first_conn_table( |
| enum policy_mgr_con_mode type, |
| enum policy_mgr_conc_priority_mode sys_pref) |
| { |
| if ((sys_pref >= PM_MAX_CONC_PRIORITY_MODE) || |
| (type >= PM_MAX_NUM_OF_MODE)) |
| return PM_MAX_PCL_TYPE; |
| return first_connection_pcl_table[type][sys_pref]; |
| } |
| |
| enum policy_mgr_pcl_type policy_mgr_get_pcl_from_second_conn_table( |
| enum policy_mgr_one_connection_mode idx, enum policy_mgr_con_mode type, |
| enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable) |
| { |
| if ((idx >= PM_MAX_ONE_CONNECTION_MODE) || |
| (sys_pref >= PM_MAX_CONC_PRIORITY_MODE) || |
| (type >= PM_MAX_NUM_OF_MODE)) |
| return PM_MAX_PCL_TYPE; |
| if (dbs_capable) |
| return (*second_connection_pcl_dbs_table)[idx][type][sys_pref]; |
| else |
| return second_connection_pcl_nodbs_table[idx][type][sys_pref]; |
| } |
| |
| enum policy_mgr_pcl_type policy_mgr_get_pcl_from_third_conn_table( |
| enum policy_mgr_two_connection_mode idx, enum policy_mgr_con_mode type, |
| enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable) |
| { |
| if ((idx >= PM_MAX_TWO_CONNECTION_MODE) || |
| (sys_pref >= PM_MAX_CONC_PRIORITY_MODE) || |
| (type >= PM_MAX_NUM_OF_MODE)) |
| return PM_MAX_PCL_TYPE; |
| if (dbs_capable) |
| return (*third_connection_pcl_dbs_table)[idx][type][sys_pref]; |
| else |
| return third_connection_pcl_nodbs_table[idx][type][sys_pref]; |
| } |
| #endif |