Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / util.c
index 0151ae33c4cd448e4812b0e9255d47898bf595da..9b99916f384fa38b41cbd620c84a6ce10ec368c1 100644 (file)
@@ -512,7 +512,7 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw)
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
 void ieee80211_iterate_active_interfaces(
-       struct ieee80211_hw *hw,
+       struct ieee80211_hw *hw, u32 iter_flags,
        void (*iterator)(void *data, u8 *mac,
                         struct ieee80211_vif *vif),
        void *data)
@@ -530,6 +530,9 @@ void ieee80211_iterate_active_interfaces(
                default:
                        break;
                }
+               if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
+                   !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+                       continue;
                if (ieee80211_sdata_running(sdata))
                        iterator(data, sdata->vif.addr,
                                 &sdata->vif);
@@ -537,7 +540,9 @@ void ieee80211_iterate_active_interfaces(
 
        sdata = rcu_dereference_protected(local->monitor_sdata,
                                          lockdep_is_held(&local->iflist_mtx));
-       if (sdata)
+       if (sdata &&
+           (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+            sdata->flags & IEEE80211_SDATA_IN_DRIVER))
                iterator(data, sdata->vif.addr, &sdata->vif);
 
        mutex_unlock(&local->iflist_mtx);
@@ -545,7 +550,7 @@ void ieee80211_iterate_active_interfaces(
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
 
 void ieee80211_iterate_active_interfaces_atomic(
-       struct ieee80211_hw *hw,
+       struct ieee80211_hw *hw, u32 iter_flags,
        void (*iterator)(void *data, u8 *mac,
                         struct ieee80211_vif *vif),
        void *data)
@@ -563,13 +568,18 @@ void ieee80211_iterate_active_interfaces_atomic(
                default:
                        break;
                }
+               if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
+                   !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+                       continue;
                if (ieee80211_sdata_running(sdata))
                        iterator(data, sdata->vif.addr,
                                 &sdata->vif);
        }
 
        sdata = rcu_dereference(local->monitor_sdata);
-       if (sdata)
+       if (sdata &&
+           (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+            sdata->flags & IEEE80211_SDATA_IN_DRIVER))
                iterator(data, sdata->vif.addr, &sdata->vif);
 
        rcu_read_unlock();
@@ -769,6 +779,18 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
                        else
                                elem_parse_failed = true;
                        break;
+               case WLAN_EID_VHT_CAPABILITY:
+                       if (elen >= sizeof(struct ieee80211_vht_cap))
+                               elems->vht_cap_elem = (void *)pos;
+                       else
+                               elem_parse_failed = true;
+                       break;
+               case WLAN_EID_VHT_OPERATION:
+                       if (elen >= sizeof(struct ieee80211_vht_operation))
+                               elems->vht_operation = (void *)pos;
+                       else
+                               elem_parse_failed = true;
+                       break;
                case WLAN_EID_MESH_ID:
                        elems->mesh_id = pos;
                        elems->mesh_id_len = elen;
@@ -837,7 +859,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
                if (elem_parse_failed)
                        elems->parse_error = true;
                else
-                       set_bit(id, seen_elems);
+                       __set_bit(id, seen_elems);
 
                left -= elen;
                pos += elen;
@@ -860,6 +882,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_tx_queue_params qparam;
+       struct ieee80211_chanctx_conf *chanctx_conf;
        int ac;
        bool use_11b, enable_qos;
        int aCWmin, aCWmax;
@@ -872,8 +895,12 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
 
        memset(&qparam, 0, sizeof(qparam));
 
-       use_11b = (local->oper_channel->band == IEEE80211_BAND_2GHZ) &&
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       use_11b = (chanctx_conf &&
+                  chanctx_conf->channel->band == IEEE80211_BAND_2GHZ) &&
                 !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
+       rcu_read_unlock();
 
        /*
         * By default disable QoS in STA mode for old access points, which do
@@ -952,7 +979,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
                                  const size_t supp_rates_len,
                                  const u8 *supp_rates)
 {
-       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *chanctx_conf;
        int i, have_higher_than_11mbit = 0;
 
        /* cf. IEEE 802.11 9.2.12 */
@@ -960,11 +987,16 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
                if ((supp_rates[i] & 0x7f) * 5 > 110)
                        have_higher_than_11mbit = 1;
 
-       if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+       if (chanctx_conf &&
+           chanctx_conf->channel->band == IEEE80211_BAND_2GHZ &&
            have_higher_than_11mbit)
                sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
        else
                sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+       rcu_read_unlock();
 
        ieee80211_set_wmm_default(sdata, true);
 }
