Merge tag 'random_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / vht.c
1 /*
2  * VHT handling
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8
9 #include <linux/ieee80211.h>
10 #include <linux/export.h>
11 #include <net/mac80211.h>
12 #include "ieee80211_i.h"
13 #include "rate.h"
14
15
16 void
17 ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
18                                     struct ieee80211_supported_band *sband,
19                                     const struct ieee80211_vht_cap *vht_cap_ie,
20                                     struct sta_info *sta)
21 {
22         struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap;
23
24         memset(vht_cap, 0, sizeof(*vht_cap));
25
26         if (!sta->sta.ht_cap.ht_supported)
27                 return;
28
29         if (!vht_cap_ie || !sband->vht_cap.vht_supported)
30                 return;
31
32         /* A VHT STA must support 40 MHz */
33         if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
34                 return;
35
36         vht_cap->vht_supported = true;
37
38         vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info);
39
40         /* Copy peer MCS info, the driver might need them. */
41         memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,
42                sizeof(struct ieee80211_vht_mcs_info));
43
44         switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
45         case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
46         case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
47                 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
48                 break;
49         default:
50                 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
51         }
52
53         sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
54 }
55
56 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
57 {
58         struct ieee80211_sub_if_data *sdata = sta->sdata;
59         u32 cap = sta->sta.vht_cap.cap;
60         enum ieee80211_sta_rx_bandwidth bw;
61
62         if (!sta->sta.vht_cap.vht_supported) {
63                 bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
64                                 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
65                 goto check_max;
66         }
67
68         switch (sdata->vif.bss_conf.chandef.width) {
69         default:
70                 WARN_ON_ONCE(1);
71                 /* fall through */
72         case NL80211_CHAN_WIDTH_20_NOHT:
73         case NL80211_CHAN_WIDTH_20:
74         case NL80211_CHAN_WIDTH_40:
75                 bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
76                                 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
77                 break;
78         case NL80211_CHAN_WIDTH_160:
79                 if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
80                                 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) {
81                         bw = IEEE80211_STA_RX_BW_160;
82                         break;
83                 }
84                 /* fall through */
85         case NL80211_CHAN_WIDTH_80P80:
86                 if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
87                                 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
88                         bw = IEEE80211_STA_RX_BW_160;
89                         break;
90                 }
91                 /* fall through */
92         case NL80211_CHAN_WIDTH_80:
93                 bw = IEEE80211_STA_RX_BW_80;
94         }
95
96  check_max:
97         if (bw > sta->cur_max_bandwidth)
98                 bw = sta->cur_max_bandwidth;
99         return bw;
100 }
101
102 void ieee80211_sta_set_rx_nss(struct sta_info *sta)
103 {
104         u8 ht_rx_nss = 0, vht_rx_nss = 0;
105
106         /* if we received a notification already don't overwrite it */
107         if (sta->sta.rx_nss)
108                 return;
109
110         if (sta->sta.ht_cap.ht_supported) {
111                 if (sta->sta.ht_cap.mcs.rx_mask[0])
112                         ht_rx_nss++;
113                 if (sta->sta.ht_cap.mcs.rx_mask[1])
114                         ht_rx_nss++;
115                 if (sta->sta.ht_cap.mcs.rx_mask[2])
116                         ht_rx_nss++;
117                 if (sta->sta.ht_cap.mcs.rx_mask[3])
118                         ht_rx_nss++;
119                 /* FIXME: consider rx_highest? */
120         }
121
122         if (sta->sta.vht_cap.vht_supported) {
123                 int i;
124                 u16 rx_mcs_map;
125
126                 rx_mcs_map = le16_to_cpu(sta->sta.vht_cap.vht_mcs.rx_mcs_map);
127
128                 for (i = 7; i >= 0; i--) {
129                         u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
130
131                         if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
132                                 vht_rx_nss = i + 1;
133                                 break;
134                         }
135                 }
136                 /* FIXME: consider rx_highest? */
137         }
138
139         ht_rx_nss = max(ht_rx_nss, vht_rx_nss);
140         sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss);
141 }
142
143 void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
144                                  struct sta_info *sta, u8 opmode,
145                                  enum ieee80211_band band, bool nss_only)
146 {
147         struct ieee80211_local *local = sdata->local;
148         struct ieee80211_supported_band *sband;
149         enum ieee80211_sta_rx_bandwidth new_bw;
150         u32 changed = 0;
151         u8 nss;
152
153         sband = local->hw.wiphy->bands[band];
154
155         /* ignore - no support for BF yet */
156         if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
157                 return;
158
159         nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
160         nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
161         nss += 1;
162
163         if (sta->sta.rx_nss != nss) {
164                 sta->sta.rx_nss = nss;
165                 changed |= IEEE80211_RC_NSS_CHANGED;
166         }
167
168         if (nss_only)
169                 goto change;
170
171         switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
172         case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
173                 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20;
174                 break;
175         case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ:
176                 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40;
177                 break;
178         case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ:
179                 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
180                 break;
181         case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ:
182                 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
183                 break;
184         }
185
186         new_bw = ieee80211_sta_cur_vht_bw(sta);
187         if (new_bw != sta->sta.bandwidth) {
188                 sta->sta.bandwidth = new_bw;
189                 changed |= IEEE80211_RC_NSS_CHANGED;
190         }
191
192  change:
193         if (changed)
194                 rate_control_rate_update(local, sband, sta, changed);
195 }