diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h index 153cd68d7356..f228c696d61d 100644 --- a/core/wma/inc/wma.h +++ b/core/wma/inc/wma.h @@ -2055,6 +2055,7 @@ QDF_STATUS wma_send_ht40_obss_scanind(tp_wma_handle wma, int wma_get_bpf_caps_event_handler(void *handle, u_int8_t *cmd_param_info, u_int32_t len); +uint32_t wma_get_num_of_setbits_from_bitmask(uint32_t mask); QDF_STATUS wma_get_bpf_capabilities(tp_wma_handle wma); QDF_STATUS wma_set_bpf_instructions(tp_wma_handle wma, struct sir_bpf_set_offload *bpf_set_offload); diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index da68b5df928c..9c745e266705 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -3518,6 +3518,301 @@ static inline void wma_update_target_vht_cap(tp_wma_handle wh, cfg->vht_max_ampdu_len_exp); } +/** + * wma_derive_ext_ht_cap() - Derive HT caps based on given value + * @wma_handle: pointer to wma_handle + * @ht_cap: given pointer to HT caps which needs to be updated + * @tx_chain: given tx chainmask value + * @rx_chain: given rx chainmask value + * @value: new HT cap info provided in form of bitmask + * + * This function takes the value provided in form of bitmask and decodes + * it. After decoding, what ever value it gets, it takes the union(max) or + * intersection(min) with previously derived values. + * + * Return: none + * + */ +static void wma_derive_ext_ht_cap(tp_wma_handle wma_handle, + struct wma_tgt_ht_cap *ht_cap, uint32_t value, + uint32_t tx_chain, uint32_t rx_chain) +{ + struct wma_tgt_ht_cap tmp = {0}; + + if (NULL == wma_handle || NULL == ht_cap) + return; + + if (0 == qdf_mem_cmp(ht_cap, &tmp, sizeof(struct wma_tgt_ht_cap))) { + ht_cap->ht_rx_stbc = (!!(value & WMI_HT_CAP_RX_STBC)); + ht_cap->ht_tx_stbc = (!!(value & WMI_HT_CAP_TX_STBC)); + ht_cap->mpdu_density = (!!(value & WMI_HT_CAP_MPDU_DENSITY)); + ht_cap->ht_rx_ldpc = (!!(value & WMI_HT_CAP_RX_LDPC)); + ht_cap->ht_sgi_20 = (!!(value & WMI_HT_CAP_HT20_SGI)); + ht_cap->ht_sgi_40 = (!!(value & WMI_HT_CAP_HT40_SGI)); + ht_cap->num_rf_chains = + QDF_MAX(wma_get_num_of_setbits_from_bitmask(tx_chain), + wma_get_num_of_setbits_from_bitmask(rx_chain)); + } else { + ht_cap->ht_rx_stbc = QDF_MIN(ht_cap->ht_rx_stbc, + (!!(value & WMI_HT_CAP_RX_STBC))); + ht_cap->ht_tx_stbc = QDF_MAX(ht_cap->ht_tx_stbc, + (!!(value & WMI_HT_CAP_TX_STBC))); + ht_cap->mpdu_density = QDF_MIN(ht_cap->mpdu_density, + (!!(value & WMI_HT_CAP_MPDU_DENSITY))); + ht_cap->ht_rx_ldpc = QDF_MIN(ht_cap->ht_rx_ldpc, + (!!(value & WMI_HT_CAP_RX_LDPC))); + ht_cap->ht_sgi_20 = QDF_MIN(ht_cap->ht_sgi_20, + (!!(value & WMI_HT_CAP_HT20_SGI))); + ht_cap->ht_sgi_40 = QDF_MIN(ht_cap->ht_sgi_40, + (!!(value & WMI_HT_CAP_HT40_SGI))); + ht_cap->num_rf_chains = + QDF_MAX(ht_cap->num_rf_chains, + QDF_MAX(wma_get_num_of_setbits_from_bitmask( + tx_chain), + wma_get_num_of_setbits_from_bitmask( + rx_chain))); + } +} + +/** + * wma_update_target_ext_ht_cap() - Update HT caps with given extended cap + * @wma_handle: pointer to wma_handle + * @ht_cap: HT cap structure to be filled + * + * This function loop through each hardware mode and for each hardware mode + * again it loop through each MAC/PHY and pull the caps 2G and 5G specific + * HT caps and derives the final cap. + * + * Return: none + * + */ +static void wma_update_target_ext_ht_cap(tp_wma_handle wma_handle, + struct wma_tgt_ht_cap *ht_cap) +{ + int i, j = 0, max_mac; + uint32_t ht_2g, ht_5g; + struct wma_tgt_ht_cap tmp_ht_cap = {0}, tmp_cap = {0}; + struct extended_caps *phy_caps; + WMI_MAC_PHY_CAPABILITIES *mac_cap; + + /* + * for legacy device extended cap might not even come, so in that case + * don't overwrite legacy values + */ + if (!wma_handle || + (0 == wma_handle->phy_caps.num_hw_modes.num_hw_modes)) { + WMA_LOGI("%s: No extended HT cap for current SOC", __func__); + return; + } + + phy_caps = &wma_handle->phy_caps; + for (i = 0; i < phy_caps->num_hw_modes.num_hw_modes; i++) { + if (phy_caps->each_hw_mode_cap[i].phy_id_map == PHY1_PHY2) + max_mac = j + 2; + else + max_mac = j + 1; + for ( ; j < max_mac; j++) { + mac_cap = &phy_caps->each_phy_cap_per_hwmode[j]; + ht_2g = mac_cap->ht_cap_info_2G; + ht_5g = mac_cap->ht_cap_info_5G; + if (ht_2g) + wma_derive_ext_ht_cap(wma_handle, &tmp_ht_cap, + ht_2g, mac_cap->tx_chain_mask_2G, + mac_cap->rx_chain_mask_2G); + if (ht_5g) + wma_derive_ext_ht_cap(wma_handle, &tmp_ht_cap, + ht_5g, mac_cap->tx_chain_mask_5G, + mac_cap->rx_chain_mask_5G); + } + } + + if (0 != qdf_mem_cmp(&tmp_cap, &tmp_ht_cap, + sizeof(struct wma_tgt_ht_cap))) { + qdf_mem_copy(ht_cap, &tmp_ht_cap, + sizeof(struct wma_tgt_ht_cap)); + } + + WMA_LOGI("%s: [ext ht cap] ht_rx_stbc - %d, ht_tx_stbc - %d\n\ + mpdu_density - %d ht_rx_ldpc - %d ht_sgi_20 - %d\n\ + ht_sgi_40 - %d num_rf_chains - %d ", __func__, + ht_cap->ht_rx_stbc, ht_cap->ht_tx_stbc, + ht_cap->mpdu_density, ht_cap->ht_rx_ldpc, + ht_cap->ht_sgi_20, ht_cap->ht_sgi_40, + ht_cap->num_rf_chains); +} + +/** + * wma_derive_ext_vht_cap() - Derive VHT caps based on given value + * @wma_handle: pointer to wma_handle + * @vht_cap: pointer to given VHT caps to be filled + * @value: new VHT cap info provided in form of bitmask + * + * This function takes the value provided in form of bitmask and decodes + * it. After decoding, what ever value it gets, it takes the union(max) or + * intersection(min) with previously derived values. + * + * Return: none + * + */ +static void wma_derive_ext_vht_cap(t_wma_handle *wma_handle, + struct wma_tgt_vht_cap *vht_cap, uint32_t value) +{ + struct wma_tgt_vht_cap tmp_cap = {0}; + uint32_t tmp = 0; + + if (NULL == wma_handle || NULL == vht_cap) + return; + + if (0 == qdf_mem_cmp(vht_cap, &tmp_cap, + sizeof(struct wma_tgt_vht_cap))) { + if (value & WMI_VHT_CAP_MAX_MPDU_LEN_11454) + vht_cap->vht_max_mpdu = WMI_VHT_CAP_MAX_MPDU_LEN_11454; + else if (value & WMI_VHT_CAP_MAX_MPDU_LEN_7935) + vht_cap->vht_max_mpdu = WMI_VHT_CAP_MAX_MPDU_LEN_7935; + else + vht_cap->vht_max_mpdu = 0; + + if (value & WMI_VHT_CAP_CH_WIDTH_80P80_160MHZ) { + vht_cap->supp_chan_width = + 1 << eHT_CHANNEL_WIDTH_80P80MHZ; + vht_cap->supp_chan_width |= + 1 << eHT_CHANNEL_WIDTH_160MHZ; + } else if (value & WMI_VHT_CAP_CH_WIDTH_160MHZ) { + vht_cap->supp_chan_width = + 1 << eHT_CHANNEL_WIDTH_160MHZ; + } else { + vht_cap->supp_chan_width = 1 << eHT_CHANNEL_WIDTH_80MHZ; + } + vht_cap->vht_rx_ldpc = value & WMI_VHT_CAP_RX_LDPC; + vht_cap->vht_short_gi_80 = value & WMI_VHT_CAP_SGI_80MHZ; + vht_cap->vht_short_gi_160 = value & WMI_VHT_CAP_SGI_160MHZ; + vht_cap->vht_tx_stbc = value & WMI_VHT_CAP_TX_STBC; + vht_cap->vht_rx_stbc = + (value & WMI_VHT_CAP_RX_STBC_1SS) | + (value & WMI_VHT_CAP_RX_STBC_2SS) | + (value & WMI_VHT_CAP_RX_STBC_3SS); + vht_cap->vht_max_ampdu_len_exp = + (value & WMI_VHT_CAP_MAX_AMPDU_LEN_EXP) >> + WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT; + vht_cap->vht_su_bformer = value & WMI_VHT_CAP_SU_BFORMER; + vht_cap->vht_su_bformee = value & WMI_VHT_CAP_SU_BFORMEE; + vht_cap->vht_mu_bformer = value & WMI_VHT_CAP_MU_BFORMER; + vht_cap->vht_mu_bformee = value & WMI_VHT_CAP_MU_BFORMEE; + vht_cap->vht_txop_ps = value & WMI_VHT_CAP_TXOP_PS; + } else { + if (value & WMI_VHT_CAP_MAX_MPDU_LEN_11454) + tmp = WMI_VHT_CAP_MAX_MPDU_LEN_11454; + else if (value & WMI_VHT_CAP_MAX_MPDU_LEN_7935) + tmp = WMI_VHT_CAP_MAX_MPDU_LEN_7935; + else + tmp = 0; + vht_cap->vht_max_mpdu = QDF_MIN(vht_cap->vht_max_mpdu, tmp); + + if ((value & WMI_VHT_CAP_CH_WIDTH_80P80_160MHZ)) { + tmp = (1 << eHT_CHANNEL_WIDTH_80P80MHZ) | + (1 << eHT_CHANNEL_WIDTH_160MHZ); + } else if (value & WMI_VHT_CAP_CH_WIDTH_160MHZ) { + tmp = 1 << eHT_CHANNEL_WIDTH_160MHZ; + } else { + tmp = 1 << eHT_CHANNEL_WIDTH_80MHZ; + } + vht_cap->supp_chan_width = + QDF_MAX(vht_cap->supp_chan_width, tmp); + vht_cap->vht_rx_ldpc = QDF_MIN(vht_cap->vht_rx_ldpc, + value & WMI_VHT_CAP_RX_LDPC); + vht_cap->vht_short_gi_80 = QDF_MAX(vht_cap->vht_short_gi_80, + value & WMI_VHT_CAP_SGI_80MHZ); + vht_cap->vht_short_gi_160 = QDF_MAX(vht_cap->vht_short_gi_160, + value & WMI_VHT_CAP_SGI_160MHZ); + vht_cap->vht_tx_stbc = QDF_MAX(vht_cap->vht_tx_stbc, + value & WMI_VHT_CAP_TX_STBC); + vht_cap->vht_rx_stbc = QDF_MIN(vht_cap->vht_rx_stbc, + (value & WMI_VHT_CAP_RX_STBC_1SS) | + (value & WMI_VHT_CAP_RX_STBC_2SS) | + (value & WMI_VHT_CAP_RX_STBC_3SS)); + vht_cap->vht_max_ampdu_len_exp = + QDF_MIN(vht_cap->vht_max_ampdu_len_exp, + (value & WMI_VHT_CAP_MAX_AMPDU_LEN_EXP) >> + WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT); + vht_cap->vht_su_bformer = QDF_MAX(vht_cap->vht_su_bformer, + value & WMI_VHT_CAP_SU_BFORMER); + vht_cap->vht_su_bformee = QDF_MAX(vht_cap->vht_su_bformee, + value & WMI_VHT_CAP_SU_BFORMEE); + vht_cap->vht_mu_bformer = QDF_MAX(vht_cap->vht_mu_bformer, + value & WMI_VHT_CAP_MU_BFORMER); + vht_cap->vht_mu_bformee = QDF_MAX(vht_cap->vht_mu_bformee, + value & WMI_VHT_CAP_MU_BFORMEE); + vht_cap->vht_txop_ps = QDF_MIN(vht_cap->vht_txop_ps, + value & WMI_VHT_CAP_TXOP_PS); + } +} + +/** + * wma_update_target_ext_vht_cap() - Update VHT caps with given extended cap + * @wma_handle: pointer to wma_handle + * @vht_cap: VHT cap structure to be filled + * + * This function loop through each hardware mode and for each hardware mode + * again it loop through each MAC/PHY and pull the caps 2G and 5G specific + * VHT caps and derives the final cap. + * + * Return: none + * + */ +static void wma_update_target_ext_vht_cap(t_wma_handle *wma_handle, + struct wma_tgt_vht_cap *vht_cap) +{ + int i, j = 0, max_mac; + uint32_t vht_cap_info_2g, vht_cap_info_5g; + struct wma_tgt_vht_cap tmp_vht_cap = {0}, tmp_cap = {0}; + struct extended_caps *phy_caps; + WMI_MAC_PHY_CAPABILITIES *mac_cap; + + /* + * for legacy device extended cap might not even come, so in that case + * don't overwrite legacy values + */ + if (!wma_handle || + (0 == wma_handle->phy_caps.num_hw_modes.num_hw_modes)) { + WMA_LOGI("%s: No extended VHT cap for current SOC", __func__); + return; + } + + phy_caps = &wma_handle->phy_caps; + for (i = 0; i < phy_caps->num_hw_modes.num_hw_modes; i++) { + if (phy_caps->each_hw_mode_cap[i].phy_id_map == PHY1_PHY2) + max_mac = j + 2; + else + max_mac = j + 1; + for ( ; j < max_mac; j++) { + mac_cap = &phy_caps->each_phy_cap_per_hwmode[j]; + vht_cap_info_2g = mac_cap->vht_cap_info_2G; + vht_cap_info_5g = mac_cap->vht_cap_info_5G; + if (vht_cap_info_2g) + wma_derive_ext_vht_cap(wma_handle, &tmp_vht_cap, + vht_cap_info_2g); + if (vht_cap_info_5g) + wma_derive_ext_vht_cap(wma_handle, &tmp_vht_cap, + vht_cap_info_5g); + } + } + + if (0 != qdf_mem_cmp(&tmp_cap, &tmp_vht_cap, + sizeof(struct wma_tgt_vht_cap))) { + qdf_mem_copy(vht_cap, &tmp_vht_cap, + sizeof(struct wma_tgt_vht_cap)); + } + + WMA_LOGI("%s: [ext vhtcap] max_mpdu %d supp_chan_width %x rx_ldpc %x\n \ + short_gi_80 %x tx_stbc %x rx_stbc %x txop_ps %x\n \ + su_bformee %x mu_bformee %x max_ampdu_len_exp %d", __func__, + vht_cap->vht_max_mpdu, vht_cap->supp_chan_width, + vht_cap->vht_rx_ldpc, vht_cap->vht_short_gi_80, + vht_cap->vht_tx_stbc, vht_cap->vht_rx_stbc, + vht_cap->vht_txop_ps, vht_cap->vht_su_bformee, + vht_cap->vht_mu_bformee, vht_cap->vht_max_ampdu_len_exp); +} + /** * wma_update_hdd_cfg() - update HDD config * @wma_handle: wma handle @@ -3557,6 +3852,12 @@ static void wma_update_hdd_cfg(tp_wma_handle wma_handle) wma_update_target_services(wma_handle, &tgt_cfg.services); wma_update_target_ht_cap(wma_handle, &tgt_cfg.ht_cap); wma_update_target_vht_cap(wma_handle, &tgt_cfg.vht_cap); + /* + * This will overwrite the structure filled by wma_update_target_ht_cap + * and wma_update_target_vht_cap APIs. + */ + wma_update_target_ext_ht_cap(wma_handle, &tgt_cfg.ht_cap); + wma_update_target_ext_vht_cap(wma_handle, &tgt_cfg.vht_cap); tgt_cfg.target_fw_version = wma_handle->target_fw_version; #ifdef WLAN_FEATURE_LPSS diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c index d5cb5c1219d3..dc6475e1098d 100644 --- a/core/wma/src/wma_utils.c +++ b/core/wma/src/wma_utils.c @@ -3382,6 +3382,25 @@ uint32_t wma_get_vht_ch_width(void) return fw_ch_wd; } +/** + * wma_get_num_of_setbits_from_bitmask() - to get num of setbits from bitmask + * @mask: given bitmask + * + * This helper function should return number of setbits from bitmask + * + * Return: number of setbits from bitmask + */ +uint32_t wma_get_num_of_setbits_from_bitmask(uint32_t mask) +{ + uint32_t num_of_setbits = 0; + + while (mask) { + mask &= (mask - 1); + num_of_setbits++; + } + return num_of_setbits; +} + /** * wma_config_debug_module_cmd - set debug log config * @wmi_handle: wmi layer handle