diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 868de9d4a14a..2c3e68646fe4 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -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,11 +376,36 @@ static void wcn36xx_stop(struct ieee80211_hw *hw) kfree(wcn->hal_buf); } +static void wcn36xx_change_ps(struct wcn36xx *wcn, bool enable) +{ + 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; - struct ieee80211_vif *vif = NULL; - struct wcn36xx_vif *tmp; wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed); @@ -392,23 +415,28 @@ 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 (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) { - 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 (changed & IEEE80211_CONF_CHANGE_PS) + wcn36xx_change_ps(wcn, hw->conf.flags & IEEE80211_CONF_PS); mutex_unlock(&wcn->conf_mutex); @@ -614,55 +642,26 @@ out: return ret; } -static void wcn36xx_hw_scan_worker(struct work_struct *work) -{ - 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; - 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; - } - 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); -} - static int wcn36xx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *hw_req) { struct wcn36xx *wcn = hw->priv; + int i; + + if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) { + /* fallback to mac80211 software scan */ + return 1; + } + + /* 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; + } + mutex_lock(&wcn->scan_lock); if (wcn->scan_req) { mutex_unlock(&wcn->scan_lock); @@ -674,12 +673,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 +689,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) { @@ -1151,6 +1163,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, @@ -1329,8 +1343,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"); diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 908cc6cf7b1a..5a565f119e5d 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -517,8 +517,10 @@ out: return ret; } -int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode) +int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode, + struct ieee80211_vif *vif) { + struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); struct wcn36xx_hal_init_scan_req_msg msg_body; int ret; @@ -526,6 +528,13 @@ int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode) INIT_HAL_MSG(msg_body, WCN36XX_HAL_INIT_SCAN_REQ); msg_body.mode = mode; + if (vif_priv->bss_index != WCN36XX_HAL_BSS_INVALID_IDX) { + /* Notify BSSID with null DATA packet */ + msg_body.frame_type = 2; + msg_body.notify = 1; + msg_body.scan_entry.bss_index[0] = vif_priv->bss_index; + msg_body.scan_entry.active_bss_count = 1; + } PREPARE_HAL_BUF(wcn->hal_buf, msg_body); @@ -607,8 +616,10 @@ out: } int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, - enum wcn36xx_hal_sys_mode mode) + enum wcn36xx_hal_sys_mode mode, + struct ieee80211_vif *vif) { + struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); struct wcn36xx_hal_finish_scan_req_msg msg_body; int ret; @@ -616,6 +627,14 @@ int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, INIT_HAL_MSG(msg_body, WCN36XX_HAL_FINISH_SCAN_REQ); msg_body.mode = mode; + msg_body.oper_channel = WCN36XX_HW_CHANNEL(wcn); + if (vif_priv->bss_index != WCN36XX_HAL_BSS_INVALID_IDX) { + /* Notify BSSID with null data packet */ + msg_body.notify = 1; + msg_body.frame_type = 2; + msg_body.scan_entry.bss_index[0] = vif_priv->bss_index; + msg_body.scan_entry.active_bss_count = 1; + } PREPARE_HAL_BUF(wcn->hal_buf, msg_body); diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h index 68c59df7a0ad..b1d8083d9d9d 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.h +++ b/drivers/net/wireless/ath/wcn36xx/smd.h @@ -59,11 +59,13 @@ void wcn36xx_smd_close(struct wcn36xx *wcn); int wcn36xx_smd_load_nv(struct wcn36xx *wcn); int wcn36xx_smd_start(struct wcn36xx *wcn); int wcn36xx_smd_stop(struct wcn36xx *wcn); -int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode); int wcn36xx_smd_start_scan(struct wcn36xx *wcn, u8 scan_channel); int wcn36xx_smd_end_scan(struct wcn36xx *wcn, u8 scan_channel); -int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, - enum wcn36xx_hal_sys_mode mode); +int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode, + struct ieee80211_vif *vif); +int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode, + struct ieee80211_vif *vif); + int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn, u8 *channels, size_t channel_count); int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif, struct cfg80211_scan_request *req); diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index f5872e7dfb51..820505619f66 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -144,15 +144,8 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) fc = __le16_to_cpu(hdr->frame_control); sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); - /* When scanning associate beacons to this */ - if (ieee80211_is_beacon(hdr->frame_control) && wcn->scan_freq) { - status.freq = wcn->scan_freq; - status.band = wcn->scan_band; - } else { - status.freq = WCN36XX_CENTER_FREQ(wcn); - status.band = WCN36XX_BAND(wcn); - } - + status.freq = WCN36XX_CENTER_FREQ(wcn); + status.band = WCN36XX_BAND(wcn); status.mactime = 10; status.signal = -get_rssi0(bd); status.antenna = 1; diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index 2d89849c630b..3221fed15620 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -223,10 +223,10 @@ struct wcn36xx { spinlock_t hal_ind_lock; struct list_head hal_ind_queue; - struct work_struct scan_work; struct cfg80211_scan_request *scan_req; - int scan_freq; - int scan_band; + bool sw_scan; + u8 sw_scan_opchannel; + struct ieee80211_vif *sw_scan_vif; struct mutex scan_lock; bool scan_aborted;