iwlwifi: mvm: Update the supported interface combinations
[deliverable/linux.git] / drivers / net / wireless / iwlwifi / mvm / mac80211.c
index d9eabdc6e493fb561d3b6b8b0ae848ee08777fc1..f091cf24b96c74daab58668f5433948a97baaab5 100644 (file)
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
                .max = 1,
-               .types = BIT(NL80211_IFTYPE_STATION) |
-                       BIT(NL80211_IFTYPE_AP),
+               .types = BIT(NL80211_IFTYPE_STATION),
        },
        {
                .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+               .types = BIT(NL80211_IFTYPE_AP) |
+                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
                        BIT(NL80211_IFTYPE_P2P_GO),
        },
        {
@@ -152,7 +152,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_SUPPORTS_PS |
                    IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
                    IEEE80211_HW_AMPDU_AGGREGATION |
-                   IEEE80211_HW_TIMING_BEACON_ONLY;
+                   IEEE80211_HW_TIMING_BEACON_ONLY |
+                   IEEE80211_HW_CONNECTION_MONITOR;
 
        hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
@@ -265,8 +266,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
-       if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) {
-               IWL_DEBUG_DROP(mvm, "Dropping - RF KILL\n");
+       if (iwl_mvm_is_radio_killed(mvm)) {
+               IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
                goto drop;
        }
 
@@ -608,21 +609,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        mvmvif->phy_ctxt = NULL;
        iwl_mvm_mac_ctxt_remove(mvm, vif);
  out_release:
-       /*
-        * TODO: remove this temporary code.
-        * Currently MVM FW supports power management only on single MAC.
-        * Check if only one additional interface remains after releasing
-        * current one. Update power mode on the remaining interface.
-        */
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
-       IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
-                          mvm->vif_count);
-       if (mvm->vif_count == 1) {
-               ieee80211_iterate_active_interfaces(
-                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
-                                       iwl_mvm_power_update_iterator, mvm);
-       }
+       ieee80211_iterate_active_interfaces(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_power_update_iterator, mvm);
        iwl_mvm_mac_ctxt_release(mvm, vif);
  out_unlock:
        mutex_unlock(&mvm->mutex);
@@ -660,8 +651,7 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
                 * By now, all the AC queues are empty. The AGG queues are
                 * empty too. We already got all the Tx responses for all the
                 * packets in the queues. The drain work can have been
-                * triggered. Flush it. This work item takes the mutex, so kill
-                * it before we take it.
+                * triggered. Flush it.
                 */
                flush_work(&mvm->sta_drained_wk);
        }
@@ -737,6 +727,20 @@ static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
        *total_flags = 0;
 }
 
+static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif)
+{
+       struct iwl_mcast_filter_cmd mcast_filter_cmd = {
+               .pass_all = 1,
+       };
+
+       memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN);
+
+       return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC,
+                                   sizeof(mcast_filter_cmd),
+                                   &mcast_filter_cmd);
+}
+
 static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                             struct ieee80211_vif *vif,
                                             struct ieee80211_bss_conf *bss_conf,
@@ -758,6 +762,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                return;
                        }
                        iwl_mvm_bt_coex_vif_assoc(mvm, vif);
+                       iwl_mvm_configure_mcast_filter(mvm, vif);
                } 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);
@@ -769,6 +774,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                        if (ret)
                                IWL_ERR(mvm, "failed to update quotas\n");
                }
+               ret = iwl_mvm_power_update_mode(mvm, vif);
+               if (ret)
+                       IWL_ERR(mvm, "failed to update power mode\n");
        } else if (changes & BSS_CHANGED_DTIM_PERIOD) {
                /*
                 * We received a beacon _after_ association so
@@ -777,19 +785,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                iwl_mvm_remove_time_event(mvm, mvmvif,
                                          &mvmvif->time_event_data);
        } else if (changes & BSS_CHANGED_PS) {
-               /*
-                * TODO: remove this temporary code.
-                * Currently MVM FW supports power management only on single
-                * MAC. Avoid power mode update if more than one interface
-                * is active.
-                */
-               IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
-                                  mvm->vif_count);
-               if (mvm->vif_count == 1) {
-                       ret = iwl_mvm_power_update_mode(mvm, vif);
-                       if (ret)
-                               IWL_ERR(mvm, "failed to update power mode\n");
-               }
+               ret = iwl_mvm_power_update_mode(mvm, vif);
+               if (ret)
+                       IWL_ERR(mvm, "failed to update power mode\n");
        }
 }
 
