|
@@ -5433,6 +5433,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)
|
|
|
{
|
|
@@ -5807,6 +5884,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,
|
|
@@ -6023,7 +6101,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) +
|