diff options
Diffstat (limited to 'drivers/net/wireless/ath/wcn36xx/main.c')
-rw-r--r-- | drivers/net/wireless/ath/wcn36xx/main.c | 288 |
1 files changed, 182 insertions, 106 deletions
diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 702b689c06df..706728fba72d 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -39,10 +39,10 @@ MODULE_PARM_DESC(debug_mask, "Debugging mask"); .max_power = 25, \ } -#define CHAN5G(_freq, _idx) { \ +#define CHAN5G(_freq, _idx, _phy_val) { \ .band = NL80211_BAND_5GHZ, \ .center_freq = (_freq), \ - .hw_value = (_idx), \ + .hw_value = (_phy_val) << HW_VALUE_PHY_SHIFT | HW_VALUE_CHANNEL(_idx), \ .max_power = 25, \ } @@ -67,29 +67,29 @@ static struct ieee80211_channel wcn_2ghz_channels[] = { }; static struct ieee80211_channel wcn_5ghz_channels[] = { - CHAN5G(5180, 36), - CHAN5G(5200, 40), - CHAN5G(5220, 44), - CHAN5G(5240, 48), - CHAN5G(5260, 52), - CHAN5G(5280, 56), - CHAN5G(5300, 60), - CHAN5G(5320, 64), - CHAN5G(5500, 100), - CHAN5G(5520, 104), - CHAN5G(5540, 108), - CHAN5G(5560, 112), - CHAN5G(5580, 116), - CHAN5G(5600, 120), - CHAN5G(5620, 124), - CHAN5G(5640, 128), - CHAN5G(5660, 132), - CHAN5G(5700, 140), - CHAN5G(5745, 149), - CHAN5G(5765, 153), - CHAN5G(5785, 157), - CHAN5G(5805, 161), - CHAN5G(5825, 165) + CHAN5G(5180, 36, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), + CHAN5G(5200, 40, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), + CHAN5G(5220, 44, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), + CHAN5G(5240, 48, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), + CHAN5G(5260, 52, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), + CHAN5G(5280, 56, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), + CHAN5G(5300, 60, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), + CHAN5G(5320, 64, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), + CHAN5G(5500, 100, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), + CHAN5G(5520, 104, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), + CHAN5G(5540, 108, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), + CHAN5G(5560, 112, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), + CHAN5G(5580, 116, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), + CHAN5G(5600, 120, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), + CHAN5G(5620, 124, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), + CHAN5G(5640, 128, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), + CHAN5G(5660, 132, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), + CHAN5G(5700, 140, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), + CHAN5G(5745, 149, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), + CHAN5G(5765, 153, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), + CHAN5G(5785, 157, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), + CHAN5G(5805, 161, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), + CHAN5G(5825, 165, 0) }; #define RATE(_bitrate, _hw_rate, _flags) { \ @@ -163,7 +163,7 @@ static struct ieee80211_supported_band wcn_band_5ghz = { .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .mcs = { .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, - .rx_highest = cpu_to_le16(72), + .rx_highest = cpu_to_le16(150), .tx_params = IEEE80211_HT_MCS_TX_DEFINED, } } @@ -354,8 +354,6 @@ static void wcn36xx_stop(struct ieee80211_hw *hw) wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n"); - cancel_work_sync(&wcn->scan_work); - mutex_lock(&wcn->scan_lock); if (wcn->scan_req) { struct cfg80211_scan_info scan_info = { @@ -378,12 +376,37 @@ static void wcn36xx_stop(struct ieee80211_hw *hw) kfree(wcn->hal_buf); } -static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) +static void wcn36xx_change_ps(struct wcn36xx *wcn, bool enable) { - struct wcn36xx *wcn = hw->priv; struct ieee80211_vif *vif = NULL; struct wcn36xx_vif *tmp; + list_for_each_entry(tmp, &wcn->vif_list, list) { + vif = wcn36xx_priv_to_vif(tmp); + if (enable && !wcn->sw_scan) { + if (vif->bss_conf.ps) /* ps allowed ? */ + wcn36xx_pmc_enter_bmps_state(wcn, vif); + } else { + wcn36xx_pmc_exit_bmps_state(wcn, vif); + } + } +} + +static void wcn36xx_change_opchannel(struct wcn36xx *wcn, int ch) +{ + struct ieee80211_vif *vif = NULL; + struct wcn36xx_vif *tmp; + + list_for_each_entry(tmp, &wcn->vif_list, list) { + vif = wcn36xx_priv_to_vif(tmp); + wcn36xx_smd_switch_channel(wcn, vif, ch); + } +} + +static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) +{ + struct wcn36xx *wcn = hw->priv; + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed); mutex_lock(&wcn->conf_mutex); @@ -392,24 +415,29 @@ static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) int ch = WCN36XX_HW_CHANNEL(wcn); wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n", ch); - list_for_each_entry(tmp, &wcn->vif_list, list) { - vif = wcn36xx_priv_to_vif(tmp); - wcn36xx_smd_switch_channel(wcn, vif, ch); - } - } - if (changed & IEEE80211_CONF_CHANGE_PS) { - list_for_each_entry(tmp, &wcn->vif_list, list) { - vif = wcn36xx_priv_to_vif(tmp); - if (hw->conf.flags & IEEE80211_CONF_PS) { - if (vif->bss_conf.ps) /* ps allowed ? */ - wcn36xx_pmc_enter_bmps_state(wcn, vif); - } else { - wcn36xx_pmc_exit_bmps_state(wcn, vif); - } + if (wcn->sw_scan_opchannel == ch) { + /* If channel is the initial operating channel, we may + * want to receive/transmit regular data packets, then + * simply stop the scan session and exit PS mode. + */ + wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN, + wcn->sw_scan_vif); + } else if (wcn->sw_scan) { + /* A scan is ongoing, do not change the operating + * channel, but start a scan session on the channel. + */ + wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN, + wcn->sw_scan_vif); + wcn36xx_smd_start_scan(wcn, ch); + } else { + wcn36xx_change_opchannel(wcn, ch); } } + if (changed & IEEE80211_CONF_CHANGE_PS) + wcn36xx_change_ps(wcn, hw->conf.flags & IEEE80211_CONF_PS); + mutex_unlock(&wcn->conf_mutex); return 0; @@ -582,6 +610,15 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } } } + /* FIXME: Only enable bmps support when encryption is enabled. + * For any reasons, when connected to open/no-security BSS, + * the wcn36xx controller in bmps mode does not forward + * 'wake-up' beacons despite AP sends DTIM with station AID. + * It could be due to a firmware issue or to the way driver + * configure the station. + */ + if (vif->type == NL80211_IFTYPE_STATION) + vif_priv->allow_bmps = true; break; case DISABLE_KEY: if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) { @@ -614,55 +651,26 @@ out: return ret; } -static void wcn36xx_hw_scan_worker(struct work_struct *work) +static int wcn36xx_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) { - struct wcn36xx *wcn = container_of(work, struct wcn36xx, scan_work); - struct cfg80211_scan_request *req = wcn->scan_req; - u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX]; - struct cfg80211_scan_info scan_info = {}; - bool aborted = false; + struct wcn36xx *wcn = hw->priv; int i; - wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 scan %d channels worker\n", req->n_channels); - - for (i = 0; i < req->n_channels; i++) - channels[i] = req->channels[i]->hw_value; - - wcn36xx_smd_update_scan_params(wcn, channels, req->n_channels); - - wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN); - for (i = 0; i < req->n_channels; i++) { - mutex_lock(&wcn->scan_lock); - aborted = wcn->scan_aborted; - mutex_unlock(&wcn->scan_lock); - - if (aborted) - break; - - wcn->scan_freq = req->channels[i]->center_freq; - wcn->scan_band = req->channels[i]->band; - - wcn36xx_smd_start_scan(wcn, req->channels[i]->hw_value); - msleep(30); - wcn36xx_smd_end_scan(wcn, req->channels[i]->hw_value); - - wcn->scan_freq = 0; + if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) { + /* fallback to mac80211 software scan */ + return 1; } - wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); - - scan_info.aborted = aborted; - ieee80211_scan_completed(wcn->hw, &scan_info); - mutex_lock(&wcn->scan_lock); - wcn->scan_req = NULL; - mutex_unlock(&wcn->scan_lock); -} + /* For unknown reason, the hardware offloaded scan only works with + * 2.4Ghz channels, fallback to software scan in other cases. + */ + for (i = 0; i < hw_req->req.n_channels; i++) { + if (hw_req->req.channels[i]->band != NL80211_BAND_2GHZ) + return 1; + } -static int wcn36xx_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct wcn36xx *wcn = hw->priv; mutex_lock(&wcn->scan_lock); if (wcn->scan_req) { mutex_unlock(&wcn->scan_lock); @@ -674,12 +682,6 @@ static int wcn36xx_hw_scan(struct ieee80211_hw *hw, mutex_unlock(&wcn->scan_lock); - if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) { - /* legacy manual/sw scan */ - schedule_work(&wcn->scan_work); - return 0; - } - return wcn36xx_smd_start_hw_scan(wcn, vif, &hw_req->req); } @@ -696,16 +698,35 @@ static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw, /* ieee80211_scan_completed will be called on FW scan * indication */ wcn36xx_smd_stop_hw_scan(wcn); - } else { - struct cfg80211_scan_info scan_info = { - .aborted = true, - }; - - cancel_work_sync(&wcn->scan_work); - ieee80211_scan_completed(wcn->hw, &scan_info); } } +static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); + + wcn->sw_scan = true; + wcn->sw_scan_vif = vif; + if (vif_priv->sta_assoc) + wcn->sw_scan_opchannel = WCN36XX_HW_CHANNEL(wcn); + else + wcn->sw_scan_opchannel = 0; +} + +static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct wcn36xx *wcn = hw->priv; + + /* ensure that any scan session is finished */ + wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN, wcn->sw_scan_vif); + wcn->sw_scan = false; + wcn->sw_scan_opchannel = 0; +} + static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, enum nl80211_band band) { @@ -745,7 +766,16 @@ static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, sta->ht_cap.mcs.rx_mask, sizeof(sta->ht_cap.mcs.rx_mask)); } + + if (sta->vht_cap.vht_supported) { + sta_priv->supported_rates.op_rate_mode = STA_11ac; + sta_priv->supported_rates.vht_rx_mcs_map = + sta->vht_cap.vht_mcs.rx_mcs_map; + sta_priv->supported_rates.vht_tx_mcs_map = + sta->vht_cap.vht_mcs.tx_mcs_map; + } } + void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates) { u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = { @@ -772,6 +802,14 @@ void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates) sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES); rates->supported_mcs_set[0] = 0xFF; } + +void wcn36xx_set_default_rates_v1(struct wcn36xx_hal_supported_rates_v1 *rates) +{ + rates->op_rate_mode = STA_11ac; + rates->vht_rx_mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9; + rates->vht_tx_mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9; +} + static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -879,6 +917,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, vif->addr, bss_conf->aid); vif_priv->sta_assoc = false; + vif_priv->allow_bmps = false; wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, vif->addr, @@ -1083,6 +1122,7 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, u16 tid = params->tid; u16 *ssn = ¶ms->ssn; int ret = 0; + u8 session; wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", action, tid); @@ -1092,10 +1132,11 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, switch (action) { case IEEE80211_AMPDU_RX_START: sta_priv->tid = tid; - wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0, - get_sta_index(vif, sta_priv)); - wcn36xx_smd_add_ba(wcn); - wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv)); + session = wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0, + get_sta_index(vif, sta_priv)); + wcn36xx_smd_add_ba(wcn, session); + wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv), tid, + session); break; case IEEE80211_AMPDU_RX_STOP: wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv)); @@ -1149,6 +1190,8 @@ static const struct ieee80211_ops wcn36xx_ops = { .set_key = wcn36xx_set_key, .hw_scan = wcn36xx_hw_scan, .cancel_hw_scan = wcn36xx_cancel_hw_scan, + .sw_scan_start = wcn36xx_sw_scan_start, + .sw_scan_complete = wcn36xx_sw_scan_complete, .bss_info_changed = wcn36xx_bss_info_changed, .set_rts_threshold = wcn36xx_set_rts_threshold, .sta_add = wcn36xx_sta_add, @@ -1158,6 +1201,35 @@ static const struct ieee80211_ops wcn36xx_ops = { CFG80211_TESTMODE_CMD(wcn36xx_tm_cmd) }; +static void +wcn36xx_set_ieee80211_vht_caps(struct ieee80211_sta_vht_cap *vht_cap) +{ + vht_cap->vht_supported = true; + + vht_cap->cap = (IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 | + IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | + 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | + 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT); + + vht_cap->vht_mcs.rx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); + + vht_cap->vht_mcs.rx_highest = cpu_to_le16(433); + vht_cap->vht_mcs.tx_highest = vht_cap->vht_mcs.rx_highest; + + vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; +} + static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) { static const u32 cipher_suites[] = { @@ -1173,6 +1245,7 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) ieee80211_hw_set(wcn->hw, SIGNAL_DBM); ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL); ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS); + ieee80211_hw_set(wcn->hw, REPORTS_TX_ACK_STATUS); wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | @@ -1183,6 +1256,9 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) if (wcn->rf_id != RF_IRIS_WCN3620) wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz; + if (wcn->rf_id == RF_IRIS_WCN3680) + wcn36xx_set_ieee80211_vht_caps(&wcn_band_5ghz.vht_cap); + wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS; wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN; @@ -1280,6 +1356,8 @@ static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, if (iris_node) { if (of_device_is_compatible(iris_node, "qcom,wcn3620")) wcn->rf_id = RF_IRIS_WCN3620; + if (of_device_is_compatible(iris_node, "qcom,wcn3680")) + wcn->rf_id = RF_IRIS_WCN3680; of_node_put(iris_node); } @@ -1326,8 +1404,6 @@ static int wcn36xx_probe(struct platform_device *pdev) goto out_wq; } - INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker); - wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw); if (IS_ERR(wcn->smd_channel)) { wcn36xx_err("failed to open WLAN_CTRL channel\n"); |