mac80211: stop toggling IEEE80211_HT_CAP_SUP_WIDTH_20_40
[cascardo/linux.git] / net / mac80211 / ibss.c
index 8881fc7..40b71df 100644 (file)
@@ -67,7 +67,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        skb_reserve(skb, sdata->local->hw.extra_tx_headroom);
 
        if (!ether_addr_equal(ifibss->bssid, bssid))
-               sta_info_flush(sdata->local, sdata);
+               sta_info_flush(sdata);
 
        /* if merging, indicate to driver that we leave the old IBSS */
        if (sdata->vif.bss_conf.ibss_joined) {
@@ -191,6 +191,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        rcu_assign_pointer(ifibss->presp, skb);
 
+       sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.beacon_int = beacon_int;
        sdata->vif.bss_conf.basic_rates = basic_rates;
        bss_change = BSS_CHANGED_BEACON_INT;
@@ -227,7 +228,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan,
                                        mgmt, skb->len, 0, GFP_KERNEL);
-       cfg80211_put_bss(bss);
+       cfg80211_put_bss(local->hw.wiphy, bss);
        netif_carrier_on(sdata->dev);
        cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
 }
@@ -241,6 +242,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        u32 basic_rates;
        int i, j;
        u16 beacon_int = cbss->beacon_interval;
+       const struct cfg80211_bss_ies *ies;
+       u64 tsf;
 
        lockdep_assert_held(&sdata->u.ibss.mtx);
 
@@ -264,13 +267,17 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                }
        }
 
+       rcu_read_lock();
+       ies = rcu_dereference(cbss->ies);
+       tsf = ies->tsf;
+       rcu_read_unlock();
+
        __ieee80211_sta_join_ibss(sdata, cbss->bssid,
                                  beacon_int,
                                  cbss->channel,
                                  basic_rates,
                                  cbss->capability,
-                                 cbss->tsf,
-                                 false);
+                                 tsf, false);
 }
 
 static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
@@ -301,7 +308,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
                         "TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
                         sdata->vif.addr, addr, sdata->u.ibss.bssid);
                ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, 0, NULL, 0,
-                                   addr, sdata->u.ibss.bssid, NULL, 0, 0);
+                                   addr, sdata->u.ibss.bssid, NULL, 0, 0, 0);
        }
        return sta;
 }
@@ -421,15 +428,13 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
         * has actually implemented this.
         */
        ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0,
-                           mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0);
+                           mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0);
 }
 
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
-                                 struct ieee80211_mgmt *mgmt,
-                                 size_t len,
+                                 struct ieee80211_mgmt *mgmt, size_t len,
                                  struct ieee80211_rx_status *rx_status,
-                                 struct ieee802_11_elems *elems,
-                                 bool beacon)
+                                 struct ieee802_11_elems *elems)
 {
        struct ieee80211_local *local = sdata->local;
        int freq;
@@ -491,33 +496,26 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                if (sta && elems->ht_operation && elems->ht_cap_elem &&
                    sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
                        /* we both use HT */
-                       struct ieee80211_sta_ht_cap sta_ht_cap_new;
+                       struct ieee80211_ht_cap htcap_ie;
                        struct cfg80211_chan_def chandef;
 
                        ieee80211_ht_oper_to_chandef(channel,
                                                     elems->ht_operation,
                                                     &chandef);
 
-                       ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
-                                                         elems->ht_cap_elem,
-                                                         &sta_ht_cap_new);
+                       memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
 
                        /*
                         * fall back to HT20 if we don't use or use
                         * the other extension channel
                         */
-                       if (chandef.width != NL80211_CHAN_WIDTH_40 ||
-                           cfg80211_get_chandef_type(&chandef) !=
+                       if (cfg80211_get_chandef_type(&chandef) !=
                                                sdata->u.ibss.channel_type)
-                               sta_ht_cap_new.cap &=
-                                       ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-
-                       if (memcmp(&sta->sta.ht_cap, &sta_ht_cap_new,
-                                  sizeof(sta_ht_cap_new))) {
-                               memcpy(&sta->sta.ht_cap, &sta_ht_cap_new,
-                                      sizeof(sta_ht_cap_new));
-                               rates_updated = true;
-                       }
+                               htcap_ie.cap_info &=
+                                       cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+
+                       rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(
+                                               sdata, sband, &htcap_ie, sta);
                }
 
                if (sta && rates_updated) {
@@ -530,14 +528,14 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        }
 
        bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
-                                       channel, beacon);
+                                       channel);
        if (!bss)
                return;
 
        cbss = container_of((void *)bss, struct cfg80211_bss, priv);
 
