diff options
Diffstat (limited to 'net/wireless')
-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 | ||