--- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1085,6 +1085,7 @@ struct cfg80211_ibss_params { u8 *ssid; u8 *bssid; struct ieee80211_channel *channel; + enum nl80211_channel_type channel_type; u8 *ie; u8 ssid_len, ie_len; u16 beacon_interval; @@ -2584,6 +2585,12 @@ struct cfg80211_bss *cfg80211_get_bss(st const u8 *bssid, const u8 *ssid, size_t ssid_len, u16 capa_mask, u16 capa_val); +struct cfg80211_bss *cfg80211_get_bss_ht(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *ssid, size_t ssid_len, + u16 capa_mask, u16 capa_val, + enum nl80211_channel_type channel_type); static inline struct cfg80211_bss * cfg80211_get_ibss(struct wiphy *wiphy, struct ieee80211_channel *channel, --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4470,13 +4470,41 @@ static int nl80211_join_ibss(struct sk_b ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - ibss.channel = ieee80211_get_channel(wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + enum nl80211_channel_type channel_type; + + channel_type = nla_get_u32( + info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + if (channel_type != NL80211_CHAN_NO_HT && + channel_type != NL80211_CHAN_HT20 && + channel_type != NL80211_CHAN_HT40PLUS && + channel_type != NL80211_CHAN_HT40MINUS) + return -EINVAL; + ibss.channel_type = channel_type; + } else { + ibss.channel_type = NL80211_CHAN_NO_HT; + } + + ibss.channel = rdev_freq_to_chan(rdev, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), + ibss.channel_type); if (!ibss.channel || + ibss.channel->flags & IEEE80211_CHAN_PASSIVE_SCAN || ibss.channel->flags & IEEE80211_CHAN_NO_IBSS || - ibss.channel->flags & IEEE80211_CHAN_DISABLED) + ibss.channel->flags & IEEE80211_CHAN_RADAR) return -EINVAL; + /* Both channels should be able to initiate communication */ + if ((ibss.channel_type == NL80211_CHAN_HT40PLUS || + ibss.channel_type == NL80211_CHAN_HT40MINUS) && + !can_beacon_sec_chan(&rdev->wiphy, ibss.channel, + ibss.channel_type)) { + printk(KERN_DEBUG + "cfg80211: Secondary channel not " + "allowed to initiate communication\n"); + return -EINVAL; + } + ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -44,7 +44,7 @@ rdev_freq_to_chan(struct cfg80211_regist return chan; } -static bool can_beacon_sec_chan(struct wiphy *wiphy, +bool can_beacon_sec_chan(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { @@ -75,6 +75,7 @@ static bool can_beacon_sec_chan(struct w return true; } +EXPORT_SYMBOL(can_beacon_sec_chan); int cfg80211_set_freq(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, int freq, --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -441,6 +441,9 @@ cfg80211_can_add_interface(struct cfg802 struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type channel_type); +bool can_beacon_sec_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type); int cfg80211_set_freq(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, int freq, enum nl80211_channel_type channel_type); --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -365,6 +365,19 @@ struct cfg80211_bss *cfg80211_get_bss(st const u8 *ssid, size_t ssid_len, u16 capa_mask, u16 capa_val) { + /* call HT version with no HT requirements */ + return cfg80211_get_bss_ht(wiphy, channel, bssid, ssid, ssid_len, + capa_mask, capa_val, NL80211_CHAN_NO_HT); +} +EXPORT_SYMBOL(cfg80211_get_bss); + +struct cfg80211_bss *cfg80211_get_bss_ht(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *ssid, size_t ssid_len, + u16 capa_mask, u16 capa_val, + enum nl80211_channel_type require_ht) +{ struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; unsigned long now = jiffies; @@ -374,8 +387,26 @@ struct cfg80211_bss *cfg80211_get_bss(st list_for_each_entry(bss, &dev->bss_list, list) { if ((bss->pub.capability & capa_mask) != capa_val) continue; - if (channel && bss->pub.channel != channel) - continue; + if (channel) { + if (bss->pub.channel != channel) + continue; + if (require_ht != NL80211_CHAN_NO_HT) { + struct ieee80211_ht_info *ht_info; + ht_info = (struct ieee80211_ht_info *) + ieee80211_bss_get_ie(&bss->pub, + WLAN_EID_HT_INFORMATION); + if (!ht_info) + continue; + if (require_ht == NL80211_CHAN_HT40MINUS && + !(ht_info->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_BELOW)) + continue; + if (require_ht == NL80211_CHAN_HT40PLUS && + !(ht_info->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_ABOVE)) + continue; + } + } /* Don't get expired BSS structs */ if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) && !atomic_read(&bss->hold)) @@ -392,7 +423,7 @@ struct cfg80211_bss *cfg80211_get_bss(st return NULL; return &res->pub; } -EXPORT_SYMBOL(cfg80211_get_bss); +EXPORT_SYMBOL(cfg80211_get_bss_ht); struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, struct ieee80211_channel *channel, --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -464,6 +464,7 @@ struct ieee80211_if_ibss { u8 ssid_len, ie_len; u8 *ie; struct ieee80211_channel *channel; + enum nl80211_channel_type channel_type; unsigned long ibss_join_req; /* probe response/beacon for IBSS */ @@ -1089,6 +1090,7 @@ void ieee80211_ibss_notify_scan_complete void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata); struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, u8 *bssid, u8 *addr, u32 supp_rates, + struct ieee80211_ht_cap *ht_cap, gfp_t gfp); int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, struct cfg80211_ibss_params *params); @@ -1343,6 +1345,12 @@ void ieee80211_recalc_smps(struct ieee80 size_t ieee80211_ie_split(const u8 *ies, size_t ielen, const u8 *ids, int n_ids, size_t offset); size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); +u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_supported_band *sband, + u16 cap); +u8 *ieee80211_ie_build_ht_info(u8 *pos, + struct ieee80211_sta_ht_cap *ht_cap, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type); /* internal work items */ void ieee80211_work_init(struct ieee80211_local *local); @@ -1371,6 +1379,8 @@ ieee80211_get_channel_mode(struct ieee80 bool ieee80211_set_channel_type(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum nl80211_channel_type chantype); +enum nl80211_channel_type ieee80211_ht_info_to_channel_type( + struct ieee80211_ht_info *ht_info); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -841,23 +841,8 @@ int ieee80211_build_preq_ies(struct ieee offset = noffset; } - if (sband->ht_cap.ht_supported) { - u16 cap = sband->ht_cap.cap; - __le16 tmp; - - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - memset(pos, 0, sizeof(struct ieee80211_ht_cap)); - tmp = cpu_to_le16(cap); - memcpy(pos, &tmp, sizeof(u16)); - pos += sizeof(u16); - *pos++ = sband->ht_cap.ampdu_factor | - (sband->ht_cap.ampdu_density << - IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); - memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); - pos += sizeof(sband->ht_cap.mcs); - pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ - } + if (sband->ht_cap.ht_supported) + pos = ieee80211_ie_build_ht_cap(pos, sband, sband->ht_cap.cap); /* * If adding more here, adjust code in main.c @@ -1381,3 +1366,100 @@ void ieee80211_disable_rssi_reports(stru _ieee80211_enable_rssi_reports(sdata, 0, 0); } EXPORT_SYMBOL(ieee80211_disable_rssi_reports); + +u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_supported_band *sband, + u16 cap) +{ + __le16 tmp; + + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + memset(pos, 0, sizeof(struct ieee80211_ht_cap)); + + /* capability flags */ + tmp = cpu_to_le16(cap); + memcpy(pos, &tmp, sizeof(u16)); + pos += sizeof(u16); + + /* AMPDU parameters */ + *pos++ = sband->ht_cap.ampdu_factor | + (sband->ht_cap.ampdu_density << + IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); + + /* MCS set */ + memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); + pos += sizeof(sband->ht_cap.mcs); + + /* extended capabilities */ + pos += sizeof(__le16); + + /* BF capabilities */ + pos += sizeof(__le32); + + /* antenna selection */ + pos += sizeof(u8); + + return pos; +} + +u8 *ieee80211_ie_build_ht_info(u8 *pos, + struct ieee80211_sta_ht_cap *ht_cap, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type) +{ + struct ieee80211_ht_info *ht_info; + /* Build HT Information */ + *pos++ = WLAN_EID_HT_INFORMATION; + *pos++ = sizeof(struct ieee80211_ht_info); + ht_info = (struct ieee80211_ht_info *)pos; + ht_info->control_chan = + ieee80211_frequency_to_channel(channel->center_freq); + switch (channel_type) { + case NL80211_CHAN_HT40MINUS: + ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case NL80211_CHAN_HT40PLUS: + ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + break; + case NL80211_CHAN_HT20: + default: + ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + } + if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) + ht_info->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; + ht_info->operation_mode = 0x0000; + ht_info->stbc_param = 0x0000; + + /* It seems that Basic MCS set and Supported MCS set + are identical for the first 10 bytes */ + memset(&ht_info->basic_set, 0, 16); + memcpy(&ht_info->basic_set, &ht_cap->mcs, 10); + + return pos + sizeof(struct ieee80211_ht_info); +} + +enum nl80211_channel_type ieee80211_ht_info_to_channel_type( + struct ieee80211_ht_info *ht_info) +{ + enum nl80211_channel_type channel_type; + + if (!ht_info) + return NL80211_CHAN_NO_HT; + + switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + channel_type = NL80211_CHAN_HT20; + break; + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + channel_type = NL80211_CHAN_HT40PLUS; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + channel_type = NL80211_CHAN_HT40MINUS; + break; + default: + channel_type = NL80211_CHAN_NO_HT; + } + + return channel_type; +} --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -118,7 +118,6 @@ static void ieee80211_add_ht_ie(struct s u8 *pos; u32 flags = channel->flags; u16 cap = sband->ht_cap.cap; - __le16 tmp; if (!sband->ht_cap.ht_supported) return; @@ -169,34 +168,8 @@ static void ieee80211_add_ht_ie(struct s } /* reserve and fill IE */ - pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - memset(pos, 0, sizeof(struct ieee80211_ht_cap)); - - /* capability flags */ - tmp = cpu_to_le16(cap); - memcpy(pos, &tmp, sizeof(u16)); - pos += sizeof(u16); - - /* AMPDU parameters */ - *pos++ = sband->ht_cap.ampdu_factor | - (sband->ht_cap.ampdu_density << - IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); - - /* MCS set */ - memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); - pos += sizeof(sband->ht_cap.mcs); - - /* extended capabilities */ - pos += sizeof(__le16); - - /* BF capabilities */ - pos += sizeof(__le32); - - /* antenna selection */ - pos += sizeof(u8); + ieee80211_ie_build_ht_cap(pos, sband, cap); } static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -84,6 +84,8 @@ static void ieee80211_send_addba_request memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); @@ -400,7 +402,8 @@ int ieee80211_start_tx_ba_session(struct if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP && - sdata->vif.type != NL80211_IFTYPE_WDS) + sdata->vif.type != NL80211_IFTYPE_WDS && + sdata->vif.type != NL80211_IFTYPE_ADHOC) return -EINVAL; if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) { --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -203,6 +203,8 @@ void ieee80211_send_delba(struct ieee802 memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -35,6 +35,76 @@ #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 +static bool ieee80211_can_use_ext_chan(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type) +{ + /* check if we are legally allowed to use HT extension channel */ + if ((channel_type == NL80211_CHAN_HT40PLUS) || + (channel_type == NL80211_CHAN_HT40MINUS)) { + int sec_freq = channel->center_freq + + (channel_type == NL80211_CHAN_HT40PLUS ? 20 : -20); + struct ieee80211_channel *sec_chan = + ieee80211_get_channel(sdata->wdev.wiphy, sec_freq); + if (!sec_chan || sec_chan->flags & (IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_RADAR)) { + return false; + } + } + return true; +} + +static void ieee80211_update_ht_elems(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + struct ieee80211_ht_info *ht_info) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband = + local->hw.wiphy->bands[local->oper_channel->band]; + enum nl80211_channel_type channel_type = + ieee80211_ht_info_to_channel_type(ht_info); + + if (!ieee80211_can_use_ext_chan(sdata, local->oper_channel, channel_type)) + channel_type = NL80211_CHAN_HT20; + + if (channel_type != local->_oper_channel_type) { + struct sk_buff *skb = rcu_dereference_protected( + sdata->u.ibss.presp, + lockdep_is_held(&ifibss->mtx)); + struct sk_buff *nskb; + u8 *ht_ie; + + /* update HT IE. If not yet existing, create one */ + nskb = skb_copy(skb, GFP_ATOMIC); + ht_ie = (u8 *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, + (const u8 *)(nskb->data + 24 + + sizeof(mgmt->u.beacon)), + nskb->len - 24 - + sizeof(mgmt->u.beacon)); + if (!ht_ie) + ht_ie = skb_put(nskb, 4 + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee80211_ht_info)); + + ht_ie = ieee80211_ie_build_ht_cap(ht_ie, sband, + sband->ht_cap.cap); + ht_ie = ieee80211_ie_build_ht_info(ht_ie, &sband->ht_cap, + local->oper_channel, channel_type); + rcu_assign_pointer(sdata->u.ibss.presp, nskb); + kfree_skb(skb); + + if(!ieee80211_set_channel_type(local, sdata, channel_type)) { + channel_type = NL80211_CHAN_HT20; + WARN_ON(!ieee80211_set_channel_type(local, sdata, + channel_type)); + } + + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + } + +} static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, @@ -64,6 +134,7 @@ static void ieee80211_rx_mgmt_auth_ibss( static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, const u8 *bssid, const int beacon_int, struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, const u32 basic_rates, const u16 capability, u64 tsf) { @@ -104,8 +175,17 @@ static void __ieee80211_sta_join_ibss(st sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; + /* entering a legacy IBSS. Use given HT configuration. */ + if (channel_type == NL80211_CHAN_NO_HT) + channel_type = ifibss->channel_type; local->oper_channel = chan; - WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); + + /* if phy is on a different extension channel, setting ht40 will fail */ + if (!ieee80211_set_channel_type(local, sdata, channel_type)) { + channel_type = NL80211_CHAN_HT20; + WARN_ON(!ieee80211_set_channel_type(local, sdata, + channel_type)); + } ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); sband = local->hw.wiphy->bands[chan->band]; @@ -171,6 +251,18 @@ static void __ieee80211_sta_join_ibss(st memcpy(skb_put(skb, ifibss->ie_len), ifibss->ie, ifibss->ie_len); + /* add HT capability and information IEs */ + if (channel_type != NL80211_CHAN_NO_HT && 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, 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; @@ -219,6 +311,8 @@ static void ieee80211_sta_join_ibss(stru u32 basic_rates; int i, j; u16 beacon_int = cbss->beacon_interval; + const u8 *ht_info_ie; + enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; lockdep_assert_held(&sdata->u.ibss.mtx); @@ -242,9 +336,23 @@ static void ieee80211_sta_join_ibss(stru } } + ht_info_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_INFORMATION); + if (ht_info_ie) + channel_type = ieee80211_ht_info_to_channel_type( + (struct ieee80211_ht_info *) (ht_info_ie + 2)); + + if (!ieee80211_can_use_ext_chan(sdata, cbss->channel, channel_type)) { + channel_type = NL80211_CHAN_HT20; +#ifdef CONFIG_MAC80211_IBSS_DEBUG + printk(KERN_DEBUG "%s: IBSS not allowed on secondary channel\n", + sdata->name); +#endif + } + __ieee80211_sta_join_ibss(sdata, cbss->bssid, beacon_int, cbss->channel, + channel_type, basic_rates, cbss->capability, cbss->tsf); @@ -310,11 +418,24 @@ static void ieee80211_rx_bss_info(struct } else sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates, - GFP_ATOMIC); + elems->ht_cap_elem, GFP_ATOMIC); } - if (sta && elems->wmm_info) - set_sta_flags(sta, WLAN_STA_WME); + if (sta) { + if (elems->wmm_info) + set_sta_flags(sta, WLAN_STA_WME); + + /* remote station uses ht */ + if (elems->ht_info_elem) { + ieee80211_update_ht_elems(sdata, mgmt, + elems->ht_info_elem); + ieee80211_ht_cap_ie_to_sta_ht_cap( + local->hw.wiphy->bands[ + local->oper_channel->band], + elems->ht_cap_elem, + &sta->sta.ht_cap); + } + } rcu_read_unlock(); } @@ -404,7 +525,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sta_join_ibss(sdata, bss); supp_rates = ieee80211_sta_get_rates(local, elems, band); ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, - supp_rates, GFP_KERNEL); + supp_rates, elems->ht_cap_elem, GFP_KERNEL); } put_bss: @@ -417,7 +538,8 @@ static void ieee80211_rx_bss_info(struct * must be callable in atomic context. */ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, - u8 *bssid,u8 *addr, u32 supp_rates, + u8 *bssid, u8 *addr, u32 supp_rates, + struct ieee80211_ht_cap *ht_cap, gfp_t gfp) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; @@ -458,6 +580,11 @@ struct sta_info *ieee80211_ibss_add_sta( sta->sta.supp_rates[band] = supp_rates | ieee80211_mandatory_rates(local, band); + /* fill in ht rates */ + if (ht_cap) + ieee80211_ht_cap_ie_to_sta_ht_cap(local->hw.wiphy->bands[band], + ht_cap, &sta->sta.ht_cap); + rate_control_rate_init(sta); /* If it fails, maybe we raced another insertion? */ @@ -556,8 +683,8 @@ static void ieee80211_sta_create_ibss(st sdata->drop_unencrypted = 0; __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, - ifibss->channel, ifibss->basic_rates, - capability, 0); + ifibss->channel, ifibss->channel_type, + ifibss->basic_rates, capability, 0); } /* @@ -594,10 +721,10 @@ static void ieee80211_sta_find_ibss(stru chan = ifibss->channel; if (!is_zero_ether_addr(ifibss->bssid)) bssid = ifibss->bssid; - cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid, + cbss = cfg80211_get_bss_ht(local->hw.wiphy, chan, bssid, ifibss->ssid, ifibss->ssid_len, WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY, - capability); + capability, ifibss->channel_type); if (cbss) { struct ieee80211_bss *bss; @@ -896,10 +1023,15 @@ int ieee80211_ibss_join(struct ieee80211 struct sk_buff *skb; 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; @@ -920,13 +1052,15 @@ int ieee80211_ibss_join(struct ieee80211 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)) + return -EINVAL; } if (params->ie) { --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2161,7 +2161,8 @@ ieee80211_rx_h_action(struct ieee80211_r if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP && - sdata->vif.type != NL80211_IFTYPE_WDS) + sdata->vif.type != NL80211_IFTYPE_WDS && + sdata->vif.type != NL80211_IFTYPE_ADHOC) break; /* verify action_code is present */ @@ -2696,7 +2697,8 @@ static int prepare_for_handlers(struct i else rate_idx = status->rate_idx; rx->sta = ieee80211_ibss_add_sta(sdata, bssid, - hdr->addr2, BIT(rate_idx), GFP_ATOMIC); + hdr->addr2, BIT(rate_idx), NULL, + GFP_ATOMIC); } break; case NL80211_IFTYPE_MESH_POINT: --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -186,6 +186,8 @@ static void ieee80211_send_addba_resp(st memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_WDS) memcpy(mgmt->bssid, da, ETH_ALEN); + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);