ht.c 7.3 KB
Newer Older
1 2 3 4
/*
 * HT handling
 *
 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
5 6
 * Copyright 2002-2005, Instant802 Networks, Inc.
 * Copyright 2005-2006, Devicescape Software, Inc.
7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
 * Copyright 2007-2008, Intel Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/ieee80211.h>
#include <net/wireless.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"

J
Johannes Berg 已提交
21 22
void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
				       struct ieee80211_ht_cap *ht_cap_ie,
J
Johannes Berg 已提交
23
				       struct ieee80211_sta_ht_cap *ht_cap)
24
{
J
Johannes Berg 已提交
25 26
	u8 ampdu_info, tx_mcs_set_cap;
	int i, max_tx_streams;
27

J
Johannes Berg 已提交
28
	BUG_ON(!ht_cap);
29

J
Johannes Berg 已提交
30
	memset(ht_cap, 0, sizeof(*ht_cap));
31

J
Johannes Berg 已提交
32 33
	if (!ht_cap_ie)
		return;
J
Johannes Berg 已提交
34

J
Johannes Berg 已提交
35
	ht_cap->ht_supported = true;
J
Johannes Berg 已提交
36

37
	ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & sband->ht_cap.cap;
J
Johannes Berg 已提交
38 39
	ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS;
	ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS;
J
Johannes Berg 已提交
40

J
Johannes Berg 已提交
41 42 43 44 45
	ampdu_info = ht_cap_ie->ampdu_params_info;
	ht_cap->ampdu_factor =
		ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
	ht_cap->ampdu_density =
		(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
46

J
Johannes Berg 已提交
47 48 49 50 51
	/* own MCS TX capabilities */
	tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;

	/* can we TX with MCS rates? */
	if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
J
Johannes Berg 已提交
52
		return;
J
Johannes Berg 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

	/* Counting from 0, therefore +1 */
	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
		max_tx_streams =
			((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
				>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
	else
		max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS;

	/*
	 * 802.11n D5.0 20.3.5 / 20.6 says:
	 * - indices 0 to 7 and 32 are single spatial stream
	 * - 8 to 31 are multiple spatial streams using equal modulation
	 *   [8..15 for two streams, 16..23 for three and 24..31 for four]
	 * - remainder are multiple spatial streams using unequal modulation
	 */
	for (i = 0; i < max_tx_streams; i++)
J
Johannes Berg 已提交
70 71
		ht_cap->mcs.rx_mask[i] =
			sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
J
Johannes Berg 已提交
72 73 74 75

	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
		for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
		     i < IEEE80211_HT_MCS_MASK_LEN; i++)
J
Johannes Berg 已提交
76
			ht_cap->mcs.rx_mask[i] =
J
Johannes Berg 已提交
77
				sband->ht_cap.mcs.rx_mask[i] &
J
Johannes Berg 已提交
78
					ht_cap_ie->mcs.rx_mask[i];
J
Johannes Berg 已提交
79 80

	/* handle MCS rate 32 too */
J
Johannes Berg 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
		ht_cap->mcs.rx_mask[32/8] |= 1;
}

/*
 * 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.
 */
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_bss_ht_conf ht;
	u32 changed = 0;
	bool enable_ht = true, ht_changed;
S
Sujith 已提交
99
	enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
J
Johannes Berg 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112 113

	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;

S
Sujith 已提交
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
	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;
			}
		}
	}

131
	ht_changed = conf_is_ht(&local->hw.conf) != enable_ht ||
132
		     channel_type != local->hw.conf.channel_type;
S
Sujith 已提交
133 134 135

	local->oper_channel_type = channel_type;

136 137 138 139
	if (ht_changed) {
                /* channel_type change automatically detected */
		ieee80211_hw_config(local, 0);
        }
J
Johannes Berg 已提交
140 141 142 143

	/* disable HT */
	if (!enable_ht)
		return 0;
S
Sujith 已提交
144