-       /* was just updated in ieee80211_bss_info_update */
-       beacon_timestamp = cbss->tsf;
+       /* same for beacon and probe response */
+       beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
 
        /* check if we need to merge IBSS */
 
@@ -703,8 +701,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
        sdata_info(sdata,
                   "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
 
-       ieee80211_request_internal_scan(sdata,
-                       ifibss->ssid, ifibss->ssid_len, NULL);
+       ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
+                                   NULL);
 }
 
 static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -802,9 +800,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
                                        IEEE80211_SCAN_INTERVAL)) {
                sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
 
-               ieee80211_request_internal_scan(sdata,
-                               ifibss->ssid, ifibss->ssid_len,
-                               ifibss->fixed_channel ? ifibss->channel : NULL);
+               ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+                                           ifibss->ssid_len, chan);
        } else {
                int interval = IEEE80211_SCAN_INTERVAL;
 
@@ -878,14 +875,21 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        ieee80211_tx_skb(sdata, skb);
 }
 
-static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
-                                        struct ieee80211_mgmt *mgmt,
-                                        size_t len,
-                                        struct ieee80211_rx_status *rx_status)
+static
+void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
+                                   struct ieee80211_mgmt *mgmt, size_t len,
+                                   struct ieee80211_rx_status *rx_status)
 {
        size_t baselen;
        struct ieee802_11_elems elems;
 
+       BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) !=
+                    offsetof(typeof(mgmt->u.beacon), variable));
+
+       /*
+        * either beacon or probe_resp but the variable field is at the
+        * same offset
+        */
        baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
        if (baselen > len)
                return;
@@ -893,25 +897,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
        ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
                                &elems);
 
-       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
-}
-
-static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len,
-                                    struct ieee80211_rx_status *rx_status)
-{
-       size_t baselen;
-       struct ieee802_11_elems elems;
-
-       /* Process beacon from the current BSS */
-       baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
-       if (baselen > len)
-               return;
-
-       ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
-
-       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);
+       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
 }
 
 void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@ -935,12 +921,9 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                ieee80211_rx_mgmt_probe_req(sdata, skb);
                break;
        case IEEE80211_STYPE_PROBE_RESP:
-               ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
-                                            rx_status);
-               break;
        case IEEE80211_STYPE_BEACON:
-               ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
-                                        rx_status);
+               ieee80211_rx_mgmt_probe_beacon(sdata, mgmt, skb->len,
+                                              rx_status);
                break;
        case IEEE80211_STYPE_AUTH:
                ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
@@ -1118,10 +1101,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 
        mutex_unlock(&sdata->u.ibss.mtx);
 
-       mutex_lock(&sdata->local->mtx);
-       ieee80211_recalc_idle(sdata->local);
-       mutex_unlock(&sdata->local->mtx);
-
        /*
         * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
         * reserved, but an HT STA shall protect HT transmissions as though
@@ -1175,7 +1154,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
 
                if (cbss) {
                        cfg80211_unlink_bss(local->hw.wiphy, cbss);
-                       cfg80211_put_bss(cbss);
+                       cfg80211_put_bss(local->hw.wiphy, cbss);
                }
        }
 
@@ -1183,7 +1162,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        memset(ifibss->bssid, 0, ETH_ALEN);
        ifibss->ssid_len = 0;
 
-       sta_info_flush(sdata->local, sdata);
+       sta_info_flush(sdata);
 
        spin_lock_bh(&ifibss->incomplete_lock);
        while (!list_empty(&ifibss->incomplete_stations)) {
@@ -1206,6 +1185,8 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
        sdata->vif.bss_conf.ibss_joined = false;
        sdata->vif.bss_conf.ibss_creator = false;
+       sdata->vif.bss_conf.enable_beacon = false;
+       clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
                                                BSS_CHANGED_IBSS);
        synchronize_rcu();
@@ -1217,9 +1198,5 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
 
        mutex_unlock(&sdata->u.ibss.mtx);
 
-       mutex_lock(&local->mtx);
-       ieee80211_recalc_idle(sdata->local);
-       mutex_unlock(&local->mtx);
-
        return 0;
 }