@@ -996,7 +1028,7 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
 }
 
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
-                        u16 transaction, u16 auth_alg,
+                        u16 transaction, u16 auth_alg, u16 status,
                         u8 *extra, size_t extra_len, const u8 *da,
                         const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx)
 {
@@ -1021,7 +1053,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        memcpy(mgmt->bssid, bssid, ETH_ALEN);
        mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
        mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
-       mgmt->u.auth.status_code = cpu_to_le16(0);
+       mgmt->u.auth.status_code = cpu_to_le16(status);
        if (extra)
                memcpy(skb_put(skb, extra_len), extra, extra_len);
 
@@ -1234,7 +1266,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
                              const u8 *ssid, size_t ssid_len,
                              const u8 *ie, size_t ie_len,
                              u32 ratemask, bool directed, bool no_cck,
-                             struct ieee80211_channel *channel)
+                             struct ieee80211_channel *channel, bool scan)
 {
        struct sk_buff *skb;
 
@@ -1245,7 +1277,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
                if (no_cck)
                        IEEE80211_SKB_CB(skb)->flags |=
                                IEEE80211_TX_CTL_NO_CCK_RATE;
-               ieee80211_tx_skb(sdata, skb);
+               if (scan)
+                       ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
+               else
+                       ieee80211_tx_skb(sdata, skb);
        }
 }
 
@@ -1308,6 +1343,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 {
        struct ieee80211_hw *hw = &local->hw;
        struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_chanctx *ctx;
        struct sta_info *sta;
        int res, i;
 
@@ -1380,6 +1416,46 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                        res = drv_add_interface(local, sdata);
        }
 
+       /* add channel contexts */
+       if (local->use_chanctx) {
+               mutex_lock(&local->chanctx_mtx);
+               list_for_each_entry(ctx, &local->chanctx_list, list)
+                       WARN_ON(drv_add_chanctx(local, ctx));
+               mutex_unlock(&local->chanctx_mtx);
+       }
+
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               struct ieee80211_chanctx_conf *ctx_conf;
+
+               if (!ieee80211_sdata_running(sdata))
+                       continue;
+
+               mutex_lock(&local->chanctx_mtx);
+               ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                               lockdep_is_held(&local->chanctx_mtx));
+               if (ctx_conf) {
+                       ctx = container_of(ctx_conf, struct ieee80211_chanctx,
+                                          conf);
+                       drv_assign_vif_chanctx(local, sdata, ctx);
+               }
+               mutex_unlock(&local->chanctx_mtx);
+       }
+
+       sdata = rtnl_dereference(local->monitor_sdata);
+       if (sdata && local->use_chanctx && ieee80211_sdata_running(sdata)) {
+               struct ieee80211_chanctx_conf *ctx_conf;
+
+               mutex_lock(&local->chanctx_mtx);
+               ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                               lockdep_is_held(&local->chanctx_mtx));
+               if (ctx_conf) {
+                       ctx = container_of(ctx_conf, struct ieee80211_chanctx,
+                                          conf);
+                       drv_assign_vif_chanctx(local, sdata, ctx);
+               }
+               mutex_unlock(&local->chanctx_mtx);
+       }
+
        /* add STAs back */
        mutex_lock(&local->sta_mtx);
        list_for_each_entry(sta, &local->sta_list, list) {
@@ -1435,7 +1511,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                          BSS_CHANGED_BSSID |
                          BSS_CHANGED_CQM |
                          BSS_CHANGED_QOS |
-                         BSS_CHANGED_IDLE;
+                         BSS_CHANGED_IDLE |
+                         BSS_CHANGED_TXPOWER;
 
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_STATION:
@@ -1452,9 +1529,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                case NL80211_IFTYPE_AP:
                        changed |= BSS_CHANGED_SSID;
 
-                       if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       if (sdata->vif.type == NL80211_IFTYPE_AP) {
                                changed |= BSS_CHANGED_AP_PROBE_RESP;
 
+                               if (rcu_access_pointer(sdata->u.ap.beacon))
+                                       drv_start_ap(local, sdata);
+                       }
+
                        /* fall through */
                case NL80211_IFTYPE_MESH_POINT:
                        changed |= BSS_CHANGED_BEACON |
@@ -1553,8 +1634,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
         * If this is for hw restart things are still running.
         * We may want to change that later, however.
         */
-       if (!local->suspended)
+       if (!local->suspended) {
+               drv_restart_complete(local);
                return 0;
+       }
 
 #ifdef CONFIG_PM
        /* first set suspended false, then resuming */
@@ -1617,68 +1700,24 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
 }
 EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
 
