|
@@ -80,6 +80,89 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
|
|
return count;
|
|
return count;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * ieee80211_enable_ht should be called only after the operating band
|
|
|
|
+ * has been determined as ht configuration depends on the hw's
|
|
|
|
+ * HT abilities for a specific band.
|
|
|
|
+ */
|
|
|
|
+static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
|
|
|
|
+ struct ieee80211_ht_info *hti,
|
|
|
|
+ u16 ap_ht_cap_flags)
|
|
|
|
+{
|
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
|
+ struct ieee80211_supported_band *sband;
|
|
|
|
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
+ struct ieee80211_bss_ht_conf ht;
|
|
|
|
+ struct sta_info *sta;
|
|
|
|
+ u32 changed = 0;
|
|
|
|
+ bool enable_ht = true, ht_changed;
|
|
|
|
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
|
|
|
|
+
|
|
|
|
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
|
|
|
|
+
|
|
|
|
+ memset(&ht, 0, sizeof(ht));
|
|
|
|
+
|
|
|
|
+ /* HT is not supported */
|
|
|
|
+ if (!sband->ht_cap.ht_supported)
|
|
|
|
+ enable_ht = false;
|
|
|
|
+
|
|
|
|
+ /* check that channel matches the right operating channel */
|
|
|
|
+ if (local->hw.conf.channel->center_freq !=
|
|
|
|
+ ieee80211_channel_to_frequency(hti->control_chan))
|
|
|
|
+ enable_ht = false;
|
|
|
|
+
|
|
|
|
+ if (enable_ht) {
|
|
|
|
+ channel_type = NL80211_CHAN_HT20;
|
|
|
|
+
|
|
|
|
+ if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
|
|
|
|
+ (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
|
|
|
|
+ (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
|
|
|
|
+ switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ht_changed = conf_is_ht(&local->hw.conf) != enable_ht ||
|
|
|
|
+ channel_type != local->hw.conf.channel_type;
|
|
|
|
+
|
|
|
|
+ local->oper_channel_type = channel_type;
|
|
|
|
+
|
|
|
|
+ if (ht_changed) {
|
|
|
|
+ /* channel_type change automatically detected */
|
|
|
|
+ ieee80211_hw_config(local, 0);
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+
|
|
|
|
+ sta = sta_info_get(local, ifmgd->bssid);
|
|
|
|
+ if (sta)
|
|
|
|
+ rate_control_rate_update(local, sband, sta,
|
|
|
|
+ IEEE80211_RC_HT_CHANGED);
|
|
|
|
+
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* disable HT */
|
|
|
|
+ if (!enable_ht)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ ht.operation_mode = le16_to_cpu(hti->operation_mode);
|
|
|
|
+
|
|
|
|
+ /* if bss configuration changed store the new one */
|
|
|
|
+ if (memcmp(&sdata->vif.bss_conf.ht, &ht, sizeof(ht))) {
|
|
|
|
+ changed |= BSS_CHANGED_HT;
|
|
|
|
+ sdata->vif.bss_conf.ht = ht;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return changed;
|
|
|
|
+}
|
|
|
|
+
|
|
/* frame sending functions */
|
|
/* frame sending functions */
|
|
|
|
|
|
static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
|
static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|