diff options
author | Luis R. Rodriguez <lrodriguez@atheros.com> | 2009-05-02 00:37:17 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-05-20 14:46:22 -0400 |
commit | 038659e7c6b385065cb223872771ac437ef70b62 (patch) | |
tree | df9e86adcc611d10ed5f98672421bffe1f43989f /net/wireless/reg.c | |
parent | 97bc54152e3a91dd2dc297e8a084c05e93527e60 (diff) |
cfg80211: Process regulatory max bandwidth checks for HT40
We are not correctly listening to the regulatory max bandwidth
settings. To actually make use of it we need to redesign things
a bit. This patch does the work for that. We do this to so we
can obey to regulatory rules accordingly for use of HT40.
We end up dealing with HT40 by having two passes for each channel.
The first check will see if a 20 MHz channel fits into the channel's
center freq on a given frequency range. We check for a 20 MHz
banwidth channel as that is the maximum an individual channel
will use, at least for now. The first pass will go ahead and
check if the regulatory rule for that given center of frequency
allows 40 MHz bandwidths and we use this to determine whether
or not the channel supports HT40 or not. So to support HT40 you'll
need at a regulatory rule that allows you to use 40 MHz channels
but you're channel must also be enabled and support 20 MHz by itself.
The second pass is done after we do the regulatory checks over
an device's supported channel list. On each channel we'll check
if the control channel and the extension both:
o exist
o are enabled
o regulatory allows 40 MHz bandwidth on its frequency range
This work allows allows us to idependently check for HT40- and
HT40+.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 201 |
1 files changed, 159 insertions, 42 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 537af62ec42b..d897528ee7fc 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -48,12 +48,6 @@ static struct regulatory_request *last_request; | |||
48 | /* To trigger userspace events */ | 48 | /* To trigger userspace events */ |
49 | static struct platform_device *reg_pdev; | 49 | static struct platform_device *reg_pdev; |
50 | 50 | ||
51 | /* Keep the ordering from large to small */ | ||
52 | static u32 supported_bandwidths[] = { | ||
53 | MHZ_TO_KHZ(40), | ||
54 | MHZ_TO_KHZ(20), | ||
55 | }; | ||
56 | |||
57 | /* | 51 | /* |
58 | * Central wireless core regulatory domains, we only need two, | 52 | * Central wireless core regulatory domains, we only need two, |
59 | * the current one and a world regulatory domain in case we have no | 53 | * the current one and a world regulatory domain in case we have no |
@@ -435,19 +429,20 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) | |||
435 | return true; | 429 | return true; |
436 | } | 430 | } |
437 | 431 | ||
438 | /* Returns value in KHz */ | 432 | static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, |
439 | static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, | 433 | u32 center_freq_khz, |
440 | u32 freq) | 434 | u32 bw_khz) |
441 | { | 435 | { |
442 | unsigned int i; | 436 | u32 start_freq_khz, end_freq_khz; |
443 | for (i = 0; i < ARRAY_SIZE(supported_bandwidths); i++) { | 437 | |
444 | u32 start_freq_khz = freq - supported_bandwidths[i]/2; | 438 | start_freq_khz = center_freq_khz - (bw_khz/2); |
445 | u32 end_freq_khz = freq + supported_bandwidths[i]/2; | 439 | end_freq_khz = center_freq_khz + (bw_khz/2); |
446 | if (start_freq_khz >= freq_range->start_freq_khz && | 440 | |
447 | end_freq_khz <= freq_range->end_freq_khz) | 441 | if (start_freq_khz >= freq_range->start_freq_khz && |
448 | return supported_bandwidths[i]; | 442 | end_freq_khz <= freq_range->end_freq_khz) |
449 | } | 443 | return true; |
450 | return 0; | 444 | |
445 | return false; | ||
451 | } | 446 | } |
452 | 447 | ||
453 | /** | 448 | /** |
@@ -847,14 +842,17 @@ static u32 map_regdom_flags(u32 rd_flags) | |||
847 | 842 | ||
848 | static int freq_reg_info_regd(struct wiphy *wiphy, | 843 | static int freq_reg_info_regd(struct wiphy *wiphy, |
849 | u32 center_freq, | 844 | u32 center_freq, |
850 | u32 *bandwidth, | 845 | u32 desired_bw_khz, |
851 | const struct ieee80211_reg_rule **reg_rule, | 846 | const struct ieee80211_reg_rule **reg_rule, |
852 | const struct ieee80211_regdomain *custom_regd) | 847 | const struct ieee80211_regdomain *custom_regd) |
853 | { | 848 | { |
854 | int i; | 849 | int i; |
855 | bool band_rule_found = false; | 850 | bool band_rule_found = false; |
856 | const struct ieee80211_regdomain *regd; | 851 | const struct ieee80211_regdomain *regd; |
857 | u32 max_bandwidth = 0; | 852 | bool bw_fits = false; |
853 | |||
854 | if (!desired_bw_khz) | ||
855 | desired_bw_khz = MHZ_TO_KHZ(20); | ||
858 | 856 | ||
859 | regd = custom_regd ? custom_regd : cfg80211_regdomain; | 857 | regd = custom_regd ? custom_regd : cfg80211_regdomain; |
860 | 858 | ||
@@ -887,38 +885,54 @@ static int freq_reg_info_regd(struct wiphy *wiphy, | |||
887 | if (!band_rule_found) | 885 | if (!band_rule_found) |
888 | band_rule_found = freq_in_rule_band(fr, center_freq); | 886 | band_rule_found = freq_in_rule_band(fr, center_freq); |
889 | 887 | ||
890 | max_bandwidth = freq_max_bandwidth(fr, center_freq); | 888 | bw_fits = reg_does_bw_fit(fr, |
889 | center_freq, | ||
890 | desired_bw_khz); | ||
891 | 891 | ||
892 | if (max_bandwidth && *bandwidth <= max_bandwidth) { | 892 | if (band_rule_found && bw_fits) { |
893 | *reg_rule = rr; | 893 | *reg_rule = rr; |
894 | *bandwidth = max_bandwidth; | 894 | return 0; |
895 | break; | ||
896 | } | 895 | } |
897 | } | 896 | } |
898 | 897 | ||
899 | if (!band_rule_found) | 898 | if (!band_rule_found) |
900 | return -ERANGE; | 899 | return -ERANGE; |
901 | 900 | ||
902 | return !max_bandwidth; | 901 | return -EINVAL; |
903 | } | 902 | } |
904 | EXPORT_SYMBOL(freq_reg_info); | 903 | EXPORT_SYMBOL(freq_reg_info); |
905 | 904 | ||
906 | int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, | 905 | int freq_reg_info(struct wiphy *wiphy, |
907 | const struct ieee80211_reg_rule **reg_rule) | 906 | u32 center_freq, |
907 | u32 desired_bw_khz, | ||
908 | const struct ieee80211_reg_rule **reg_rule) | ||
908 | { | 909 | { |
909 | assert_cfg80211_lock(); | 910 | assert_cfg80211_lock(); |
910 | return freq_reg_info_regd(wiphy, center_freq, | 911 | return freq_reg_info_regd(wiphy, |
911 | bandwidth, reg_rule, NULL); | 912 | center_freq, |
913 | desired_bw_khz, | ||
914 | reg_rule, | ||
915 | NULL); | ||
912 | } | 916 | } |
913 | 917 | ||
918 | /* | ||
919 | * Note that right now we assume the desired channel bandwidth | ||
920 | * is always 20 MHz for each individual channel (HT40 uses 20 MHz | ||
921 | * per channel, the primary and the extension channel). To support | ||
922 | * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a | ||
923 | * new ieee80211_channel.target_bw and re run the regulatory check | ||
924 | * on the wiphy with the target_bw specified. Then we can simply use | ||
925 | * that below for the desired_bw_khz below. | ||
926 | */ | ||
914 | static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | 927 | static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, |
915 | unsigned int chan_idx) | 928 | unsigned int chan_idx) |
916 | { | 929 | { |
917 | int r; | 930 | int r; |
918 | u32 flags; | 931 | u32 flags, bw_flags = 0; |
919 | u32 max_bandwidth = 0; | 932 | u32 desired_bw_khz = MHZ_TO_KHZ(20); |
920 | const struct ieee80211_reg_rule *reg_rule = NULL; | 933 | const struct ieee80211_reg_rule *reg_rule = NULL; |
921 | const struct ieee80211_power_rule *power_rule = NULL; | 934 | const struct ieee80211_power_rule *power_rule = NULL; |
935 | const struct ieee80211_freq_range *freq_range = NULL; | ||
922 | struct ieee80211_supported_band *sband; | 936 | struct ieee80211_supported_band *sband; |
923 | struct ieee80211_channel *chan; | 937 | struct ieee80211_channel *chan; |
924 | struct wiphy *request_wiphy = NULL; | 938 | struct wiphy *request_wiphy = NULL; |
@@ -933,8 +947,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
933 | 947 | ||
934 | flags = chan->orig_flags; | 948 | flags = chan->orig_flags; |
935 | 949 | ||
936 | r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), | 950 | r = freq_reg_info(wiphy, |
937 | &max_bandwidth, ®_rule); | 951 | MHZ_TO_KHZ(chan->center_freq), |
952 | desired_bw_khz, | ||
953 | ®_rule); | ||
938 | 954 | ||
939 | if (r) { | 955 | if (r) { |
940 | /* | 956 | /* |
@@ -977,6 +993,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
977 | } | 993 | } |
978 | 994 | ||
979 | power_rule = ®_rule->power_rule; | 995 | power_rule = ®_rule->power_rule; |
996 | freq_range = ®_rule->freq_range; | ||
997 | |||
998 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) | ||
999 | bw_flags = IEEE80211_CHAN_NO_HT40; | ||
980 | 1000 | ||
981 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1001 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
982 | request_wiphy && request_wiphy == wiphy && | 1002 | request_wiphy && request_wiphy == wiphy && |
@@ -987,19 +1007,19 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
987 | * settings | 1007 | * settings |
988 | */ | 1008 | */ |
989 | chan->flags = chan->orig_flags = | 1009 | chan->flags = chan->orig_flags = |
990 | map_regdom_flags(reg_rule->flags); | 1010 | map_regdom_flags(reg_rule->flags) | bw_flags; |
991 | chan->max_antenna_gain = chan->orig_mag = | 1011 | chan->max_antenna_gain = chan->orig_mag = |
992 | (int) MBI_TO_DBI(power_rule->max_antenna_gain); | 1012 | (int) MBI_TO_DBI(power_rule->max_antenna_gain); |
993 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | 1013 | chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); |
994 | chan->max_power = chan->orig_mpwr = | 1014 | chan->max_power = chan->orig_mpwr = |
995 | (int) MBM_TO_DBM(power_rule->max_eirp); | 1015 | (int) MBM_TO_DBM(power_rule->max_eirp); |
996 | return; | 1016 | return; |
997 | } | 1017 | } |
998 | 1018 | ||
999 | chan->flags = flags | map_regdom_flags(reg_rule->flags); | 1019 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); |
1000 | chan->max_antenna_gain = min(chan->orig_mag, | 1020 | chan->max_antenna_gain = min(chan->orig_mag, |
1001 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); | 1021 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); |
1002 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | 1022 | chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); |
1003 | if (chan->orig_mpwr) | 1023 | if (chan->orig_mpwr) |
1004 | chan->max_power = min(chan->orig_mpwr, | 1024 | chan->max_power = min(chan->orig_mpwr, |
1005 | (int) MBM_TO_DBM(power_rule->max_eirp)); | 1025 | (int) MBM_TO_DBM(power_rule->max_eirp)); |
@@ -1156,6 +1176,93 @@ static void reg_process_beacons(struct wiphy *wiphy) | |||
1156 | wiphy_update_beacon_reg(wiphy); | 1176 | wiphy_update_beacon_reg(wiphy); |
1157 | } | 1177 | } |
1158 | 1178 | ||
1179 | static bool is_ht40_not_allowed(struct ieee80211_channel *chan) | ||
1180 | { | ||
1181 | if (!chan) | ||
1182 | return true; | ||
1183 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
1184 | return true; | ||
1185 | /* This would happen when regulatory rules disallow HT40 completely */ | ||
1186 | if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) | ||
1187 | return true; | ||
1188 | return false; | ||
1189 | } | ||
1190 | |||
1191 | static void reg_process_ht_flags_channel(struct wiphy *wiphy, | ||
1192 | enum ieee80211_band band, | ||
1193 | unsigned int chan_idx) | ||
1194 | { | ||
1195 | struct ieee80211_supported_band *sband; | ||
1196 | struct ieee80211_channel *channel; | ||
1197 | struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; | ||
1198 | unsigned int i; | ||
1199 | |||
1200 | assert_cfg80211_lock(); | ||
1201 | |||
1202 | sband = wiphy->bands[band]; | ||
1203 | BUG_ON(chan_idx >= sband->n_channels); | ||
1204 | channel = &sband->channels[chan_idx]; | ||
1205 | |||
1206 | if (is_ht40_not_allowed(channel)) { | ||
1207 | channel->flags |= IEEE80211_CHAN_NO_HT40; | ||
1208 | return; | ||
1209 | } | ||
1210 | |||
1211 | /* | ||
1212 | * We need to ensure the extension channels exist to | ||
1213 | * be able to use HT40- or HT40+, this finds them (or not) | ||
1214 | */ | ||
1215 | for (i = 0; i < sband->n_channels; i++) { | ||
1216 | struct ieee80211_channel *c = &sband->channels[i]; | ||
1217 | if (c->center_freq == (channel->center_freq - 20)) | ||
1218 | channel_before = c; | ||
1219 | if (c->center_freq == (channel->center_freq + 20)) | ||
1220 | channel_after = c; | ||
1221 | } | ||
1222 | |||
1223 | /* | ||
1224 | * Please note that this assumes target bandwidth is 20 MHz, | ||
1225 | * if that ever changes we also need to change the below logic | ||
1226 | * to include that as well. | ||
1227 | */ | ||
1228 | if (is_ht40_not_allowed(channel_before)) | ||
1229 | channel->flags |= IEEE80211_CHAN_NO_FAT_BELOW; | ||
1230 | else | ||
1231 | channel->flags &= ~IEEE80211_CHAN_NO_FAT_BELOW; | ||
1232 | |||
1233 | if (is_ht40_not_allowed(channel_after)) | ||
1234 | channel->flags |= IEEE80211_CHAN_NO_FAT_ABOVE; | ||
1235 | else | ||
1236 | channel->flags &= ~IEEE80211_CHAN_NO_FAT_ABOVE; | ||
1237 | } | ||
1238 | |||
1239 | static void reg_process_ht_flags_band(struct wiphy *wiphy, | ||
1240 | enum ieee80211_band band) | ||
1241 | { | ||
1242 | unsigned int i; | ||
1243 | struct ieee80211_supported_band *sband; | ||
1244 | |||
1245 | BUG_ON(!wiphy->bands[band]); | ||
1246 | sband = wiphy->bands[band]; | ||
1247 | |||
1248 | for (i = 0; i < sband->n_channels; i++) | ||
1249 | reg_process_ht_flags_channel(wiphy, band, i); | ||
1250 | } | ||
1251 | |||
1252 | static void reg_process_ht_flags(struct wiphy *wiphy) | ||
1253 | { | ||
1254 | enum ieee80211_band band; | ||
1255 | |||
1256 | if (!wiphy) | ||
1257 | return; | ||
1258 | |||
1259 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
1260 | if (wiphy->bands[band]) | ||
1261 | reg_process_ht_flags_band(wiphy, band); | ||
1262 | } | ||
1263 | |||
1264 | } | ||
1265 | |||
1159 | void wiphy_update_regulatory(struct wiphy *wiphy, | 1266 | void wiphy_update_regulatory(struct wiphy *wiphy, |
1160 | enum nl80211_reg_initiator initiator) | 1267 | enum nl80211_reg_initiator initiator) |
1161 | { | 1268 | { |
@@ -1169,6 +1276,7 @@ void wiphy_update_regulatory(struct wiphy *wiphy, | |||
1169 | } | 1276 | } |
1170 | out: | 1277 | out: |
1171 | reg_process_beacons(wiphy); | 1278 | reg_process_beacons(wiphy); |
1279 | reg_process_ht_flags(wiphy); | ||
1172 | if (wiphy->reg_notifier) | 1280 | if (wiphy->reg_notifier) |
1173 | wiphy->reg_notifier(wiphy, last_request); | 1281 | wiphy->reg_notifier(wiphy, last_request); |
1174 | } | 1282 | } |
@@ -1179,9 +1287,11 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1179 | const struct ieee80211_regdomain *regd) | 1287 | const struct ieee80211_regdomain *regd) |
1180 | { | 1288 | { |
1181 | int r; | 1289 | int r; |
1182 | u32 max_bandwidth = 0; | 1290 | u32 desired_bw_khz = MHZ_TO_KHZ(20); |
1291 | u32 bw_flags = 0; | ||
1183 | const struct ieee80211_reg_rule *reg_rule = NULL; | 1292 | const struct ieee80211_reg_rule *reg_rule = NULL; |
1184 | const struct ieee80211_power_rule *power_rule = NULL; | 1293 | const struct ieee80211_power_rule *power_rule = NULL; |
1294 | const struct ieee80211_freq_range *freq_range = NULL; | ||
1185 | struct ieee80211_supported_band *sband; | 1295 | struct ieee80211_supported_band *sband; |
1186 | struct ieee80211_channel *chan; | 1296 | struct ieee80211_channel *chan; |
1187 | 1297 | ||
@@ -1191,8 +1301,11 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1191 | BUG_ON(chan_idx >= sband->n_channels); | 1301 | BUG_ON(chan_idx >= sband->n_channels); |
1192 | chan = &sband->channels[chan_idx]; | 1302 | chan = &sband->channels[chan_idx]; |
1193 | 1303 | ||
1194 | r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), | 1304 | r = freq_reg_info_regd(wiphy, |
1195 | &max_bandwidth, ®_rule, regd); | 1305 | MHZ_TO_KHZ(chan->center_freq), |
1306 | desired_bw_khz, | ||
1307 | ®_rule, | ||
1308 | regd); | ||
1196 | 1309 | ||
1197 | if (r) { | 1310 | if (r) { |
1198 | chan->flags = IEEE80211_CHAN_DISABLED; | 1311 | chan->flags = IEEE80211_CHAN_DISABLED; |
@@ -1200,10 +1313,14 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1200 | } | 1313 | } |
1201 | 1314 | ||
1202 | power_rule = ®_rule->power_rule; | 1315 | power_rule = ®_rule->power_rule; |
1316 | freq_range = ®_rule->freq_range; | ||
1317 | |||
1318 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) | ||
1319 | bw_flags = IEEE80211_CHAN_NO_HT40; | ||
1203 | 1320 | ||
1204 | chan->flags |= map_regdom_flags(reg_rule->flags); | 1321 | chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; |
1205 | chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); | 1322 | chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); |
1206 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | 1323 | chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); |
1207 | chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); | 1324 | chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); |
1208 | } | 1325 | } |
1209 | 1326 | ||