]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/wireless/iwlwifi/mvm/mac80211.c
Merge remote-tracking branch 'sound/for-next'
[karo-tx-linux.git] / drivers / net / wireless / iwlwifi / mvm / mac80211.c
index 9833cdf6177cd34d199de063cb0ff466042cfed0..f40685c3764ea8b211ad9fa7affb8ed675ef1ddd 100644 (file)
@@ -77,6 +77,7 @@
 #include "iwl-eeprom-parse.h"
 #include "fw-api-scan.h"
 #include "iwl-phy-db.h"
+#include "testmode.h"
 
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
@@ -138,6 +139,14 @@ static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
        }
 }
 
+static int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm)
+{
+       /* we create the 802.11 header and SSID element */
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID)
+               return mvm->fw->ucode_capa.max_probe_length - 24 - 2;
+       return mvm->fw->ucode_capa.max_probe_length - 24 - 34;
+}
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
        struct ieee80211_hw *hw = mvm->hw;
@@ -158,7 +167,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_SUPPORTS_STATIC_SMPS |
                    IEEE80211_HW_SUPPORTS_UAPSD;
 
-       hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
+       hw->queues = mvm->first_agg_queue;
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
        hw->rate_control_algorithm = "iwl-mvm-rs";
 
@@ -181,6 +190,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                BIT(NL80211_IFTYPE_P2P_GO) |
                BIT(NL80211_IFTYPE_P2P_DEVICE);
 
+       /* IBSS has bugs in older versions */
+       if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8)
+               hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
+
        hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
                            WIPHY_FLAG_DISABLE_BEACON_HINTS |
                            WIPHY_FLAG_IBSS_RSN;
@@ -212,9 +225,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        iwl_mvm_reset_phy_ctxts(mvm);
 
-       /* we create the 802.11 header and a max-length SSID element */
-       hw->wiphy->max_scan_ie_len =
-               mvm->fw->ucode_capa.max_probe_length - 24 - 34;
+       hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm);
+
        hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
 
        if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
@@ -231,6 +243,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        else
                hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
+               hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+               hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
+               hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
+               /* we create the 802.11 header and zero length SSID IE. */
+               hw->wiphy->max_sched_scan_ie_len =
+                                       SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
+       }
+
        hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
                               NL80211_FEATURE_P2P_GO_OPPPS;
 
@@ -548,7 +569,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
         * In short: there's not much we can do at this point, other than
         * allocating resources :)
         */
-       if (vif->type == NL80211_IFTYPE_AP) {
+       if (vif->type == NL80211_IFTYPE_AP ||
+           vif->type == NL80211_IFTYPE_ADHOC) {
                u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
                ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
                                               qmask);
@@ -698,7 +720,14 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
         * For AP/GO interface, the tear down of the resources allocated to the
         * interface is be handled as part of the stop_ap flow.
         */
-       if (vif->type == NL80211_IFTYPE_AP) {
+       if (vif->type == NL80211_IFTYPE_AP ||
+           vif->type == NL80211_IFTYPE_ADHOC) {
+#ifdef CONFIG_NL80211_TESTMODE
+               if (vif == mvm->noa_vif) {
+                       mvm->noa_vif = NULL;
+                       mvm->noa_duration = 0;
+               }
+#endif
                iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);
                goto out_release;
        }
@@ -796,6 +825,27 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                return;
                        }
                        iwl_mvm_configure_mcast_filter(mvm, vif);
+
+                       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
+                                    &mvm->status)) {
+                               /*
+                                * If we're restarting then the firmware will
+                                * obviously have lost synchronisation with
+                                * the AP. It will attempt to synchronise by
+                                * itself, but we can make it more reliable by
+                                * scheduling a session protection time event.
+                                *
+                                * The firmware needs to receive a beacon to
+                                * catch up with synchronisation, use 110% of
+                                * the beacon interval.
+                                *
+                                * Set a large maximum delay to allow for more
+                                * than a single interface.
+                                */
+                               u32 dur = (11 * vif->bss_conf.beacon_int) / 10;
+                               iwl_mvm_protect_session(mvm, vif, dur, dur,
+                                                       5 * dur);
+                       }
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /* remove AP station now that the MAC is unassoc */
                        ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
