diff --git a/core/cds/inc/cds_concurrency.h b/core/cds/inc/cds_concurrency.h index 71d61ed720fb..003219b4aa4b 100644 --- a/core/cds/inc/cds_concurrency.h +++ b/core/cds/inc/cds_concurrency.h @@ -587,6 +587,9 @@ QDF_STATUS cds_init_policy_mgr(void); QDF_STATUS cds_deinit_policy_mgr(void); QDF_STATUS cds_get_pcl(enum cds_con_mode mode, uint8_t *pcl_Channels, uint32_t *len); +uint8_t cds_get_nondfs_preferred_channel(enum cds_con_mode mode, + bool for_existing_conn); +bool cds_is_any_nondfs_chnl_present(uint8_t *channel); bool cds_allow_concurrency(enum cds_con_mode mode, uint8_t channel, enum hw_mode_bandwidth bw); enum cds_conc_priority_mode cds_get_first_connection_pcl_table_index(void); diff --git a/core/cds/inc/cds_utils.h b/core/cds/inc/cds_utils.h index 66a30b38d587..1bc711484120 100644 --- a/core/cds/inc/cds_utils.h +++ b/core/cds/inc/cds_utils.h @@ -56,6 +56,7 @@ #define CDS_24_GHZ_BASE_FREQ (2407) #define CDS_5_GHZ_BASE_FREQ (5000) +#define CDS_24_GHZ_CHANNEL_6 (6) #define CDS_24_GHZ_CHANNEL_14 (14) #define CDS_24_GHZ_CHANNEL_15 (15) #define CDS_24_GHZ_CHANNEL_27 (27) diff --git a/core/cds/src/cds_concurrency.c b/core/cds/src/cds_concurrency.c index 50e2238a0717..3972ade8badb 100644 --- a/core/cds/src/cds_concurrency.c +++ b/core/cds/src/cds_concurrency.c @@ -4251,6 +4251,7 @@ QDF_STATUS cds_decr_connection_count(uint32_t vdev_id) * @len: Number of channels * @order: no order OR 2.4 Ghz channel followed by 5 Ghz * channel OR 5 Ghz channel followed by 2.4 Ghz channel + * @skip_dfs_channel: if this flag is true then skip the dfs channel * * * This function provides the channel(s) on which current @@ -4259,7 +4260,8 @@ QDF_STATUS cds_decr_connection_count(uint32_t vdev_id) * Return: QDF_STATUS */ QDF_STATUS cds_get_connection_channels(uint8_t *channels, - uint32_t *len, uint8_t order) + uint32_t *len, uint8_t order, + bool skip_dfs_channel) { QDF_STATUS status = QDF_STATUS_SUCCESS; uint32_t conn_index = 0, num_channels = 0; @@ -4273,45 +4275,55 @@ QDF_STATUS cds_get_connection_channels(uint8_t *channels, if (0 == order) { while (CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { - channels[num_channels++] = - conc_connection_list[conn_index++].chan; + if (skip_dfs_channel && CDS_IS_DFS_CH( + conc_connection_list[conn_index].chan)) + conn_index++; + else + channels[num_channels++] = + conc_connection_list[conn_index++].chan; } *len = num_channels; } else if (1 == order) { while (CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { if (CDS_IS_CHANNEL_24GHZ( - conc_connection_list[conn_index].chan)) { + conc_connection_list[conn_index].chan)) channels[num_channels++] = conc_connection_list[conn_index++].chan; - } else + else conn_index++; } conn_index = 0; while (CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { - if (CDS_IS_CHANNEL_5GHZ( - conc_connection_list[conn_index].chan)) { + if (skip_dfs_channel && CDS_IS_DFS_CH( + conc_connection_list[conn_index].chan)) + conn_index++; + else if (CDS_IS_CHANNEL_5GHZ( + conc_connection_list[conn_index].chan)) channels[num_channels++] = conc_connection_list[conn_index++].chan; - } else + else conn_index++; } *len = num_channels; } else if (2 == order) { while (CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { - if (CDS_IS_CHANNEL_5GHZ( - conc_connection_list[conn_index].chan)) { + if (skip_dfs_channel && CDS_IS_DFS_CH( + conc_connection_list[conn_index].chan)) + conn_index++; + else if (CDS_IS_CHANNEL_5GHZ( + conc_connection_list[conn_index].chan)) channels[num_channels++] = conc_connection_list[conn_index++].chan; - } else + else conn_index++; } conn_index = 0; while (CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { if (CDS_IS_CHANNEL_24GHZ( - conc_connection_list[conn_index].chan)) { + conc_connection_list[conn_index].chan)) channels[num_channels++] = conc_connection_list[conn_index++].chan; - } else + else conn_index++; } *len = num_channels; @@ -4319,6 +4331,7 @@ QDF_STATUS cds_get_connection_channels(uint8_t *channels, cds_err("unknown order %d", order); status = QDF_STATUS_E_FAILURE; } + return status; } @@ -4393,7 +4406,8 @@ void cds_update_with_safe_channel_list(uint8_t *pcl_channels, uint32_t *len) * @hdd_ctx: HDD Context * @pcl: The preferred channel list enum * @pcl_channels: PCL channels - * @len: lenght of the PCL + * @len: length of the PCL + * @mode: concurrency mode for which channel list is requested * * This function provides the actual channel list based on the * current regulatory domain derived using preferred channel @@ -4402,7 +4416,7 @@ void cds_update_with_safe_channel_list(uint8_t *pcl_channels, uint32_t *len) * Return: Channel List */ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, - uint8_t *pcl_channels, uint32_t *len) + uint8_t *pcl_channels, uint32_t *len, enum cds_con_mode mode) { QDF_STATUS status = QDF_STATUS_E_FAILURE; uint32_t num_channels = WNI_CFG_VALID_CHANNEL_LIST_LEN; @@ -4410,6 +4424,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, uint8_t channel_list[MAX_NUM_CHAN] = {0}; uint8_t channel_list_24[MAX_NUM_CHAN] = {0}; uint8_t channel_list_5[MAX_NUM_CHAN] = {0}; + bool skip_dfs_channel = false; hdd_context_t *hdd_ctx; hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); @@ -4443,6 +4458,17 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, cds_err("No valid channel"); return status; } + + /* + * if you have atleast one STA connection then don't fill DFS channels + * in the preferred channel list + */ + if (((mode == CDS_SAP_MODE) || (mode == CDS_P2P_GO_MODE)) && + (cds_mode_specific_connection_count(CDS_STA_MODE, NULL) > 0)) { + cds_info("STA present, skip DFS channels from pcl for SAP/Go"); + skip_dfs_channel = true; + } + /* Let's divide the list in 2.4 & 5 Ghz lists */ while ((channel_list[chan_index] <= 11) && (chan_index_24 < MAX_NUM_CHAN)) @@ -4457,9 +4483,15 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, channel_list[chan_index++]; } } - while ((chan_index < num_channels) && - (chan_index_5 < MAX_NUM_CHAN)) + + while ((chan_index < num_channels) && (chan_index_5 < MAX_NUM_CHAN)) { + if ((true == skip_dfs_channel) && + CDS_IS_DFS_CH(channel_list[chan_index])) { + chan_index++; + continue; + } channel_list_5[chan_index_5++] = channel_list[chan_index++]; + } num_channels = 0; switch (pcl) { @@ -4478,7 +4510,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, case CDS_SCC_CH: case CDS_MCC_CH: cds_get_connection_channels( - channel_list, &num_channels, 0); + channel_list, &num_channels, 0, skip_dfs_channel); qdf_mem_copy(pcl_channels, channel_list, num_channels); *len = num_channels; status = QDF_STATUS_SUCCESS; @@ -4486,7 +4518,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, case CDS_SCC_CH_24G: case CDS_MCC_CH_24G: cds_get_connection_channels( - channel_list, &num_channels, 0); + channel_list, &num_channels, 0, skip_dfs_channel); qdf_mem_copy(pcl_channels, channel_list, num_channels); *len = num_channels; qdf_mem_copy(&pcl_channels[num_channels], @@ -4497,7 +4529,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, case CDS_SCC_CH_5G: case CDS_MCC_CH_5G: cds_get_connection_channels( - channel_list, &num_channels, 0); + channel_list, &num_channels, 0, skip_dfs_channel); qdf_mem_copy(pcl_channels, channel_list, num_channels); *len = num_channels; @@ -4512,7 +4544,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, chan_index_24); *len = chan_index_24; cds_get_connection_channels( - channel_list, &num_channels, 0); + channel_list, &num_channels, 0, skip_dfs_channel); qdf_mem_copy(&pcl_channels[chan_index_24], channel_list, num_channels); *len += num_channels; @@ -4524,7 +4556,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, chan_index_5); *len = chan_index_5; cds_get_connection_channels( - channel_list, &num_channels, 0); + channel_list, &num_channels, 0, skip_dfs_channel); qdf_mem_copy(&pcl_channels[chan_index_5], channel_list, num_channels); *len += num_channels; @@ -4532,7 +4564,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, break; case CDS_SCC_ON_24_SCC_ON_5: cds_get_connection_channels( - channel_list, &num_channels, 1); + channel_list, &num_channels, 1, skip_dfs_channel); qdf_mem_copy(pcl_channels, channel_list, num_channels); *len = num_channels; @@ -4540,14 +4572,14 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, break; case CDS_SCC_ON_5_SCC_ON_24: cds_get_connection_channels( - channel_list, &num_channels, 2); + channel_list, &num_channels, 2, skip_dfs_channel); qdf_mem_copy(pcl_channels, channel_list, num_channels); *len = num_channels; status = QDF_STATUS_SUCCESS; break; case CDS_SCC_ON_24_SCC_ON_5_24G: cds_get_connection_channels( - channel_list, &num_channels, 1); + channel_list, &num_channels, 1, skip_dfs_channel); qdf_mem_copy(pcl_channels, channel_list, num_channels); *len = num_channels; qdf_mem_copy(&pcl_channels[num_channels], @@ -4557,7 +4589,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, break; case CDS_SCC_ON_24_SCC_ON_5_5G: cds_get_connection_channels( - channel_list, &num_channels, 1); + channel_list, &num_channels, 1, skip_dfs_channel); qdf_mem_copy(pcl_channels, channel_list, num_channels); *len = num_channels; qdf_mem_copy(&pcl_channels[num_channels], @@ -4567,7 +4599,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, break; case CDS_SCC_ON_5_SCC_ON_24_24G: cds_get_connection_channels( - channel_list, &num_channels, 2); + channel_list, &num_channels, 2, skip_dfs_channel); qdf_mem_copy(pcl_channels, channel_list, num_channels); *len = num_channels; qdf_mem_copy(&pcl_channels[num_channels], @@ -4577,7 +4609,7 @@ QDF_STATUS cds_get_channel_list(enum cds_pcl_type pcl, break; case CDS_SCC_ON_5_SCC_ON_24_5G: cds_get_connection_channels( - channel_list, &num_channels, 2); + channel_list, &num_channels, 2, skip_dfs_channel); qdf_mem_copy(pcl_channels, channel_list, num_channels); *len = num_channels; qdf_mem_copy(&pcl_channels[num_channels], @@ -4743,7 +4775,7 @@ QDF_STATUS cds_get_pcl(enum cds_con_mode mode, /* once the PCL enum is obtained find out the exact channel list with * help from sme_get_cfg_valid_channels */ - status = cds_get_channel_list(pcl, pcl_channels, len); + status = cds_get_channel_list(pcl, pcl_channels, len, mode); if (status == QDF_STATUS_SUCCESS) { uint32_t i; cds_debug("pcl len:%d", *len); @@ -4944,9 +4976,41 @@ bool cds_allow_concurrency(enum cds_con_mode mode, num_connections)) goto done; - /* don't allow MCC if SAP/GO on DFS channel or about to come up - * on DFS channel - */ + /* + * If you already have STA connection then don't + * allow any other persona to make connection on DFS channel + * because STA might be on non-DFS right now but later on as + * part of roaming if STA connects to DFS channel which happens + * to be different than requested DFS channel then MCC DFS + * scenario will be encountered + */ + count = cds_mode_specific_connection_count(CDS_STA_MODE, + list); + if ((count > 0) && CDS_IS_DFS_CH(channel)) { + /* err msg */ + cds_err("STA active, don't allow DFS channel for 2nd connection"); +#ifndef QCA_WIFI_3_0_EMU + /* + * REMOVE THIS COMMENT ONCE QCA_WIFI_3_0_EMU + * FLAG GETS REMOVED + * ================================================== + * As on emulation we can support only 2 nodes max, if + * we don't allow STA DFS (ch.100) + SAP DFS (ch.100) + * then we can't test the mechanism on other device + * where corresponding SAP DFS(ch.100) + STA DFS( + * STA is trying to come up and as a result it will push + * SAP on this device to non-dfs through CSA IE) + * will trigger CSA. + * ================================================= + */ + goto done; +#endif + } + + /* + * don't allow MCC if SAP/GO on DFS channel or about to come up + * on DFS channel + */ count = cds_mode_specific_connection_count( CDS_P2P_GO_MODE, list); while (index < count) { @@ -8068,3 +8132,107 @@ QDF_STATUS cds_register_sap_restart_channel_switch_cb( return QDF_STATUS_SUCCESS; } #endif + + + +/** + * cds_get_nondfs_preferred_channel() - to get non-dfs preferred channel + * for given mode + * @mode: mode for which preferred non-dfs channel is requested + * @for_existing_conn: flag to indicate if preferred channel is requested + * for existing connection + * + * this routine will return non-dfs channel + * 1) for getting non-dfs preferred channel, first we check if there are any + * other connection exist whose channel is non-dfs. if yes then return that + * channel so that we can accommodate upto 3 mode concurrency. + * 2) if there no any other connection present then query concurrency module + * to give preferred channel list. once we get preferred channel list, loop + * through list to find first non-dfs channel from ascending order. + * + * Return: uint8_t non-dfs channel + */ +uint8_t +cds_get_nondfs_preferred_channel(enum cds_con_mode mode, + bool for_existing_conn) +{ + uint8_t pcl_channels[NUM_CHANNELS]; + /* + * in worst case if we can't find any channel at all + * then return 2.4G channel, so atleast we won't fall + * under 5G MCC scenario + */ + uint8_t channel = CDS_24_GHZ_CHANNEL_6; + uint32_t i, pcl_len; + + if (true == for_existing_conn) { + /* + * First try to see if there is any non-dfs channel already + * present in current connection table. If yes then return + * that channel + */ + if (true == cds_is_any_nondfs_chnl_present(&channel)) + return channel; + + if (QDF_STATUS_SUCCESS != cds_get_pcl_for_existing_conn(mode, + &pcl_channels[0], &pcl_len)) + return channel; + } else { + if (QDF_STATUS_SUCCESS != cds_get_pcl(mode, + &pcl_channels[0], &pcl_len)) + return channel; + } + + for (i = 0; i < pcl_len; i++) { +#ifdef QCA_WIFI_3_0_EMU + /* + * for Emulation platform MCC is not allowed. + * so better don't return 5G channel as this API mostly + * called when one more persona wants to come up on 5G band + */ + if (CDS_IS_CHANNEL_5GHZ(pcl_channels[i])) { + continue; + } +#endif + if (CDS_IS_DFS_CH(pcl_channels[i])) { + continue; + } else { + channel = pcl_channels[i]; + break; + } + } + return channel; +} +/** + * cds_is_any_nondfs_chnl_present() - Find any non-dfs channel from conc table + * @channel: pointer to channel which needs to be filled + * + * In-case if any connection is already present whose channel is none dfs then + * return that channel + * + * Return: true up-on finding non-dfs channel else false + */ +bool cds_is_any_nondfs_chnl_present(uint8_t *channel) +{ + cds_context_type *cds_ctx; + bool status = false; + uint32_t conn_index = 0; + cds_ctx = cds_get_context(QDF_MODULE_ID_QDF); + + if (!cds_ctx) { + cds_err("Invalid CDS Context"); + return false; + } + qdf_mutex_acquire(&cds_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (conc_connection_list[conn_index].in_use && + !CDS_IS_DFS_CH(conc_connection_list[conn_index].chan)) { + *channel = conc_connection_list[conn_index].chan; + status = true; + } + } + qdf_mutex_release(&cds_ctx->qdf_conc_list_lock); + return status; +} + diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index 2a2c9940a53a..7db20291e808 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -7902,6 +7902,107 @@ void hdd_select_cbmode(hdd_adapter_t *pAdapter, uint8_t operationChannel) &ch_params); } +/** + * wlan_hdd_handle_sap_sta_dfs_conc() - to handle SAP STA DFS conc + * @adapter: STA adapter + * @roam_profile: STA roam profile + * + * This routine will move SAP from dfs to non-dfs, if sta is coming up. + * + * Return: false if sta-sap conc is not allowed, else return true + */ +static bool wlan_hdd_handle_sap_sta_dfs_conc(hdd_adapter_t *adapter, + tCsrRoamProfile *roam_profile) +{ + hdd_context_t *hdd_ctx; + hdd_adapter_t *ap_adapter; + hdd_ap_ctx_t *hdd_ap_ctx; + hdd_hostapd_state_t *hostapd_state; + uint8_t channel = 0; + QDF_STATUS status; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return true; + } + + ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); + /* probably no sap running, no handling required */ + if (ap_adapter == NULL) + return true; + + /* + * sap is not in started state, so it is fine to go ahead with sta. + * if sap is currently doing CAC then don't allow sta to go further. + */ + if (!test_bit(SOFTAP_BSS_STARTED, &(ap_adapter)->event_flags) && + (hdd_ctx->dev_dfs_cac_status != DFS_CAC_IN_PROGRESS)) + return true; + + if (hdd_ctx->dev_dfs_cac_status == DFS_CAC_IN_PROGRESS) { + hdd_err("Concurrent SAP is in CAC state, STA is not allowed"); + return false; + } + + /* + * log and return error, if we allow STA to go through, we don't + * know what is going to happen better stop sta connection + */ + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); + if (NULL == hdd_ap_ctx) { + hdd_err("AP context not found"); + return false; + } + + /* sap is on non-dfs channel, nothing to handle */ + if (!CDS_IS_DFS_CH(hdd_ap_ctx->operatingChannel)) { + hdd_info("sap is on non-dfs channel, sta is allowed"); + return true; + } + /* + * find out by looking in to scan cache where sta is going to + * connect by passing its roam_profile. if channel is 0 or DFS then + * better to call pcl and find out the best channel. if channel + * is non-dfs then better move SAP to STA's channel to make + * scc, so we have room for 3port MCC scenario + */ + status = cds_get_channel_from_scan_result(adapter, + roam_profile, &channel); + + if ((QDF_STATUS_SUCCESS != status) || (0 == channel) || + CDS_IS_DFS_CH(channel)) + channel = cds_get_nondfs_preferred_channel(CDS_SAP_MODE, + true); + + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter); + qdf_event_reset(&hostapd_state->qdf_event); + status = wlansap_set_channel_change_with_csa( + WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), channel, + hdd_ap_ctx->sapConfig.ch_width_orig); + + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Set channel with CSA IE failed, can't allow STA"); + return false; + } + + /* + * wait here for SAP to finish the channel switch. When channel + * switch happens, SAP sends few beacons with CSA_IE. After + * successfully Transmission of those beacons, it will move its + * state from started to disconnected and move to new channel. + * once it moves to new channel, sap again moves its state + * machine from disconnected to started and set this event. + * wait for 10 secs to finish this. + */ + status = qdf_wait_single_event(&hostapd_state->qdf_event, 10000); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("wait for qdf_event failed, STA not allowed!!"); + return false; + } + + return true; +} /* * FUNCTION: wlan_hdd_cfg80211_connect_start @@ -8130,9 +8231,20 @@ int wlan_hdd_cfg80211_connect_start(hdd_adapter_t *pAdapter, return 0; } + if (pHddCtx->config->policy_manager_enabled && + (false == wlan_hdd_handle_sap_sta_dfs_conc(pAdapter, + pRoamProfile))) { + hdd_err("sap-sta conc will fail, can't allow sta"); + hdd_conn_set_connection_state(pAdapter, + eConnectionState_NotConnected); + return -ENOMEM; + } + sme_config = qdf_mem_malloc(sizeof(*sme_config)); if (!sme_config) { hdd_err("unable to allocate sme_config"); + hdd_conn_set_connection_state(pAdapter, + eConnectionState_NotConnected); return -ENOMEM; } qdf_mem_zero(sme_config, sizeof(*sme_config)); diff --git a/core/hdd/src/wlan_hdd_scan.c b/core/hdd/src/wlan_hdd_scan.c index df4b23c1a4f9..d35f21bd0d0f 100644 --- a/core/hdd/src/wlan_hdd_scan.c +++ b/core/hdd/src/wlan_hdd_scan.c @@ -1316,7 +1316,7 @@ static int __wlan_hdd_cfg80211_scan(struct wiphy *wiphy, con_dfs_ch = con_sap_adapter->sessionCtx.ap.operatingChannel; - if (CDS_IS_DFS_CH(con_dfs_ch)) { + if (!wma_is_hw_dbs_capable() && CDS_IS_DFS_CH(con_dfs_ch)) { /* Provide empty scan result during DFS operation since * scanning not supported during DFS. Reason is * following case: