mac80211: introduce per vif frame registration API
[cascardo/linux.git] / net / mac80211 / cfg.c
index bf7023f..9eab783 100644 (file)
@@ -1019,6 +1019,65 @@ static int sta_apply_auth_flags(struct ieee80211_local *local,
        return 0;
 }
 
+static void sta_apply_mesh_params(struct ieee80211_local *local,
+                                 struct sta_info *sta,
+                                 struct station_parameters *params)
+{
+#ifdef CONFIG_MAC80211_MESH
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       u32 changed = 0;
+
+       if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
+               switch (params->plink_state) {
+               case NL80211_PLINK_ESTAB:
+                       if (sta->mesh->plink_state != NL80211_PLINK_ESTAB)
+                               changed = mesh_plink_inc_estab_count(sdata);
+                       sta->mesh->plink_state = params->plink_state;
+
+                       ieee80211_mps_sta_status_update(sta);
+                       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                     sdata->u.mesh.mshcfg.power_mode);
+                       break;
+               case NL80211_PLINK_LISTEN:
+               case NL80211_PLINK_BLOCKED:
+               case NL80211_PLINK_OPN_SNT:
+               case NL80211_PLINK_OPN_RCVD:
+               case NL80211_PLINK_CNF_RCVD:
+               case NL80211_PLINK_HOLDING:
+                       if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
+                               changed = mesh_plink_dec_estab_count(sdata);
+                       sta->mesh->plink_state = params->plink_state;
+
+                       ieee80211_mps_sta_status_update(sta);
+                       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                       NL80211_MESH_POWER_UNKNOWN);
+                       break;
+               default:
+                       /*  nothing  */
+                       break;
+               }
+       }
+
+       switch (params->plink_action) {
+       case NL80211_PLINK_ACTION_NO_ACTION:
+               /* nothing */
+               break;
+       case NL80211_PLINK_ACTION_OPEN:
+               changed |= mesh_plink_open(sta);
+               break;
+       case NL80211_PLINK_ACTION_BLOCK:
+               changed |= mesh_plink_block(sta);
+               break;
+       }
+
+       if (params->local_pm)
+               changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                                         params->local_pm);
+
+       ieee80211_mbss_info_change_notify(sdata, changed);
+#endif
+}
+
 static int sta_apply_parameters(struct ieee80211_local *local,
                                struct sta_info *sta,
                                struct station_parameters *params)
@@ -1076,7 +1135,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
        }
 
        if (mask & BIT(NL80211_STA_FLAG_MFP)) {
-               sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
                if (set & BIT(NL80211_STA_FLAG_MFP))
                        set_sta_flag(sta, WLAN_STA_MFP);
                else
@@ -1097,6 +1155,13 @@ static int sta_apply_parameters(struct ieee80211_local *local,
            params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
                set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
 
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           !sdata->u.mgd.tdls_wider_bw_prohibited &&
+           ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
+           params->ext_capab_len >= 8 &&
+           params->ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED)
+               set_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW);
+
        if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
                sta->sta.uapsd_queues = params->uapsd_queues;
                sta->sta.max_sp = params->max_sp;
@@ -1144,62 +1209,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                              band, false);
        }
 
-       if (ieee80211_vif_is_mesh(&sdata->vif)) {
-#ifdef CONFIG_MAC80211_MESH
-               u32 changed = 0;
-
-               if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
-                       switch (params->plink_state) {
-                       case NL80211_PLINK_ESTAB:
-                               if (sta->plink_state != NL80211_PLINK_ESTAB)
-                                       changed = mesh_plink_inc_estab_count(
-                                                       sdata);
-                               sta->plink_state = params->plink_state;
-
-                               ieee80211_mps_sta_status_update(sta);
-                               changed |= ieee80211_mps_set_sta_local_pm(sta,
-                                             sdata->u.mesh.mshcfg.power_mode);
-                               break;
-                       case NL80211_PLINK_LISTEN:
-                       case NL80211_PLINK_BLOCKED:
-                       case NL80211_PLINK_OPN_SNT:
-                       case NL80211_PLINK_OPN_RCVD:
-                       case NL80211_PLINK_CNF_RCVD:
-                       case NL80211_PLINK_HOLDING:
-                               if (sta->plink_state == NL80211_PLINK_ESTAB)
-                                       changed = mesh_plink_dec_estab_count(
-                                                       sdata);
-                               sta->plink_state = params->plink_state;
-
-                               ieee80211_mps_sta_status_update(sta);
-                               changed |= ieee80211_mps_set_sta_local_pm(sta,
-                                               NL80211_MESH_POWER_UNKNOWN);
-                               break;
-                       default:
-                               /*  nothing  */
-                               break;
-                       }
-               }
-
-               switch (params->plink_action) {
-               case NL80211_PLINK_ACTION_NO_ACTION:
-                       /* nothing */
-                       break;
-               case NL80211_PLINK_ACTION_OPEN:
-                       changed |= mesh_plink_open(sta);
-                       break;
-               case NL80211_PLINK_ACTION_BLOCK:
-                       changed |= mesh_plink_block(sta);
-                       break;
-               }
-
-               if (params->local_pm)
-                       changed |=
-                             ieee80211_mps_set_sta_local_pm(sta,
-                                                            params->local_pm);
-               ieee80211_mbss_info_change_notify(sdata, changed);
-#endif
-       }
+       if (ieee80211_vif_is_mesh(&sdata->vif))
+               sta_apply_mesh_params(local, sta, params);
 
        /* set the STA state after all sta info from usermode has been set */
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
@@ -2358,6 +2369,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
        const u8 *ap;
        enum ieee80211_smps_mode old_req;
        int err;
+       struct sta_info *sta;
+       bool tdls_peer_found = false;
 
        lockdep_assert_held(&sdata->wdev.mtx);
 
@@ -2382,11 +2395,22 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
 
        ap = sdata->u.mgd.associated->bssid;
 
+       rcu_read_lock();
+       list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
+               if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
+                   !test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       continue;
+
+               tdls_peer_found = true;
+               break;
+       }
+       rcu_read_unlock();
+
        if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
-               if (sdata->u.mgd.powersave)
-                       smps_mode = IEEE80211_SMPS_DYNAMIC;
-               else
+               if (tdls_peer_found || !sdata->u.mgd.powersave)
                        smps_mode = IEEE80211_SMPS_OFF;
+               else
+                       smps_mode = IEEE80211_SMPS_DYNAMIC;
        }
 
        /* send SM PS frame to AP */
@@ -2394,6 +2418,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
                                         ap, ap);
        if (err)
                sdata->u.mgd.req_smps = old_req;
+       else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
+               ieee80211_teardown_tdls_peers(sdata);
 
        return err;
 }
@@ -2479,16 +2505,26 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
                sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
                memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
                       sizeof(mask->control[i].ht_mcs));
+               memcpy(sdata->rc_rateidx_vht_mcs_mask[i],
+                      mask->control[i].vht_mcs,
+                      sizeof(mask->control[i].vht_mcs));
 
                sdata->rc_has_mcs_mask[i] = false;
+               sdata->rc_has_vht_mcs_mask[i] = false;
                if (!sband)
                        continue;
 
-               for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++)
-                       if (~sdata->rc_rateidx_mcs_mask[i][j]) {
+               for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) {
+                       if (~sdata->rc_rateidx_mcs_mask[i][j])
                                sdata->rc_has_mcs_mask[i] = true;
+
+                       if (~sdata->rc_rateidx_vht_mcs_mask[i][j])
+                               sdata->rc_has_vht_mcs_mask[i] = true;
+
+                       if (sdata->rc_has_mcs_mask[i] &&
+                           sdata->rc_has_vht_mcs_mask[i])
                                break;
-                       }
+               }
        }
 
        return 0;
@@ -3480,18 +3516,32 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
                                          u16 frame_type, bool reg)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 
        switch (frame_type) {
        case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
-               if (reg)
+               if (reg) {
                        local->probe_req_reg++;
-               else
-                       local->probe_req_reg--;
+                       sdata->vif.probe_req_reg++;
+               } else {
+                       if (local->probe_req_reg)
+                               local->probe_req_reg--;
+
+                       if (sdata->vif.probe_req_reg)
+                               sdata->vif.probe_req_reg--;
+               }
 
                if (!local->open_count)
                        break;
 
-               ieee80211_queue_work(&local->hw, &local->reconfig_filter);
+               if (sdata->vif.probe_req_reg == 1)
+                       drv_config_iface_filter(local, sdata, FIF_PROBE_REQ,
+                                               FIF_PROBE_REQ);
+               else if (sdata->vif.probe_req_reg == 0)
+                       drv_config_iface_filter(local, sdata, 0,
+                                               FIF_PROBE_REQ);
+
+               ieee80211_configure_filter(local);
                break;
        default:
                break;