@@ -819,7 +869,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                        if (ret)
                                IWL_ERR(mvm, "failed to update power mode\n");
                }
-               iwl_mvm_bt_coex_vif_assoc(mvm, vif);
+               iwl_mvm_bt_coex_vif_change(mvm);
        } else if (changes & BSS_CHANGED_BEACON_INFO) {
                /*
                 * We received a beacon _after_ association so
@@ -848,7 +898,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        }
 }
 
-static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -871,7 +922,7 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        if (ret)
                goto out_remove;
 
-       mvmvif->ap_active = true;
+       mvmvif->ap_ibss_active = true;
 
        /* Send the bcast station. At this stage the TBTT and DTIM time events
         * are added and applied to the scheduler */
@@ -883,10 +934,12 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        if (ret)
                goto out_rm_bcast;
 
-       /* Need to update the P2P Device MAC */
+       /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
 
+       iwl_mvm_bt_coex_vif_change(mvm);
+
        mutex_unlock(&mvm->mutex);
        return 0;
 
@@ -901,7 +954,8 @@ out_unlock:
        return ret;
 }
 
-static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -910,9 +964,11 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
        mutex_lock(&mvm->mutex);
 
-       mvmvif->ap_active = false;
+       mvmvif->ap_ibss_active = false;
+
+       iwl_mvm_bt_coex_vif_change(mvm);
 
-       /* Need to update the P2P Device MAC */
+       /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
 
@@ -924,10 +980,11 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        mutex_unlock(&mvm->mutex);
 }
 
-static void iwl_mvm_bss_info_changed_ap(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif,
-                                       struct ieee80211_bss_conf *bss_conf,
-                                       u32 changes)
+static void
+iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_bss_conf *bss_conf,
+                                u32 changes)
 {
        /* Need to send a new beacon template to the FW */
        if (changes & BSS_CHANGED_BEACON) {
@@ -950,7 +1007,8 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
                iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
                break;
        case NL80211_IFTYPE_AP:
-               iwl_mvm_bss_info_changed_ap(mvm, vif, bss_conf, changes);
+       case NL80211_IFTYPE_ADHOC:
+               iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes);
                break;
        default:
                /* shouldn't happen */
@@ -1163,7 +1221,54 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
        /* Try really hard to protect the session and hear a beacon */
-       iwl_mvm_protect_session(mvm, vif, duration, min_duration);
+       iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500);
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct cfg80211_sched_scan_request *req,
+                                       struct ieee80211_sched_scan_ies *ies)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
+               IWL_DEBUG_SCAN(mvm,
+                              "SCHED SCAN request during internal scan - abort\n");
+               ret = -EBUSY;
+               goto out;
+       }
+
+       mvm->scan_status = IWL_MVM_SCAN_SCHED;
+
+       ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
+       if (ret)
+               goto err;
+
+       ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+       if (ret)
+               goto err;
+
+       ret = iwl_mvm_sched_scan_start(mvm, req);
+       if (!ret)
+               goto out;
+err:
+       mvm->scan_status = IWL_MVM_SCAN_NONE;
+out:
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+static void iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_sched_scan_stop(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1207,8 +1312,13 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
 
        switch (cmd) {
        case SET_KEY:
-               if (vif->type == NL80211_IFTYPE_AP && !sta) {
-                       /* GTK on AP interface is a TX-only key, return 0 */
+               if ((vif->type == NL80211_IFTYPE_ADHOC ||
+                    vif->type == NL80211_IFTYPE_AP) && !sta) {
+                       /*
+                        * GTK on AP interface is a TX-only key, return 0;
+                        * on IBSS they're per-station and because we're lazy
+                        * we don't support them for RX, so do the same.
+                        */
                        ret = 0;
                        key->hw_key_idx = STA_KEY_IDX_INVALID;
                        break;
@@ -1252,6 +1362,9 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
+       if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID)
+               return;
+
        iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key);
 }
 
@@ -1445,6 +1558,7 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
        iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
                                 ctx->rx_chains_static,
                                 ctx->rx_chains_dynamic);
