diff options
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 274 |
1 files changed, 207 insertions, 67 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 487cb627ddba..5e14371cda70 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -37,7 +37,6 @@ | |||
37 | #include <linux/random.h> | 37 | #include <linux/random.h> |
38 | #include <linux/nl80211.h> | 38 | #include <linux/nl80211.h> |
39 | #include <linux/platform_device.h> | 39 | #include <linux/platform_device.h> |
40 | #include <net/wireless.h> | ||
41 | #include <net/cfg80211.h> | 40 | #include <net/cfg80211.h> |
42 | #include "core.h" | 41 | #include "core.h" |
43 | #include "reg.h" | 42 | #include "reg.h" |
@@ -49,12 +48,6 @@ static struct regulatory_request *last_request; | |||
49 | /* To trigger userspace events */ | 48 | /* To trigger userspace events */ |
50 | static struct platform_device *reg_pdev; | 49 | static struct platform_device *reg_pdev; |
51 | 50 | ||
52 | /* Keep the ordering from large to small */ | ||
53 | static u32 supported_bandwidths[] = { | ||
54 | MHZ_TO_KHZ(40), | ||
55 | MHZ_TO_KHZ(20), | ||
56 | }; | ||
57 | |||
58 | /* | 51 | /* |
59 | * Central wireless core regulatory domains, we only need two, | 52 | * Central wireless core regulatory domains, we only need two, |
60 | * 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 |
@@ -389,6 +382,8 @@ static int call_crda(const char *alpha2) | |||
389 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ | 382 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ |
390 | bool reg_is_valid_request(const char *alpha2) | 383 | bool reg_is_valid_request(const char *alpha2) |
391 | { | 384 | { |
385 | assert_cfg80211_lock(); | ||
386 | |||
392 | if (!last_request) | 387 | if (!last_request) |
393 | return false; | 388 | return false; |
394 | 389 | ||
@@ -436,19 +431,20 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) | |||
436 | return true; | 431 | return true; |
437 | } | 432 | } |
438 | 433 | ||
439 | /* Returns value in KHz */ | 434 | static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, |
440 | static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, | 435 | u32 center_freq_khz, |
441 | u32 freq) | 436 | u32 bw_khz) |
442 | { | 437 | { |
443 | unsigned int i; | 438 | u32 start_freq_khz, end_freq_khz; |
444 | for (i = 0; i < ARRAY_SIZE(supported_bandwidths); i++) { | 439 | |
445 | u32 start_freq_khz = freq - supported_bandwidths[i]/2; | 440 | start_freq_khz = center_freq_khz - (bw_khz/2); |
446 | u32 end_freq_khz = freq + supported_bandwidths[i]/2; | 441 | end_freq_khz = center_freq_khz + (bw_khz/2); |
447 | if (start_freq_khz >= freq_range->start_freq_khz && | 442 | |
448 | end_freq_khz <= freq_range->end_freq_khz) | 443 | if (start_freq_khz >= freq_range->start_freq_khz && |
449 | return supported_bandwidths[i]; | 444 | end_freq_khz <= freq_range->end_freq_khz) |
450 | } | 445 | return true; |
451 | return 0; | 446 | |
447 | return false; | ||
452 | } | 448 | } |
453 | 449 | ||
454 | /** | 450 | /** |
@@ -848,14 +844,17 @@ static u32 map_regdom_flags(u32 rd_flags) | |||
848 | 844 | ||
849 | static int freq_reg_info_regd(struct wiphy *wiphy, | 845 | static int freq_reg_info_regd(struct wiphy *wiphy, |
850 | u32 center_freq, | 846 | u32 center_freq, |
851 | u32 *bandwidth, | 847 | u32 desired_bw_khz, |
852 | const struct ieee80211_reg_rule **reg_rule, | 848 | const struct ieee80211_reg_rule **reg_rule, |
853 | const struct ieee80211_regdomain *custom_regd) | 849 | const struct ieee80211_regdomain *custom_regd) |
854 | { | 850 | { |
855 | int i; | 851 | int i; |
856 | bool band_rule_found = false; | 852 | bool band_rule_found = false; |
857 | const struct ieee80211_regdomain *regd; | 853 | const struct ieee80211_regdomain *regd; |
858 | u32 max_bandwidth = 0; | 854 | bool bw_fits = false; |
855 | |||
856 | if (!desired_bw_khz) | ||
857 | desired_bw_khz = MHZ_TO_KHZ(20); | ||
859 | 858 | ||
860 | regd = custom_regd ? custom_regd : cfg80211_regdomain; | 859 | regd = custom_regd ? custom_regd : cfg80211_regdomain; |
861 | 860 | ||
@@ -888,38 +887,54 @@ static int freq_reg_info_regd(struct wiphy *wiphy, | |||
888 | if (!band_rule_found) | 887 | if (!band_rule_found) |
889 | band_rule_found = freq_in_rule_band(fr, center_freq); | 888 | band_rule_found = freq_in_rule_band(fr, center_freq); |
890 | 889 | ||
891 | max_bandwidth = freq_max_bandwidth(fr, center_freq); | 890 | bw_fits = reg_does_bw_fit(fr, |
891 | center_freq, | ||
892 | desired_bw_khz); | ||
892 | 893 | ||
893 | if (max_bandwidth && *bandwidth <= max_bandwidth) { | 894 | if (band_rule_found && bw_fits) { |
894 | *reg_rule = rr; | 895 | *reg_rule = rr; |
895 | *bandwidth = max_bandwidth; | 896 | return 0; |
896 | break; | ||
897 | } | 897 | } |
898 | } | 898 | } |
899 | 899 | ||
900 | if (!band_rule_found) | 900 | if (!band_rule_found) |
901 | return -ERANGE; | 901 | return -ERANGE; |
902 | 902 | ||
903 | return !max_bandwidth; | 903 | return -EINVAL; |
904 | } | 904 | } |
905 | EXPORT_SYMBOL(freq_reg_info); | 905 | EXPORT_SYMBOL(freq_reg_info); |
906 | 906 | ||
907 | int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, | 907 | int freq_reg_info(struct wiphy *wiphy, |
908 | const struct ieee80211_reg_rule **reg_rule) | 908 | u32 center_freq, |
909 | u32 desired_bw_khz, | ||
910 | const struct ieee80211_reg_rule **reg_rule) | ||
909 | { | 911 | { |
910 | assert_cfg80211_lock(); | 912 | assert_cfg80211_lock(); |
911 | return freq_reg_info_regd(wiphy, center_freq, | 913 | return freq_reg_info_regd(wiphy, |
912 | bandwidth, reg_rule, NULL); | 914 | center_freq, |
915 | desired_bw_khz, | ||
916 | reg_rule, | ||
917 | NULL); | ||
913 | } | 918 | } |
914 | 919 | ||
920 | /* | ||
921 | * Note that right now we assume the desired channel bandwidth | ||
922 | * is always 20 MHz for each individual channel (HT40 uses 20 MHz | ||
923 | * per channel, the primary and the extension channel). To support | ||
924 | * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a | ||
925 | * new ieee80211_channel.target_bw and re run the regulatory check | ||
926 | * on the wiphy with the target_bw specified. Then we can simply use | ||
927 | * that below for the desired_bw_khz below. | ||
928 | */ | ||
915 | static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | 929 | static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, |
916 | unsigned int chan_idx) | 930 | unsigned int chan_idx) |
917 | { | 931 | { |
918 | int r; | 932 | int r; |
919 | u32 flags; | 933 | u32 flags, bw_flags = 0; |
920 | u32 max_bandwidth = 0; | 934 | u32 desired_bw_khz = MHZ_TO_KHZ(20); |
921 | const struct ieee80211_reg_rule *reg_rule = NULL; | 935 | const struct ieee80211_reg_rule *reg_rule = NULL; |
922 | const struct ieee80211_power_rule *power_rule = NULL; | 936 | const struct ieee80211_power_rule *power_rule = NULL; |
937 | const struct ieee80211_freq_range *freq_range = NULL; | ||
923 | struct ieee80211_supported_band *sband; | 938 | struct ieee80211_supported_band *sband; |
924 | struct ieee80211_channel *chan; | 939 | struct ieee80211_channel *chan; |
925 | struct wiphy *request_wiphy = NULL; | 940 | struct wiphy *request_wiphy = NULL; |
@@ -934,8 +949,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
934 | 949 | ||
935 | flags = chan->orig_flags; | 950 | flags = chan->orig_flags; |
936 | 951 | ||
937 | r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), | 952 | r = freq_reg_info(wiphy, |
938 | &max_bandwidth, ®_rule); | 953 | MHZ_TO_KHZ(chan->center_freq), |
954 | desired_bw_khz, | ||
955 | ®_rule); | ||
939 | 956 | ||
940 | if (r) { | 957 | if (r) { |
941 | /* | 958 | /* |
@@ -978,6 +995,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
978 | } | 995 | } |
979 | 996 | ||
980 | power_rule = ®_rule->power_rule; | 997 | power_rule = ®_rule->power_rule; |
998 | freq_range = ®_rule->freq_range; | ||
999 | |||
1000 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) | ||
1001 | bw_flags = IEEE80211_CHAN_NO_HT40; | ||
981 | 1002 | ||
982 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1003 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
983 | request_wiphy && request_wiphy == wiphy && | 1004 | request_wiphy && request_wiphy == wiphy && |
@@ -988,19 +1009,19 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
988 | * settings | 1009 | * settings |
989 | */ | 1010 | */ |
990 | chan->flags = chan->orig_flags = | 1011 | chan->flags = chan->orig_flags = |
991 | map_regdom_flags(reg_rule->flags); | 1012 | map_regdom_flags(reg_rule->flags) | bw_flags; |
992 | chan->max_antenna_gain = chan->orig_mag = | 1013 | chan->max_antenna_gain = chan->orig_mag = |
993 | (int) MBI_TO_DBI(power_rule->max_antenna_gain); | 1014 | (int) MBI_TO_DBI(power_rule->max_antenna_gain); |
994 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | 1015 | chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); |
995 | chan->max_power = chan->orig_mpwr = | 1016 | chan->max_power = chan->orig_mpwr = |
996 | (int) MBM_TO_DBM(power_rule->max_eirp); | 1017 | (int) MBM_TO_DBM(power_rule->max_eirp); |
997 | return; | 1018 | return; |
998 | } | 1019 | } |
999 | 1020 | ||
1000 | chan->flags = flags | map_regdom_flags(reg_rule->flags); | 1021 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); |
1001 | chan->max_antenna_gain = min(chan->orig_mag, | 1022 | chan->max_antenna_gain = min(chan->orig_mag, |
1002 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); | 1023 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); |
1003 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | 1024 | chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); |
1004 | if (chan->orig_mpwr) | 1025 | if (chan->orig_mpwr) |
1005 | chan->max_power = min(chan->orig_mpwr, | 1026 | chan->max_power = min(chan->orig_mpwr, |
1006 | (int) MBM_TO_DBM(power_rule->max_eirp)); | 1027 | (int) MBM_TO_DBM(power_rule->max_eirp)); |
@@ -1050,18 +1071,10 @@ static void handle_reg_beacon(struct wiphy *wiphy, | |||
1050 | unsigned int chan_idx, | 1071 | unsigned int chan_idx, |
1051 | struct reg_beacon *reg_beacon) | 1072 | struct reg_beacon *reg_beacon) |
1052 | { | 1073 | { |
1053 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
1054 | #define REG_DEBUG_BEACON_FLAG(desc) \ | ||
1055 | printk(KERN_DEBUG "cfg80211: Enabling " desc " on " \ | ||
1056 | "frequency: %d MHz (Ch %d) on %s\n", \ | ||
1057 | reg_beacon->chan.center_freq, \ | ||
1058 | ieee80211_frequency_to_channel(reg_beacon->chan.center_freq), \ | ||
1059 | wiphy_name(wiphy)); | ||
1060 | #else | ||
1061 | #define REG_DEBUG_BEACON_FLAG(desc) do {} while (0) | ||
1062 | #endif | ||
1063 | struct ieee80211_supported_band *sband; | 1074 | struct ieee80211_supported_band *sband; |
1064 | struct ieee80211_channel *chan; | 1075 | struct ieee80211_channel *chan; |
1076 | bool channel_changed = false; | ||
1077 | struct ieee80211_channel chan_before; | ||
1065 | 1078 | ||
1066 | assert_cfg80211_lock(); | 1079 | assert_cfg80211_lock(); |
1067 | 1080 | ||
@@ -1071,18 +1084,28 @@ static void handle_reg_beacon(struct wiphy *wiphy, | |||
1071 | if (likely(chan->center_freq != reg_beacon->chan.center_freq)) | 1084 | if (likely(chan->center_freq != reg_beacon->chan.center_freq)) |
1072 | return; | 1085 | return; |
1073 | 1086 | ||
1074 | if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { | 1087 | if (chan->beacon_found) |
1088 | return; | ||
1089 | |||
1090 | chan->beacon_found = true; | ||
1091 | |||
1092 | chan_before.center_freq = chan->center_freq; | ||
1093 | chan_before.flags = chan->flags; | ||
1094 | |||
1095 | if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) && | ||
1096 | !(chan->orig_flags & IEEE80211_CHAN_PASSIVE_SCAN)) { | ||
1075 | chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; | 1097 | chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; |
1076 | REG_DEBUG_BEACON_FLAG("active scanning"); | 1098 | channel_changed = true; |
1077 | } | 1099 | } |
1078 | 1100 | ||
1079 | if (chan->flags & IEEE80211_CHAN_NO_IBSS) { | 1101 | if ((chan->flags & IEEE80211_CHAN_NO_IBSS) && |
1102 | !(chan->orig_flags & IEEE80211_CHAN_NO_IBSS)) { | ||
1080 | chan->flags &= ~IEEE80211_CHAN_NO_IBSS; | 1103 | chan->flags &= ~IEEE80211_CHAN_NO_IBSS; |
1081 | REG_DEBUG_BEACON_FLAG("beaconing"); | 1104 | channel_changed = true; |
1082 | } | 1105 | } |
1083 | 1106 | ||
1084 | chan->beacon_found = true; | 1107 | if (channel_changed) |
1085 | #undef REG_DEBUG_BEACON_FLAG | 1108 | nl80211_send_beacon_hint_event(wiphy, &chan_before, chan); |
1086 | } | 1109 | } |
1087 | 1110 | ||
1088 | /* | 1111 | /* |
@@ -1155,6 +1178,93 @@ static void reg_process_beacons(struct wiphy *wiphy) | |||
1155 | wiphy_update_beacon_reg(wiphy); | 1178 | wiphy_update_beacon_reg(wiphy); |
1156 | } | 1179 | } |
1157 | 1180 | ||
1181 | static bool is_ht40_not_allowed(struct ieee80211_channel *chan) | ||
1182 | { | ||
1183 | if (!chan) | ||
1184 | return true; | ||
1185 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
1186 | return true; | ||
1187 | /* This would happen when regulatory rules disallow HT40 completely */ | ||
1188 | if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) | ||
1189 | return true; | ||
1190 | return false; | ||
1191 | } | ||
1192 | |||
1193 | static void reg_process_ht_flags_channel(struct wiphy *wiphy, | ||
1194 | enum ieee80211_band band, | ||
1195 | unsigned int chan_idx) | ||
1196 | { | ||
1197 | struct ieee80211_supported_band *sband; | ||
1198 | struct ieee80211_channel *channel; | ||
1199 | struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; | ||
1200 | unsigned int i; | ||
1201 | |||
1202 | assert_cfg80211_lock(); | ||
1203 | |||
1204 | sband = wiphy->bands[band]; | ||
1205 | BUG_ON(chan_idx >= sband->n_channels); | ||
1206 | channel = &sband->channels[chan_idx]; | ||
1207 | |||
1208 | if (is_ht40_not_allowed(channel)) { | ||
1209 | channel->flags |= IEEE80211_CHAN_NO_HT40; | ||
1210 | return; | ||
1211 | } | ||
1212 | |||
1213 | /* | ||
1214 | * We need to ensure the extension channels exist to | ||
1215 | * be able to use HT40- or HT40+, this finds them (or not) | ||
1216 | */ | ||
1217 | for (i = 0; i < sband->n_channels; i++) { | ||
1218 | struct ieee80211_channel *c = &sband->channels[i]; | ||
1219 | if (c->center_freq == (channel->center_freq - 20)) | ||
1220 | channel_before = c; | ||
1221 | if (c->center_freq == (channel->center_freq + 20)) | ||
1222 | channel_after = c; | ||
1223 | } | ||
1224 | |||
1225 | /* | ||
1226 | * Please note that this assumes target bandwidth is 20 MHz, | ||
1227 | * if that ever changes we also need to change the below logic | ||
1228 | * to include that as well. | ||
1229 | */ | ||
1230 | if (is_ht40_not_allowed(channel_before)) | ||
1231 | channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; | ||
1232 | else | ||
1233 | channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | ||
1234 | |||
1235 | if (is_ht40_not_allowed(channel_after)) | ||
1236 | channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; | ||
1237 | else | ||
1238 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | ||
1239 | } | ||
1240 | |||
1241 | static void reg_process_ht_flags_band(struct wiphy *wiphy, | ||
1242 | enum ieee80211_band band) | ||
1243 | { | ||
1244 | unsigned int i; | ||
1245 | struct ieee80211_supported_band *sband; | ||
1246 | |||
1247 | BUG_ON(!wiphy->bands[band]); | ||
1248 | sband = wiphy->bands[band]; | ||
1249 | |||
1250 | for (i = 0; i < sband->n_channels; i++) | ||
1251 | reg_process_ht_flags_channel(wiphy, band, i); | ||
1252 | } | ||
1253 | |||
1254 | static void reg_process_ht_flags(struct wiphy *wiphy) | ||
1255 | { | ||
1256 | enum ieee80211_band band; | ||
1257 | |||
1258 | if (!wiphy) | ||
1259 | return; | ||
1260 | |||
1261 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
1262 | if (wiphy->bands[band]) | ||
1263 | reg_process_ht_flags_band(wiphy, band); | ||
1264 | } | ||
1265 | |||
1266 | } | ||
1267 | |||
1158 | void wiphy_update_regulatory(struct wiphy *wiphy, | 1268 | void wiphy_update_regulatory(struct wiphy *wiphy, |
1159 | enum nl80211_reg_initiator initiator) | 1269 | enum nl80211_reg_initiator initiator) |
1160 | { | 1270 | { |
@@ -1168,6 +1278,7 @@ void wiphy_update_regulatory(struct wiphy *wiphy, | |||
1168 | } | 1278 | } |
1169 | out: | 1279 | out: |
1170 | reg_process_beacons(wiphy); | 1280 | reg_process_beacons(wiphy); |
1281 | reg_process_ht_flags(wiphy); | ||
1171 | if (wiphy->reg_notifier) | 1282 | if (wiphy->reg_notifier) |
1172 | wiphy->reg_notifier(wiphy, last_request); | 1283 | wiphy->reg_notifier(wiphy, last_request); |
1173 | } | 1284 | } |
@@ -1178,9 +1289,11 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1178 | const struct ieee80211_regdomain *regd) | 1289 | const struct ieee80211_regdomain *regd) |
1179 | { | 1290 | { |
1180 | int r; | 1291 | int r; |
1181 | u32 max_bandwidth = 0; | 1292 | u32 desired_bw_khz = MHZ_TO_KHZ(20); |
1293 | u32 bw_flags = 0; | ||
1182 | const struct ieee80211_reg_rule *reg_rule = NULL; | 1294 | const struct ieee80211_reg_rule *reg_rule = NULL; |
1183 | const struct ieee80211_power_rule *power_rule = NULL; | 1295 | const struct ieee80211_power_rule *power_rule = NULL; |
1296 | const struct ieee80211_freq_range *freq_range = NULL; | ||
1184 | struct ieee80211_supported_band *sband; | 1297 | struct ieee80211_supported_band *sband; |
1185 | struct ieee80211_channel *chan; | 1298 | struct ieee80211_channel *chan; |
1186 | 1299 | ||
@@ -1190,8 +1303,11 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1190 | BUG_ON(chan_idx >= sband->n_channels); | 1303 | BUG_ON(chan_idx >= sband->n_channels); |
1191 | chan = &sband->channels[chan_idx]; | 1304 | chan = &sband->channels[chan_idx]; |
1192 | 1305 | ||
1193 | r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), | 1306 | r = freq_reg_info_regd(wiphy, |
1194 | &max_bandwidth, ®_rule, regd); | 1307 | MHZ_TO_KHZ(chan->center_freq), |
1308 | desired_bw_khz, | ||
1309 | ®_rule, | ||
1310 | regd); | ||
1195 | 1311 | ||
1196 | if (r) { | 1312 | if (r) { |
1197 | chan->flags = IEEE80211_CHAN_DISABLED; | 1313 | chan->flags = IEEE80211_CHAN_DISABLED; |
@@ -1199,10 +1315,14 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1199 | } | 1315 | } |
1200 | 1316 | ||
1201 | power_rule = ®_rule->power_rule; | 1317 | power_rule = ®_rule->power_rule; |
1318 | freq_range = ®_rule->freq_range; | ||
1319 | |||
1320 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) | ||
1321 | bw_flags = IEEE80211_CHAN_NO_HT40; | ||
1202 | 1322 | ||
1203 | chan->flags |= map_regdom_flags(reg_rule->flags); | 1323 | chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; |
1204 | chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); | 1324 | chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); |
1205 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | 1325 | chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); |
1206 | chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); | 1326 | chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); |
1207 | } | 1327 | } |
1208 | 1328 | ||
@@ -1224,13 +1344,22 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
1224 | const struct ieee80211_regdomain *regd) | 1344 | const struct ieee80211_regdomain *regd) |
1225 | { | 1345 | { |
1226 | enum ieee80211_band band; | 1346 | enum ieee80211_band band; |
1347 | unsigned int bands_set = 0; | ||
1227 | 1348 | ||
1228 | mutex_lock(&cfg80211_mutex); | 1349 | mutex_lock(&cfg80211_mutex); |
1229 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1350 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
1230 | if (wiphy->bands[band]) | 1351 | if (!wiphy->bands[band]) |
1231 | handle_band_custom(wiphy, band, regd); | 1352 | continue; |
1353 | handle_band_custom(wiphy, band, regd); | ||
1354 | bands_set++; | ||
1232 | } | 1355 | } |
1233 | mutex_unlock(&cfg80211_mutex); | 1356 | mutex_unlock(&cfg80211_mutex); |
1357 | |||
1358 | /* | ||
1359 | * no point in calling this if it won't have any effect | ||
1360 | * on your device's supportd bands. | ||
1361 | */ | ||
1362 | WARN_ON(!bands_set); | ||
1234 | } | 1363 | } |
1235 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1364 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
1236 | 1365 | ||
@@ -2000,7 +2129,12 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2000 | * driver wanted to the wiphy to deal with conflicts | 2129 | * driver wanted to the wiphy to deal with conflicts |
2001 | */ | 2130 | */ |
2002 | 2131 | ||
2003 | BUG_ON(request_wiphy->regd); | 2132 | /* |
2133 | * Userspace could have sent two replies with only | ||
2134 | * one kernel request. | ||
2135 | */ | ||
2136 | if (request_wiphy->regd) | ||
2137 | return -EALREADY; | ||
2004 | 2138 | ||
2005 | r = reg_copy_regd(&request_wiphy->regd, rd); | 2139 | r = reg_copy_regd(&request_wiphy->regd, rd); |
2006 | if (r) | 2140 | if (r) |
@@ -2042,7 +2176,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2042 | * the country IE rd with what CRDA believes that country should have | 2176 | * the country IE rd with what CRDA believes that country should have |
2043 | */ | 2177 | */ |
2044 | 2178 | ||
2045 | BUG_ON(!country_ie_regdomain); | 2179 | /* |
2180 | * Userspace could have sent two replies with only | ||
2181 | * one kernel request. By the second reply we would have | ||
2182 | * already processed and consumed the country_ie_regdomain. | ||
2183 | */ | ||
2184 | if (!country_ie_regdomain) | ||
2185 | return -EALREADY; | ||
2046 | BUG_ON(rd == country_ie_regdomain); | 2186 | BUG_ON(rd == country_ie_regdomain); |
2047 | 2187 | ||
2048 | /* | 2188 | /* |
@@ -2119,14 +2259,14 @@ void reg_device_remove(struct wiphy *wiphy) | |||
2119 | 2259 | ||
2120 | assert_cfg80211_lock(); | 2260 | assert_cfg80211_lock(); |
2121 | 2261 | ||
2262 | kfree(wiphy->regd); | ||
2263 | |||
2122 | if (last_request) | 2264 | if (last_request) |
2123 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 2265 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
2124 | 2266 | ||
2125 | kfree(wiphy->regd); | 2267 | if (!request_wiphy || request_wiphy != wiphy) |
2126 | if (!last_request || !request_wiphy) | ||
2127 | return; | ||
2128 | if (request_wiphy != wiphy) | ||
2129 | return; | 2268 | return; |
2269 | |||
2130 | last_request->wiphy_idx = WIPHY_IDX_STALE; | 2270 | last_request->wiphy_idx = WIPHY_IDX_STALE; |
2131 | last_request->country_ie_env = ENVIRON_ANY; | 2271 | last_request->country_ie_env = ENVIRON_ANY; |
2132 | } | 2272 | } |