diff options
Diffstat (limited to 'drivers/net/wireless/ti/wlcore/main.c')
| -rw-r--r-- | drivers/net/wireless/ti/wlcore/main.c | 405 | 
1 files changed, 349 insertions, 56 deletions
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 6ad3fcedab9b..1e136993580f 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -79,22 +79,12 @@ static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif)  static void wl1271_reg_notify(struct wiphy *wiphy,  			      struct regulatory_request *request)  { -	struct ieee80211_supported_band *band; -	struct ieee80211_channel *ch; -	int i;  	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);  	struct wl1271 *wl = hw->priv; -	band = wiphy->bands[IEEE80211_BAND_5GHZ]; -	for (i = 0; i < band->n_channels; i++) { -		ch = &band->channels[i]; -		if (ch->flags & IEEE80211_CHAN_DISABLED) -			continue; - -		if (ch->flags & IEEE80211_CHAN_RADAR) -			ch->flags |= IEEE80211_CHAN_NO_IR; - -	} +	/* copy the current dfs region */ +	if (request) +		wl->dfs_region = request->dfs_region;  	wlcore_regdomain_config(wl);  } @@ -226,6 +216,29 @@ void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl)  		msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout));  } +static void wlcore_rc_update_work(struct work_struct *work) +{ +	int ret; +	struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif, +						rc_update_work); +	struct wl1271 *wl = wlvif->wl; + +	mutex_lock(&wl->mutex); + +	if (unlikely(wl->state != WLCORE_STATE_ON)) +		goto out; + +	ret = wl1271_ps_elp_wakeup(wl); +	if (ret < 0) +		goto out; + +	wlcore_hw_sta_rc_update(wl, wlvif); + +	wl1271_ps_elp_sleep(wl); +out: +	mutex_unlock(&wl->mutex); +} +  static void wl12xx_tx_watchdog_work(struct work_struct *work)  {  	struct delayed_work *dwork; @@ -1662,19 +1675,15 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,  	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))  		goto out; -	ret = wl1271_ps_elp_wakeup(wl); -	if (ret < 0) -		goto out; -  	ret = wl1271_configure_wowlan(wl, wow);  	if (ret < 0) -		goto out_sleep; +		goto out;  	if ((wl->conf.conn.suspend_wake_up_event ==  	     wl->conf.conn.wake_up_event) &&  	    (wl->conf.conn.suspend_listen_interval ==  	     wl->conf.conn.listen_interval)) -		goto out_sleep; +		goto out;  	ret = wl1271_acx_wake_up_conditions(wl, wlvif,  				    wl->conf.conn.suspend_wake_up_event, @@ -1682,29 +1691,28 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,  	if (ret < 0)  		wl1271_error("suspend: set wake up conditions failed: %d", ret); - -out_sleep: -	wl1271_ps_elp_sleep(wl);  out:  	return ret;  }  static int wl1271_configure_suspend_ap(struct wl1271 *wl, -				       struct wl12xx_vif *wlvif) +					struct wl12xx_vif *wlvif, +					struct cfg80211_wowlan *wow)  {  	int ret = 0;  	if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))  		goto out; -	ret = wl1271_ps_elp_wakeup(wl); +	ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);  	if (ret < 0)  		goto out; -	ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); +	ret = wl1271_configure_wowlan(wl, wow); +	if (ret < 0) +		goto out; -	wl1271_ps_elp_sleep(wl);  out:  	return ret; @@ -1717,7 +1725,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl,  	if (wlvif->bss_type == BSS_TYPE_STA_BSS)  		return wl1271_configure_suspend_sta(wl, wlvif, wow);  	if (wlvif->bss_type == BSS_TYPE_AP_BSS) -		return wl1271_configure_suspend_ap(wl, wlvif); +		return wl1271_configure_suspend_ap(wl, wlvif, wow);  	return 0;  } @@ -1730,21 +1738,18 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif)  	if ((!is_ap) && (!is_sta))  		return; -	if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) +	if ((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) || +	    (is_ap && !test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)))  		return; -	ret = wl1271_ps_elp_wakeup(wl); -	if (ret < 0) -		return; +	wl1271_configure_wowlan(wl, NULL);  	if (is_sta) { -		wl1271_configure_wowlan(wl, NULL); -  		if ((wl->conf.conn.suspend_wake_up_event ==  		     wl->conf.conn.wake_up_event) &&  		    (wl->conf.conn.suspend_listen_interval ==  		     wl->conf.conn.listen_interval)) -			goto out_sleep; +			return;  		ret = wl1271_acx_wake_up_conditions(wl, wlvif,  				    wl->conf.conn.wake_up_event, @@ -1757,9 +1762,6 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif)  	} else if (is_ap) {  		ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);  	} - -out_sleep: -	wl1271_ps_elp_sleep(wl);  }  static int wl1271_op_suspend(struct ieee80211_hw *hw, @@ -1781,6 +1783,13 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,  	wl1271_tx_flush(wl);  	mutex_lock(&wl->mutex); + +	ret = wl1271_ps_elp_wakeup(wl); +	if (ret < 0) { +		mutex_unlock(&wl->mutex); +		return ret; +	} +  	wl->wow_enabled = true;  	wl12xx_for_each_wlvif(wl, wlvif) {  		ret = wl1271_configure_suspend(wl, wlvif, wow); @@ -1790,7 +1799,27 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,  			return ret;  		}  	} + +	/* disable fast link flow control notifications from FW */ +	ret = wlcore_hw_interrupt_notify(wl, false); +	if (ret < 0) +		goto out_sleep; + +	/* if filtering is enabled, configure the FW to drop all RX BA frames */ +	ret = wlcore_hw_rx_ba_filter(wl, +				     !!wl->conf.conn.suspend_rx_ba_activity); +	if (ret < 0) +		goto out_sleep; + +out_sleep: +	wl1271_ps_elp_sleep(wl);  	mutex_unlock(&wl->mutex); + +	if (ret < 0) { +		wl1271_warning("couldn't prepare device to suspend"); +		return ret; +	} +  	/* flush any remaining work */  	wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); @@ -1864,13 +1893,29 @@ static int wl1271_op_resume(struct ieee80211_hw *hw)  	if (pending_recovery) {  		wl1271_warning("queuing forgotten recovery on resume");  		ieee80211_queue_work(wl->hw, &wl->recovery_work); -		goto out; +		goto out_sleep;  	} +	ret = wl1271_ps_elp_wakeup(wl); +	if (ret < 0) +		goto out; +  	wl12xx_for_each_wlvif(wl, wlvif) {  		wl1271_configure_resume(wl, wlvif);  	} +	ret = wlcore_hw_interrupt_notify(wl, true); +	if (ret < 0) +		goto out_sleep; + +	/* if filtering is enabled, configure the FW to drop all RX BA frames */ +	ret = wlcore_hw_rx_ba_filter(wl, false); +	if (ret < 0) +		goto out_sleep; + +out_sleep: +	wl1271_ps_elp_sleep(wl); +  out:  	wl->wow_enabled = false; @@ -2279,6 +2324,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)  		  wl1271_rx_streaming_enable_work);  	INIT_WORK(&wlvif->rx_streaming_disable_work,  		  wl1271_rx_streaming_disable_work); +	INIT_WORK(&wlvif->rc_update_work, wlcore_rc_update_work);  	INIT_DELAYED_WORK(&wlvif->channel_switch_work,  			  wlcore_channel_switch_work);  	INIT_DELAYED_WORK(&wlvif->connection_loss_work, @@ -2508,6 +2554,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,  	}  	vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | +			     IEEE80211_VIF_SUPPORTS_UAPSD |  			     IEEE80211_VIF_SUPPORTS_CQM_RSSI;  	wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", @@ -2723,6 +2770,7 @@ unlock:  	del_timer_sync(&wlvif->rx_streaming_timer);  	cancel_work_sync(&wlvif->rx_streaming_enable_work);  	cancel_work_sync(&wlvif->rx_streaming_disable_work); +	cancel_work_sync(&wlvif->rc_update_work);  	cancel_delayed_work_sync(&wlvif->connection_loss_work);  	cancel_delayed_work_sync(&wlvif->channel_switch_work);  	cancel_delayed_work_sync(&wlvif->pending_auth_complete_work); @@ -4072,8 +4120,14 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,  		ret = wlcore_set_beacon_template(wl, vif, is_ap);  		if (ret < 0)  			goto out; -	} +		if (test_and_clear_bit(WLVIF_FLAG_BEACON_DISABLED, +				       &wlvif->flags)) { +			ret = wlcore_hw_dfs_master_restart(wl, wlvif); +			if (ret < 0) +				goto out; +		} +	}  out:  	if (ret != 0)  		wl1271_error("beacon info change failed: %d", ret); @@ -4574,10 +4628,46 @@ static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,  				     struct ieee80211_chanctx_conf *ctx,  				     u32 changed)  { +	struct wl1271 *wl = hw->priv; +	struct wl12xx_vif *wlvif; +	int ret; +	int channel = ieee80211_frequency_to_channel( +		ctx->def.chan->center_freq); +  	wl1271_debug(DEBUG_MAC80211,  		     "mac80211 change chanctx %d (type %d) changed 0x%x", -		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq), -		     cfg80211_get_chandef_type(&ctx->def), changed); +		     channel, cfg80211_get_chandef_type(&ctx->def), changed); + +	mutex_lock(&wl->mutex); + +	ret = wl1271_ps_elp_wakeup(wl); +	if (ret < 0) +		goto out; + +	wl12xx_for_each_wlvif(wl, wlvif) { +		struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + +		rcu_read_lock(); +		if (rcu_access_pointer(vif->chanctx_conf) != ctx) { +			rcu_read_unlock(); +			continue; +		} +		rcu_read_unlock(); + +		/* start radar if needed */ +		if (changed & IEEE80211_CHANCTX_CHANGE_RADAR && +		    wlvif->bss_type == BSS_TYPE_AP_BSS && +		    ctx->radar_enabled && !wlvif->radar_enabled && +		    ctx->def.chan->dfs_state == NL80211_DFS_USABLE) { +			wl1271_debug(DEBUG_MAC80211, "Start radar detection"); +			wlcore_hw_set_cac(wl, wlvif, true); +			wlvif->radar_enabled = true; +		} +	} + +	wl1271_ps_elp_sleep(wl); +out: +	mutex_unlock(&wl->mutex);  }  static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, @@ -4588,13 +4678,26 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,  	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);  	int channel = ieee80211_frequency_to_channel(  		ctx->def.chan->center_freq); +	int ret = -EINVAL;  	wl1271_debug(DEBUG_MAC80211, -		     "mac80211 assign chanctx (role %d) %d (type %d)", -		     wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def)); +		     "mac80211 assign chanctx (role %d) %d (type %d) (radar %d dfs_state %d)", +		     wlvif->role_id, channel, +		     cfg80211_get_chandef_type(&ctx->def), +		     ctx->radar_enabled, ctx->def.chan->dfs_state);  	mutex_lock(&wl->mutex); +	if (unlikely(wl->state != WLCORE_STATE_ON)) +		goto out; + +	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))) +		goto out; + +	ret = wl1271_ps_elp_wakeup(wl); +	if (ret < 0) +		goto out; +  	wlvif->band = ctx->def.chan->band;  	wlvif->channel = channel;  	wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def); @@ -4602,6 +4705,15 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,  	/* update default rates according to the band */  	wl1271_set_band_rate(wl, wlvif); +	if (ctx->radar_enabled && +	    ctx->def.chan->dfs_state == NL80211_DFS_USABLE) { +		wl1271_debug(DEBUG_MAC80211, "Start radar detection"); +		wlcore_hw_set_cac(wl, wlvif, true); +		wlvif->radar_enabled = true; +	} + +	wl1271_ps_elp_sleep(wl); +out:  	mutex_unlock(&wl->mutex);  	return 0; @@ -4613,6 +4725,7 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,  {  	struct wl1271 *wl = hw->priv;  	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); +	int ret;  	wl1271_debug(DEBUG_MAC80211,  		     "mac80211 unassign chanctx (role %d) %d (type %d)", @@ -4621,6 +4734,99 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,  		     cfg80211_get_chandef_type(&ctx->def));  	wl1271_tx_flush(wl); + +	mutex_lock(&wl->mutex); + +	if (unlikely(wl->state != WLCORE_STATE_ON)) +		goto out; + +	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))) +		goto out; + +	ret = wl1271_ps_elp_wakeup(wl); +	if (ret < 0) +		goto out; + +	if (wlvif->radar_enabled) { +		wl1271_debug(DEBUG_MAC80211, "Stop radar detection"); +		wlcore_hw_set_cac(wl, wlvif, false); +		wlvif->radar_enabled = false; +	} + +	wl1271_ps_elp_sleep(wl); +out: +	mutex_unlock(&wl->mutex); +} + +static int __wlcore_switch_vif_chan(struct wl1271 *wl, +				    struct wl12xx_vif *wlvif, +				    struct ieee80211_chanctx_conf *new_ctx) +{ +	int channel = ieee80211_frequency_to_channel( +		new_ctx->def.chan->center_freq); + +	wl1271_debug(DEBUG_MAC80211, +		     "switch vif (role %d) %d -> %d chan_type: %d", +		     wlvif->role_id, wlvif->channel, channel, +		     cfg80211_get_chandef_type(&new_ctx->def)); + +	if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS)) +		return 0; + +	WARN_ON(!test_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags)); + +	if (wlvif->radar_enabled) { +		wl1271_debug(DEBUG_MAC80211, "Stop radar detection"); +		wlcore_hw_set_cac(wl, wlvif, false); +		wlvif->radar_enabled = false; +	} + +	wlvif->band = new_ctx->def.chan->band; +	wlvif->channel = channel; +	wlvif->channel_type = cfg80211_get_chandef_type(&new_ctx->def); + +	/* start radar if needed */ +	if (new_ctx->radar_enabled) { +		wl1271_debug(DEBUG_MAC80211, "Start radar detection"); +		wlcore_hw_set_cac(wl, wlvif, true); +		wlvif->radar_enabled = true; +	} + +	return 0; +} + +static int +wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw, +			     struct ieee80211_vif_chanctx_switch *vifs, +			     int n_vifs, +			     enum ieee80211_chanctx_switch_mode mode) +{ +	struct wl1271 *wl = hw->priv; +	int i, ret; + +	wl1271_debug(DEBUG_MAC80211, +		     "mac80211 switch chanctx n_vifs %d mode %d", +		     n_vifs, mode); + +	mutex_lock(&wl->mutex); + +	ret = wl1271_ps_elp_wakeup(wl); +	if (ret < 0) +		goto out; + +	for (i = 0; i < n_vifs; i++) { +		struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vifs[i].vif); + +		ret = __wlcore_switch_vif_chan(wl, wlvif, vifs[i].new_ctx); +		if (ret) +			goto out_sleep; +	} +out_sleep: +	wl1271_ps_elp_sleep(wl); +out: +	mutex_unlock(&wl->mutex); + +	return 0;  }  static int wl1271_op_conf_tx(struct ieee80211_hw *hw, @@ -5228,6 +5434,83 @@ out:  	mutex_unlock(&wl->mutex);  } +static const void *wlcore_get_beacon_ie(struct wl1271 *wl, +					struct wl12xx_vif *wlvif, +					u8 eid) +{ +	int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable); +	struct sk_buff *beacon = +		ieee80211_beacon_get(wl->hw, wl12xx_wlvif_to_vif(wlvif)); + +	if (!beacon) +		return NULL; + +	return cfg80211_find_ie(eid, +				beacon->data + ieoffset, +				beacon->len - ieoffset); +} + +static int wlcore_get_csa_count(struct wl1271 *wl, struct wl12xx_vif *wlvif, +				u8 *csa_count) +{ +	const u8 *ie; +	const struct ieee80211_channel_sw_ie *ie_csa; + +	ie = wlcore_get_beacon_ie(wl, wlvif, WLAN_EID_CHANNEL_SWITCH); +	if (!ie) +		return -EINVAL; + +	ie_csa = (struct ieee80211_channel_sw_ie *)&ie[2]; +	*csa_count = ie_csa->count; + +	return 0; +} + +static void wlcore_op_channel_switch_beacon(struct ieee80211_hw *hw, +					    struct ieee80211_vif *vif, +					    struct cfg80211_chan_def *chandef) +{ +	struct wl1271 *wl = hw->priv; +	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); +	struct ieee80211_channel_switch ch_switch = { +		.block_tx = true, +		.chandef = *chandef, +	}; +	int ret; + +	wl1271_debug(DEBUG_MAC80211, +		     "mac80211 channel switch beacon (role %d)", +		     wlvif->role_id); + +	ret = wlcore_get_csa_count(wl, wlvif, &ch_switch.count); +	if (ret < 0) { +		wl1271_error("error getting beacon (for CSA counter)"); +		return; +	} + +	mutex_lock(&wl->mutex); + +	if (unlikely(wl->state != WLCORE_STATE_ON)) { +		ret = -EBUSY; +		goto out; +	} + +	ret = wl1271_ps_elp_wakeup(wl); +	if (ret < 0) +		goto out; + +	ret = wl->ops->channel_switch(wl, wlvif, &ch_switch); +	if (ret) +		goto out_sleep; + +	set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); + +out_sleep: +	wl1271_ps_elp_sleep(wl); +out: +	mutex_unlock(&wl->mutex); +} +  static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  			    u32 queues, bool drop)  { @@ -5370,19 +5653,26 @@ static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,  				    u32 changed)  {  	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); -	struct wl1271 *wl = hw->priv; -	wlcore_hw_sta_rc_update(wl, wlvif, sta, changed); +	wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update"); + +	if (!(changed & IEEE80211_RC_BW_CHANGED)) +		return; + +	/* this callback is atomic, so schedule a new work */ +	wlvif->rc_update_bw = sta->bandwidth; +	ieee80211_queue_work(hw, &wlvif->rc_update_work);  } -static int wlcore_op_get_rssi(struct ieee80211_hw *hw, -			       struct ieee80211_vif *vif, -			       struct ieee80211_sta *sta, -			       s8 *rssi_dbm) +static void wlcore_op_sta_statistics(struct ieee80211_hw *hw, +				     struct ieee80211_vif *vif, +				     struct ieee80211_sta *sta, +				     struct station_info *sinfo)  {  	struct wl1271 *wl = hw->priv;  	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); -	int ret = 0; +	s8 rssi_dbm; +	int ret;  	wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi"); @@ -5395,17 +5685,18 @@ static int wlcore_op_get_rssi(struct ieee80211_hw *hw,  	if (ret < 0)  		goto out_sleep; -	ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm); +	ret = wlcore_acx_average_rssi(wl, wlvif, &rssi_dbm);  	if (ret < 0)  		goto out_sleep; +	sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); +	sinfo->signal = rssi_dbm; +  out_sleep:  	wl1271_ps_elp_sleep(wl);  out:  	mutex_unlock(&wl->mutex); - -	return ret;  }  static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) @@ -5596,6 +5887,7 @@ static const struct ieee80211_ops wl1271_ops = {  	.set_bitrate_mask = wl12xx_set_bitrate_mask,  	.set_default_unicast_key = wl1271_op_set_default_key_idx,  	.channel_switch = wl12xx_op_channel_switch, +	.channel_switch_beacon = wlcore_op_channel_switch_beacon,  	.flush = wlcore_op_flush,  	.remain_on_channel = wlcore_op_remain_on_channel,  	.cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel, @@ -5604,8 +5896,9 @@ static const struct ieee80211_ops wl1271_ops = {  	.change_chanctx = wlcore_op_change_chanctx,  	.assign_vif_chanctx = wlcore_op_assign_vif_chanctx,  	.unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx, +	.switch_vif_chanctx = wlcore_op_switch_vif_chanctx,  	.sta_rc_update = wlcore_op_sta_rc_update, -	.get_rssi = wlcore_op_get_rssi, +	.sta_statistics = wlcore_op_sta_statistics,  	CFG80211_TESTMODE_CMD(wl1271_tm_cmd)  }; @@ -5776,7 +6069,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)  	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |  		IEEE80211_HW_SUPPORTS_PS |  		IEEE80211_HW_SUPPORTS_DYNAMIC_PS | -		IEEE80211_HW_SUPPORTS_UAPSD |  		IEEE80211_HW_HAS_RATE_CONTROL |  		IEEE80211_HW_CONNECTION_MONITOR |  		IEEE80211_HW_REPORTS_TX_ACK_STATUS | @@ -5811,7 +6103,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)  	wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |  				WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | -				WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +				WIPHY_FLAG_SUPPORTS_SCHED_SCAN | +				WIPHY_FLAG_HAS_CHANNEL_SWITCH;  	/* make sure all our channels fit in the scanned_ch bitmask */  	BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +  | 