-static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
-                         enum ieee80211_smps_mode *smps_mode)
+void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
 {
-       if (ifmgd->associated) {
-               *smps_mode = ifmgd->ap_smps;
-
-               if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) {
-                       if (ifmgd->powersave)
-                               *smps_mode = IEEE80211_SMPS_DYNAMIC;
-                       else
-                               *smps_mode = IEEE80211_SMPS_OFF;
-               }
-
-               return 1;
-       }
-
-       return 0;
-}
-
-void ieee80211_recalc_smps(struct ieee80211_local *local)
-{
-       struct ieee80211_sub_if_data *sdata;
-       enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
-       int count = 0;
-
-       mutex_lock(&local->iflist_mtx);
-
-       /*
-        * This function could be improved to handle multiple
-        * interfaces better, but right now it makes any
-        * non-station interfaces force SM PS to be turned
-        * off. If there are multiple station interfaces it
-        * could also use the best possible mode, e.g. if
-        * one is in static and the other in dynamic then
-        * dynamic is ok.
-        */
-
-       list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!ieee80211_sdata_running(sdata))
-                       continue;
-               if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
-                       continue;
-               if (sdata->vif.type != NL80211_IFTYPE_STATION)
-                       goto set;
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_chanctx *chanctx;
 
-               count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+       mutex_lock(&local->chanctx_mtx);
 
-               if (count > 1) {
-                       smps_mode = IEEE80211_SMPS_OFF;
-                       break;
-               }
-       }
+       chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                       lockdep_is_held(&local->chanctx_mtx));
 
-       if (smps_mode == local->smps_mode)
+       if (WARN_ON_ONCE(!chanctx_conf))
                goto unlock;
 
- set:
-       local->smps_mode = smps_mode;
-       /* changed flag is auto-detected for this */
-       ieee80211_hw_config(local, 0);
+       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+       ieee80211_recalc_smps_chanctx(local, chanctx);
  unlock:
-       mutex_unlock(&local->iflist_mtx);
+       mutex_unlock(&local->chanctx_mtx);
 }
 
 static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
@@ -1818,8 +1857,8 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
        __le32 tmp;
 
        *pos++ = WLAN_EID_VHT_CAPABILITY;
-       *pos++ = sizeof(struct ieee80211_vht_capabilities);
-       memset(pos, 0, sizeof(struct ieee80211_vht_capabilities));
+       *pos++ = sizeof(struct ieee80211_vht_cap);
+       memset(pos, 0, sizeof(struct ieee80211_vht_cap));
 
        /* capability flags */
        tmp = cpu_to_le32(cap);
@@ -1977,3 +2016,70 @@ int ieee80211_ave_rssi(struct ieee80211_vif *vif)
        return ifmgd->ave_beacon_signal;
 }
 EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
+
+u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
+{
+       if (!mcs)
+               return 1;
+
+       /* TODO: consider rx_highest */
+
+       if (mcs->rx_mask[3])
+               return 4;
+       if (mcs->rx_mask[2])
+               return 3;
+       if (mcs->rx_mask[1])
+               return 2;
+       return 1;
+}
+
+/**
+ * ieee80211_calculate_rx_timestamp - calculate timestamp in frame
+ * @local: mac80211 hw info struct
+ * @status: RX status
+ * @mpdu_len: total MPDU length (including FCS)
+ * @mpdu_offset: offset into MPDU to calculate timestamp at
+ *
+ * This function calculates the RX timestamp at the given MPDU offset, taking
+ * into account what the RX timestamp was. An offset of 0 will just normalize
+ * the timestamp to TSF at beginning of MPDU reception.
+ */
+u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
+                                    struct ieee80211_rx_status *status,
+                                    unsigned int mpdu_len,
+                                    unsigned int mpdu_offset)
+{
+       u64 ts = status->mactime;
+       struct rate_info ri;
+       u16 rate;
+
+       if (WARN_ON(!ieee80211_have_rx_timestamp(status)))
+               return 0;
+
+       memset(&ri, 0, sizeof(ri));
+
+       /* Fill cfg80211 rate info */
+       if (status->flag & RX_FLAG_HT) {
+               ri.mcs = status->rate_idx;
+               ri.flags |= RATE_INFO_FLAGS_MCS;
+               if (status->flag & RX_FLAG_40MHZ)
+                       ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+               if (status->flag & RX_FLAG_SHORT_GI)
+                       ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else {
+               struct ieee80211_supported_band *sband;
+
+               sband = local->hw.wiphy->bands[status->band];
+               ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+       }
+
+       rate = cfg80211_calculate_bitrate(&ri);
+
+       /* rewind from end of MPDU */
+       if (status->flag & RX_FLAG_MACTIME_END)
+               ts -= mpdu_len * 8 * 10 / rate;
+
+       ts += mpdu_offset * 8 * 10 / rate;
+
+       return ts;
+}