J
Johannes Berg 已提交
145
	ht.operation_mode = le16_to_cpu(hti->operation_mode);
J
Johannes Berg 已提交
146 147

	/* if bss configuration changed store the new one */
J
Johannes Berg 已提交
148
	if (memcmp(&sdata->vif.bss_conf.ht, &ht, sizeof(ht))) {
J
Johannes Berg 已提交
149
		changed |= BSS_CHANGED_HT;
J
Johannes Berg 已提交
150
		sdata->vif.bss_conf.ht = ht;
J
Johannes Berg 已提交
151 152 153
	}

	return changed;
154 155
}

J
Johannes Berg 已提交
156
void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr)
157 158
{
	struct ieee80211_local *local = sdata->local;
J
Johannes Berg 已提交
159
	int i;
160

J
Johannes Berg 已提交
161 162 163 164 165 166
	for (i = 0; i <  STA_TID_NUM; i++) {
		ieee80211_stop_tx_ba_session(&local->hw, addr, i,
					     WLAN_BACK_INITIATOR);
		ieee80211_sta_stop_rx_ba_session(sdata, addr, i,
						 WLAN_BACK_RECIPIENT,
						 WLAN_REASON_QSTA_LEAVE_QBSS);
167 168 169
	}
}

J
Johannes Berg 已提交
170 171 172
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
			  const u8 *da, u16 tid,
			  u16 initiator, u16 reason_code)
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
	struct sk_buff *skb;
	struct ieee80211_mgmt *mgmt;
	u16 params;

	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);

	if (!skb) {
		printk(KERN_ERR "%s: failed to allocate buffer "
					"for delba frame\n", sdata->dev->name);
		return;
	}

	skb_reserve(skb, local->hw.extra_tx_headroom);
	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
	memset(mgmt, 0, 24);
	memcpy(mgmt->da, da, ETH_ALEN);
	memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
193
	if (sdata->vif.type == NL80211_IFTYPE_AP)
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
		memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
	else
		memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
					  IEEE80211_STYPE_ACTION);

	skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));

	mgmt->u.action.category = WLAN_CATEGORY_BACK;
	mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
	params = (u16)(initiator << 11); 	/* bit 11 initiator */
	params |= (u16)(tid << 12); 		/* bit 15:12 TID number */

	mgmt->u.action.u.delba.params = cpu_to_le16(params);
	mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);

210
	ieee80211_tx_skb(sdata, skb, 1);
211 212
}

213 214 215 216 217 218 219 220 221 222 223 224 225 226
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
			     struct sta_info *sta,
			     struct ieee80211_mgmt *mgmt, size_t len)
{
	struct ieee80211_local *local = sdata->local;
	u16 tid, params;
	u16 initiator;

	params = le16_to_cpu(mgmt->u.action.u.delba.params);
	tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
	initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11;

#ifdef CONFIG_MAC80211_HT_DEBUG
	if (net_ratelimit())
227 228
		printk(KERN_DEBUG "delba from %pM (%s) tid %d reason code %d\n",
			mgmt->sa, initiator ? "initiator" : "recipient", tid,
229 230 231 232
			mgmt->u.action.u.delba.reason_code);
#endif /* CONFIG_MAC80211_HT_DEBUG */

	if (initiator == WLAN_BACK_INITIATOR)
233
		ieee80211_sta_stop_rx_ba_session(sdata, sta->sta.addr, tid,
234 235 236 237 238 239
						 WLAN_BACK_INITIATOR, 0);
	else { /* WLAN_BACK_RECIPIENT */
		spin_lock_bh(&sta->lock);
		sta->ampdu_mlme.tid_state_tx[tid] =
				HT_AGG_STATE_OPERATIONAL;
		spin_unlock_bh(&sta->lock);
240
		ieee80211_stop_tx_ba_session(&local->hw, sta->sta.addr, tid,
241 242 243
					     WLAN_BACK_RECIPIENT);
	}
}