mac80211: use station mutex in configuration
[pandora-kernel.git] / net / mac80211 / ibss.c
index a82f6b4..fe0e91e 100644 (file)
@@ -77,6 +77,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        struct cfg80211_bss *bss;
        u32 bss_change;
        u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+       enum nl80211_channel_type channel_type;
 
        lockdep_assert_held(&ifibss->mtx);
 
@@ -105,8 +106,16 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
 
-       local->oper_channel = chan;
-       WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
+       channel_type = ifibss->channel_type;
+       if (channel_type > NL80211_CHAN_HT20 &&
+           !cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type))
+               channel_type = NL80211_CHAN_HT20;
+       if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
+               /* can only fail due to HT40+/- mismatch */
+               channel_type = NL80211_CHAN_HT20;
+               WARN_ON(!ieee80211_set_channel_type(local, sdata,
+                                                   NL80211_CHAN_HT20));
+       }
        ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
        sband = local->hw.wiphy->bands[chan->band];
@@ -172,6 +181,19 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                memcpy(skb_put(skb, ifibss->ie_len),
                       ifibss->ie, ifibss->ie_len);
 
+       /* add HT capability and information IEs */
+       if (channel_type && sband->ht_cap.ht_supported) {
+               pos = skb_put(skb, 4 +
+                                  sizeof(struct ieee80211_ht_cap) +
+                                  sizeof(struct ieee80211_ht_info));
+               pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
+                                               sband->ht_cap.cap);
+               pos = ieee80211_ie_build_ht_info(pos,
+                                                &sband->ht_cap,
+                                                chan,
+                                                channel_type);
+       }
+
        if (local->hw.queues >= 4) {
                pos = skb_put(skb, 9);
                *pos++ = WLAN_EID_VENDOR_SPECIFIC;
@@ -195,6 +217,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        bss_change |= BSS_CHANGED_BEACON;
        bss_change |= BSS_CHANGED_BEACON_ENABLED;
        bss_change |= BSS_CHANGED_BASIC_RATES;
+       bss_change |= BSS_CHANGED_HT;
        bss_change |= BSS_CHANGED_IBSS;
        sdata->vif.bss_conf.ibss_joined = true;
        ieee80211_bss_info_change_notify(sdata, bss_change);
@@ -268,6 +291,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        u64 beacon_timestamp, rx_timestamp;
        u32 supp_rates = 0;
        enum ieee80211_band band = rx_status->band;
+       struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+       bool rates_updated = false;
 
        if (elems->ds_params && elems->ds_params_len == 1)
                freq = ieee80211_channel_to_frequency(elems->ds_params[0],
@@ -307,7 +332,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                                                prev_rates,
                                                sta->sta.supp_rates[band]);
 #endif
-                                       rate_control_rate_init(sta);
+                                       rates_updated = true;
                                }
                        } else
                                sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
@@ -318,6 +343,39 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                if (sta && elems->wmm_info)
                        set_sta_flag(sta, WLAN_STA_WME);
 
+               if (sta && elems->ht_info_elem && 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;
+                       enum nl80211_channel_type channel_type =
+                               ieee80211_ht_info_to_channel_type(
+                                                       elems->ht_info_elem);
+
+                       ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+                                                         elems->ht_cap_elem,
+                                                         &sta_ht_cap_new);
+
+                       /*
+                        * fall back to HT20 if we don't use or use
+                        * the other extension channel
+                        */
+                       if ((channel_type == NL80211_CHAN_HT40MINUS ||
+                            channel_type == NL80211_CHAN_HT40PLUS) &&
+                           channel_type != 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;
+                       }
+               }
+
+               if (sta && rates_updated)
+                       rate_control_rate_init(sta);
+
                rcu_read_unlock();
        }
 
@@ -899,10 +957,15 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        u32 changed = 0;
 
        skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom +
-                           36 /* bitrates */ +
-                           34 /* SSID */ +
-                           3  /* DS params */ +
-                           4  /* IBSS params */ +
+                           sizeof(struct ieee80211_hdr_3addr) +
+                           12 /* struct ieee80211_mgmt.u.beacon */ +
+                           2 + IEEE80211_MAX_SSID_LEN /* max SSID */ +
+                           2 + 8 /* max Supported Rates */ +
+                           3 /* max DS params */ +
+                           4 /* IBSS params */ +
+                           2 + (IEEE80211_MAX_SUPP_RATES - 8) +
+                           2 + sizeof(struct ieee80211_ht_cap) +
+                           2 + sizeof(struct ieee80211_ht_info) +
                            params->ie_len);
        if (!skb)
                return -ENOMEM;
@@ -923,13 +986,18 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        sdata->vif.bss_conf.beacon_int = params->beacon_interval;
 
        sdata->u.ibss.channel = params->channel;
+       sdata->u.ibss.channel_type = params->channel_type;
        sdata->u.ibss.fixed_channel = params->channel_fixed;
 
        /* fix ourselves to that channel now already */
        if (params->channel_fixed) {
                sdata->local->oper_channel = params->channel;
-               WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata,
-                                                   NL80211_CHAN_NO_HT));
+               if (!ieee80211_set_channel_type(sdata->local, sdata,
+                                              params->channel_type)) {
+                       mutex_unlock(&sdata->u.ibss.mtx);
+                       kfree_skb(skb);
+                       return -EINVAL;
+               }
        }
 
        if (params->ie) {