+       iwl_mvm_bt_coex_vif_change(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1464,14 +1578,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
 
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
                /*
                 * The AP binding flow is handled as part of the start_ap flow
-                * (in bss_info_changed).
+                * (in bss_info_changed), similarly for IBSS.
                 */
                ret = 0;
                goto out_unlock;
        case NL80211_IFTYPE_STATION:
-       case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_MONITOR:
                break;
        default:
@@ -1517,10 +1631,10 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
 
        iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
 
-       if (vif->type == NL80211_IFTYPE_AP)
-               goto out_unlock;
-
        switch (vif->type) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
+               goto out_unlock;
        case NL80211_IFTYPE_MONITOR:
                mvmvif->monitor_active = false;
                iwl_mvm_update_quotas(mvm, NULL);
@@ -1550,14 +1664,72 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
        return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
 }
 
-static void iwl_mvm_mac_rssi_callback(struct ieee80211_hw *hw,
+#ifdef CONFIG_NL80211_TESTMODE
+static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = {
+       [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 },
+       [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 },
+       [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 },
+};
+
+static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
                                      struct ieee80211_vif *vif,
-                                     enum ieee80211_rssi_event rssi_event)
+                                     void *data, int len)
+{
+       struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1];
+       int err;
+       u32 noa_duration;
+
+       err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy);
+       if (err)
+               return err;
+
+       if (!tb[IWL_MVM_TM_ATTR_CMD])
+               return -EINVAL;
+
+       switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) {
+       case IWL_MVM_TM_CMD_SET_NOA:
+               if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p ||
+                   !vif->bss_conf.enable_beacon ||
+                   !tb[IWL_MVM_TM_ATTR_NOA_DURATION])
+                       return -EINVAL;
+
+               noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]);
+               if (noa_duration >= vif->bss_conf.beacon_int)
+                       return -EINVAL;
+
+               mvm->noa_duration = noa_duration;
+               mvm->noa_vif = vif;
+
+               return iwl_mvm_update_quotas(mvm, NULL);
+       case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
+               /* must be associated client vif - ignore authorized */
+               if (!vif || vif->type != NL80211_IFTYPE_STATION ||
+                   !vif->bss_conf.assoc || !vif->bss_conf.dtim_period ||
+                   !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])
+                       return -EINVAL;
+
+               if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
+                       return iwl_mvm_enable_beacon_filter(mvm, vif);
+               return iwl_mvm_disable_beacon_filter(mvm, vif);
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   void *data, int len)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int err;
 
-       iwl_mvm_bt_rssi_event(mvm, vif, rssi_event);
+       mutex_lock(&mvm->mutex);
+       err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len);
+       mutex_unlock(&mvm->mutex);
+
+       return err;
 }
+#endif
 
 struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
@@ -1578,23 +1750,27 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
        .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
        .conf_tx = iwl_mvm_mac_conf_tx,
        .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+       .sched_scan_start = iwl_mvm_mac_sched_scan_start,
+       .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
        .set_key = iwl_mvm_mac_set_key,
        .update_tkip_key = iwl_mvm_mac_update_tkip_key,
        .remain_on_channel = iwl_mvm_roc,
        .cancel_remain_on_channel = iwl_mvm_cancel_roc,
-       .rssi_callback = iwl_mvm_mac_rssi_callback,
-
        .add_chanctx = iwl_mvm_add_chanctx,
        .remove_chanctx = iwl_mvm_remove_chanctx,
        .change_chanctx = iwl_mvm_change_chanctx,
        .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
        .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
 
-       .start_ap = iwl_mvm_start_ap,
-       .stop_ap = iwl_mvm_stop_ap,
+       .start_ap = iwl_mvm_start_ap_ibss,
+       .stop_ap = iwl_mvm_stop_ap_ibss,
+       .join_ibss = iwl_mvm_start_ap_ibss,
+       .leave_ibss = iwl_mvm_stop_ap_ibss,
 
        .set_tim = iwl_mvm_set_tim,
 
+       CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
+
 #ifdef CONFIG_PM_SLEEP
        /* look at d3.c */
        .suspend = iwl_mvm_suspend,