@@ -967,7 +965,7 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
 
        switch (cmd) {
        case STA_NOTIFY_SLEEP:
-               if (atomic_read(&mvmsta->pending_frames) > 0)
+               if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
                        ieee80211_sta_block_awake(hw, sta, true);
                /*
                 * The fw updates the STA to be asleep. Tx packets on the Tx
@@ -1194,28 +1192,105 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct cfg80211_chan_def chandef;
-       int ret;
+       struct iwl_mvm_phy_ctxt *phy_ctxt;
+       int ret, i;
+
+       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
+                          duration, type);
 
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
                IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type);
                return -EINVAL;
        }
 
-       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
-                          duration, type);
-
        mutex_lock(&mvm->mutex);
 
+       for (i = 0; i < NUM_PHY_CTX; i++) {
+               phy_ctxt = &mvm->phy_ctxts[i];
+               if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt)
+                       continue;
+
+               if (phy_ctxt->ref && channel == phy_ctxt->channel) {
+                       /*
+                        * Unbind the P2P_DEVICE from the current PHY context,
+                        * and if the PHY context is not used remove it.
+                        */
+                       ret = iwl_mvm_binding_remove_vif(mvm, vif);
+                       if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
+                               goto out_unlock;
+
+                       iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+
+                       /* Bind the P2P_DEVICE to the current PHY Context */
+                       mvmvif->phy_ctxt = phy_ctxt;
+
+                       ret = iwl_mvm_binding_add_vif(mvm, vif);
+                       if (WARN(ret, "Failed binding P2P_DEVICE\n"))
+                               goto out_unlock;
+
+                       iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+                       goto schedule_time_event;
+               }
+       }
+
+       /* Need to update the PHY context only if the ROC channel changed */
+       if (channel == mvmvif->phy_ctxt->channel)
+               goto schedule_time_event;
+
        cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
-       ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt,
-                                      &chandef, 1, 1);
 
+       /*
+        * Change the PHY context configuration as it is currently referenced
+        * only by the P2P Device MAC
+        */
+       if (mvmvif->phy_ctxt->ref == 1) {
+               ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt,
+                                              &chandef, 1, 1);
+               if (ret)
+                       goto out_unlock;
+       } else {
+               /*
+                * The PHY context is shared with other MACs. Need to remove the
+                * P2P Device from the binding, allocate an new PHY context and
+                * create a new binding
+                */
+               phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+               if (!phy_ctxt) {
+                       ret = -ENOSPC;
+                       goto out_unlock;
+               }
+
+               ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef,
+                                              1, 1);
+               if (ret) {
+                       IWL_ERR(mvm, "Failed to change PHY context\n");
+                       goto out_unlock;
+               }
+
+               /* Unbind the P2P_DEVICE from the current PHY context */
+               ret = iwl_mvm_binding_remove_vif(mvm, vif);
+               if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
+                       goto out_unlock;
+
+               iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+
+               /* Bind the P2P_DEVICE to the new allocated PHY context */
+               mvmvif->phy_ctxt = phy_ctxt;
+
+               ret = iwl_mvm_binding_add_vif(mvm, vif);
+               if (WARN(ret, "Failed binding P2P_DEVICE\n"))
+                       goto out_unlock;
+
+               iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+       }
+
+schedule_time_event:
        /* Schedule the time events */
        ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
 
+out_unlock:
        mutex_unlock(&mvm->mutex);
        IWL_DEBUG_MAC80211(mvm, "leave\n");
-
        return ret;
 }
 
@@ -1285,6 +1360,14 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
        u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
        struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
 
+       if (WARN_ONCE((phy_ctxt->ref > 1) &&
+                     (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |
+                                  IEEE80211_CHANCTX_CHANGE_RX_CHAINS |
+                                  IEEE80211_CHANCTX_CHANGE_RADAR)),
+                     "Cannot change PHY. Ref=%d, changed=0x%X\n",
+                     phy_ctxt->ref, changed))
+               return;
+
        mutex_lock(&mvm->mutex);
        iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
                                 ctx->rx_chains_static,
This page took 0.028645 seconds and 5 git commands to generate.