diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2008-10-14 10:58:37 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-10-31 19:00:16 -0400 |
commit | ae5eb02641233a4e9d1b92d22090f1b1afa14466 (patch) | |
tree | 117b7cb5efa3ff1cf714218098fc6db3820ed4e0 /net/mac80211/ht.c | |
parent | bda3933a8aceedd03e0dd410844bd310033ca756 (diff) |
mac80211: rewrite HT handling
The HT handling has the following deficiencies, which I've
(partially) fixed:
* it always uses the AP info even if there is no AP,
hence has no chance of working as an AP
* it pretends to be HW config, but really is per-BSS
* channel sanity checking is left to the drivers
* it generally lets the driver control too much
HT enabling is still wrong with this patch if you have more than
one virtual STA mode interface, but that never happens currently.
Once WDS, IBSS or AP/VLAN gets HT capabilities, it will also be
wrong, see the comment in ieee80211_enable_ht().
Additionally, this fixes a number of bugs:
* mac80211: ieee80211_set_disassoc doesn't notify the driver any
more since the refactoring
* iwl-agn-rs: always uses the HT capabilities from the wrong stuff
mac80211 gives it rather than the actual peer STA
* ath9k: a number of bugs resulting from the broken HT API
I'm not entirely happy with putting the HT capabilities into
struct ieee80211_sta as restricted to our own HT TX capabilities,
but I see no cleaner solution for now.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/ht.c')
-rw-r--r-- | net/mac80211/ht.c | 181 |
1 files changed, 75 insertions, 106 deletions
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index e2d121bf2745..42c3e590df98 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c | |||
@@ -20,114 +20,38 @@ | |||
20 | #include "sta_info.h" | 20 | #include "sta_info.h" |
21 | #include "wme.h" | 21 | #include "wme.h" |
22 | 22 | ||
23 | void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_ht_cap *ht_cap_ie, | 23 | void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, |
24 | struct ieee80211_ht_cap *ht_cap_ie, | ||
24 | struct ieee80211_sta_ht_cap *ht_cap) | 25 | struct ieee80211_sta_ht_cap *ht_cap) |
25 | { | 26 | { |
27 | u8 ampdu_info, tx_mcs_set_cap; | ||
28 | int i, max_tx_streams; | ||
26 | 29 | ||
27 | BUG_ON(!ht_cap); | 30 | BUG_ON(!ht_cap); |
28 | 31 | ||
29 | memset(ht_cap, 0, sizeof(*ht_cap)); | 32 | memset(ht_cap, 0, sizeof(*ht_cap)); |
30 | 33 | ||
31 | if (ht_cap_ie) { | 34 | if (!ht_cap_ie) |
32 | u8 ampdu_info = ht_cap_ie->ampdu_params_info; | 35 | return; |
33 | |||
34 | ht_cap->ht_supported = true; | ||
35 | ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info); | ||
36 | ht_cap->ampdu_factor = | ||
37 | ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; | ||
38 | ht_cap->ampdu_density = | ||
39 | (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; | ||
40 | memcpy(&ht_cap->mcs, &ht_cap_ie->mcs, sizeof(ht_cap->mcs)); | ||
41 | } else | ||
42 | ht_cap->ht_supported = false; | ||
43 | } | ||
44 | |||
45 | void ieee80211_ht_info_ie_to_ht_bss_info( | ||
46 | struct ieee80211_ht_info *ht_add_info_ie, | ||
47 | struct ieee80211_ht_bss_info *bss_info) | ||
48 | { | ||
49 | BUG_ON(!bss_info); | ||
50 | |||
51 | memset(bss_info, 0, sizeof(*bss_info)); | ||
52 | |||
53 | if (ht_add_info_ie) { | ||
54 | u16 op_mode; | ||
55 | op_mode = le16_to_cpu(ht_add_info_ie->operation_mode); | ||
56 | |||
57 | bss_info->primary_channel = ht_add_info_ie->control_chan; | ||
58 | bss_info->bss_cap = ht_add_info_ie->ht_param; | ||
59 | bss_info->bss_op_mode = (u8)(op_mode & 0xff); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * ieee80211_handle_ht should be called only after the operating band | ||
65 | * has been determined as ht configuration depends on the hw's | ||
66 | * HT abilities for a specific band. | ||
67 | */ | ||
68 | u32 ieee80211_handle_ht(struct ieee80211_local *local, | ||
69 | struct ieee80211_sta_ht_cap *req_ht_cap, | ||
70 | struct ieee80211_ht_bss_info *req_bss_cap) | ||
71 | { | ||
72 | struct ieee80211_conf *conf = &local->hw.conf; | ||
73 | struct ieee80211_supported_band *sband; | ||
74 | struct ieee80211_sta_ht_cap ht_cap; | ||
75 | struct ieee80211_ht_bss_info ht_bss_conf; | ||
76 | u32 changed = 0; | ||
77 | int i; | ||
78 | u8 max_tx_streams; | ||
79 | u8 tx_mcs_set_cap; | ||
80 | bool enable_ht = true; | ||
81 | |||
82 | sband = local->hw.wiphy->bands[conf->channel->band]; | ||
83 | |||
84 | memset(&ht_cap, 0, sizeof(ht_cap)); | ||
85 | memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info)); | ||
86 | |||
87 | /* HT is not supported */ | ||
88 | if (!sband->ht_cap.ht_supported) | ||
89 | enable_ht = false; | ||
90 | |||
91 | /* disable HT */ | ||
92 | if (!enable_ht) { | ||
93 | if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) | ||
94 | changed |= BSS_CHANGED_HT; | ||
95 | conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE; | ||
96 | conf->ht_cap.ht_supported = false; | ||
97 | return changed; | ||
98 | } | ||
99 | |||
100 | |||
101 | if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) | ||
102 | changed |= BSS_CHANGED_HT; | ||
103 | |||
104 | conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE; | ||
105 | ht_cap.ht_supported = true; | ||
106 | 36 | ||
107 | ht_cap.cap = req_ht_cap->cap & sband->ht_cap.cap; | 37 | ht_cap->ht_supported = true; |
108 | ht_cap.cap &= ~IEEE80211_HT_CAP_SM_PS; | ||
109 | ht_cap.cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS; | ||
110 | 38 | ||
111 | ht_bss_conf.primary_channel = req_bss_cap->primary_channel; | 39 | ht_cap->cap = ht_cap->cap & sband->ht_cap.cap; |
112 | ht_bss_conf.bss_cap = req_bss_cap->bss_cap; | 40 | ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS; |
113 | ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode; | 41 | ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS; |
114 | 42 | ||
115 | ht_cap.ampdu_factor = req_ht_cap->ampdu_factor; | 43 | ampdu_info = ht_cap_ie->ampdu_params_info; |
116 | ht_cap.ampdu_density = req_ht_cap->ampdu_density; | 44 | ht_cap->ampdu_factor = |
45 | ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; | ||
46 | ht_cap->ampdu_density = | ||
47 | (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; | ||
117 | 48 | ||
118 | /* own MCS TX capabilities */ | 49 | /* own MCS TX capabilities */ |
119 | tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; | 50 | tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; |
120 | 51 | ||
121 | /* | ||
122 | * configure supported Tx MCS according to requested MCS | ||
123 | * (based in most cases on Rx capabilities of peer) and self | ||
124 | * Tx MCS capabilities (as defined by low level driver HW | ||
125 | * Tx capabilities) | ||
126 | */ | ||
127 | |||
128 | /* can we TX with MCS rates? */ | 52 | /* can we TX with MCS rates? */ |
129 | if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) | 53 | if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) |
130 | goto check_changed; | 54 | return; |
131 | 55 | ||
132 | /* Counting from 0, therefore +1 */ | 56 | /* Counting from 0, therefore +1 */ |
133 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) | 57 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) |
@@ -145,29 +69,73 @@ u32 ieee80211_handle_ht(struct ieee80211_local *local, | |||
145 | * - remainder are multiple spatial streams using unequal modulation | 69 | * - remainder are multiple spatial streams using unequal modulation |
146 | */ | 70 | */ |
147 | for (i = 0; i < max_tx_streams; i++) | 71 | for (i = 0; i < max_tx_streams; i++) |
148 | ht_cap.mcs.rx_mask[i] = | 72 | ht_cap->mcs.rx_mask[i] = |
149 | sband->ht_cap.mcs.rx_mask[i] & | 73 | sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; |
150 | req_ht_cap->mcs.rx_mask[i]; | ||
151 | 74 | ||
152 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) | 75 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) |
153 | for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; | 76 | for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; |
154 | i < IEEE80211_HT_MCS_MASK_LEN; i++) | 77 | i < IEEE80211_HT_MCS_MASK_LEN; i++) |
155 | ht_cap.mcs.rx_mask[i] = | 78 | ht_cap->mcs.rx_mask[i] = |
156 | sband->ht_cap.mcs.rx_mask[i] & | 79 | sband->ht_cap.mcs.rx_mask[i] & |
157 | req_ht_cap->mcs.rx_mask[i]; | 80 | ht_cap_ie->mcs.rx_mask[i]; |
158 | 81 | ||
159 | /* handle MCS rate 32 too */ | 82 | /* handle MCS rate 32 too */ |
160 | if (sband->ht_cap.mcs.rx_mask[32/8] & | 83 | if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) |
161 | req_ht_cap->mcs.rx_mask[32/8] & 1) | 84 | ht_cap->mcs.rx_mask[32/8] |= 1; |
162 | ht_cap.mcs.rx_mask[32/8] |= 1; | 85 | } |
86 | |||
87 | /* | ||
88 | * ieee80211_enable_ht should be called only after the operating band | ||
89 | * has been determined as ht configuration depends on the hw's | ||
90 | * HT abilities for a specific band. | ||
91 | */ | ||
92 | u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, | ||
93 | struct ieee80211_ht_info *hti, | ||
94 | u16 ap_ht_cap_flags) | ||
95 | { | ||
96 | struct ieee80211_local *local = sdata->local; | ||
97 | struct ieee80211_supported_band *sband; | ||
98 | struct ieee80211_bss_ht_conf ht; | ||
99 | u32 changed = 0; | ||
100 | bool enable_ht = true, ht_changed; | ||
101 | |||
102 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | ||
103 | |||
104 | memset(&ht, 0, sizeof(ht)); | ||
105 | |||
106 | /* HT is not supported */ | ||
107 | if (!sband->ht_cap.ht_supported) | ||
108 | enable_ht = false; | ||
109 | |||
110 | /* check that channel matches the right operating channel */ | ||
111 | if (local->hw.conf.channel->center_freq != | ||
112 | ieee80211_channel_to_frequency(hti->control_chan)) | ||
113 | enable_ht = false; | ||
114 | |||
115 | /* | ||
116 | * XXX: This is totally incorrect when there are multiple virtual | ||
117 | * interfaces, needs to be fixed later. | ||
118 | */ | ||
119 | ht_changed = local->hw.conf.ht.enabled != enable_ht; | ||
120 | local->hw.conf.ht.enabled = enable_ht; | ||
121 | if (ht_changed) | ||
122 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT); | ||
123 | |||
124 | /* disable HT */ | ||
125 | if (!enable_ht) | ||
126 | return 0; | ||
127 | ht.secondary_channel_offset = | ||
128 | hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; | ||
129 | ht.width_40_ok = | ||
130 | !(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) && | ||
131 | (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && | ||
132 | (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY); | ||
133 | ht.operation_mode = le16_to_cpu(hti->operation_mode); | ||
163 | 134 | ||
164 | check_changed: | ||
165 | /* if bss configuration changed store the new one */ | 135 | /* if bss configuration changed store the new one */ |
166 | if (memcmp(&conf->ht_cap, &ht_cap, sizeof(ht_cap)) || | 136 | if (memcmp(&sdata->vif.bss_conf.ht, &ht, sizeof(ht))) { |
167 | memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) { | ||
168 | changed |= BSS_CHANGED_HT; | 137 | changed |= BSS_CHANGED_HT; |
169 | memcpy(&conf->ht_cap, &ht_cap, sizeof(ht_cap)); | 138 | sdata->vif.bss_conf.ht = ht; |
170 | memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf)); | ||
171 | } | 139 | } |
172 | 140 | ||
173 | return changed; | 141 | return changed; |
@@ -900,8 +868,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
900 | /* sanity check for incoming parameters: | 868 | /* sanity check for incoming parameters: |
901 | * check if configuration can support the BA policy | 869 | * check if configuration can support the BA policy |
902 | * and if buffer size does not exceeds max value */ | 870 | * and if buffer size does not exceeds max value */ |
871 | /* XXX: check own ht delayed BA capability?? */ | ||
903 | if (((ba_policy != 1) | 872 | if (((ba_policy != 1) |
904 | && (!(conf->ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) | 873 | && (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) |
905 | || (buf_size > IEEE80211_MAX_AMPDU_BUF)) { | 874 | || (buf_size > IEEE80211_MAX_AMPDU_BUF)) { |
906 | status = WLAN_STATUS_INVALID_QOS_PARAM; | 875 | status = WLAN_STATUS_INVALID_QOS_PARAM; |
907 | #ifdef CONFIG_MAC80211_HT_DEBUG | 876 | #ifdef CONFIG_MAC80211_HT_DEBUG |