Merge commit 'c1e140bf79d817d4a7aa9932eb98b0359c87af33' from mac80211-next
[cascardo/linux.git] / drivers / net / wireless / ti / wlcore / main.c
index a2133b1..0a9d9a1 100644 (file)
@@ -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,11 @@ 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)
+               return ret;
+
        wl->wow_enabled = true;
        wl12xx_for_each_wlvif(wl, wlvif) {
                ret = wl1271_configure_suspend(wl, wlvif, wow);
@@ -1790,7 +1797,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 +1891,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 +2322,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,
@@ -2724,6 +2768,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);
@@ -4073,8 +4118,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);
@@ -4575,10 +4626,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,
@@ -4589,13 +4676,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);
@@ -4603,6 +4703,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;
@@ -4614,6 +4723,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)",
@@ -4622,6 +4732,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,
@@ -5229,6 +5432,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)
 {
@@ -5371,9 +5651,15 @@ 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 void wlcore_op_sta_statistics(struct ieee80211_hw *hw,
@@ -5599,6 +5885,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,
@@ -5607,6 +5894,7 @@ 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,
        .sta_statistics = wlcore_op_sta_statistics,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
@@ -5813,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) +