diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/reg.c | 104 |
1 files changed, 92 insertions, 12 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 0f93d4526f37..5a746cd114a6 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -784,6 +784,7 @@ static u32 map_regdom_flags(u32 rd_flags) | |||
784 | 784 | ||
785 | /** | 785 | /** |
786 | * freq_reg_info - get regulatory information for the given frequency | 786 | * freq_reg_info - get regulatory information for the given frequency |
787 | * @wiphy: the wiphy for which we want to process this rule for | ||
787 | * @center_freq: Frequency in KHz for which we want regulatory information for | 788 | * @center_freq: Frequency in KHz for which we want regulatory information for |
788 | * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one | 789 | * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one |
789 | * you can set this to 0. If this frequency is allowed we then set | 790 | * you can set this to 0. If this frequency is allowed we then set |
@@ -802,22 +803,31 @@ static u32 map_regdom_flags(u32 rd_flags) | |||
802 | * freq_in_rule_band() for our current definition of a band -- this is purely | 803 | * freq_in_rule_band() for our current definition of a band -- this is purely |
803 | * subjective and right now its 802.11 specific. | 804 | * subjective and right now its 802.11 specific. |
804 | */ | 805 | */ |
805 | static int freq_reg_info(u32 center_freq, u32 *bandwidth, | 806 | static int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, |
806 | const struct ieee80211_reg_rule **reg_rule) | 807 | const struct ieee80211_reg_rule **reg_rule) |
807 | { | 808 | { |
808 | int i; | 809 | int i; |
809 | bool band_rule_found = false; | 810 | bool band_rule_found = false; |
811 | const struct ieee80211_regdomain *regd; | ||
810 | u32 max_bandwidth = 0; | 812 | u32 max_bandwidth = 0; |
811 | 813 | ||
812 | if (!cfg80211_regdomain) | 814 | regd = cfg80211_regdomain; |
815 | |||
816 | /* Follow the driver's regulatory domain, if present, unless a country | ||
817 | * IE has been processed */ | ||
818 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && | ||
819 | wiphy->regd) | ||
820 | regd = wiphy->regd; | ||
821 | |||
822 | if (!regd) | ||
813 | return -EINVAL; | 823 | return -EINVAL; |
814 | 824 | ||
815 | for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { | 825 | for (i = 0; i < regd->n_reg_rules; i++) { |
816 | const struct ieee80211_reg_rule *rr; | 826 | const struct ieee80211_reg_rule *rr; |
817 | const struct ieee80211_freq_range *fr = NULL; | 827 | const struct ieee80211_freq_range *fr = NULL; |
818 | const struct ieee80211_power_rule *pr = NULL; | 828 | const struct ieee80211_power_rule *pr = NULL; |
819 | 829 | ||
820 | rr = &cfg80211_regdomain->reg_rules[i]; | 830 | rr = ®d->reg_rules[i]; |
821 | fr = &rr->freq_range; | 831 | fr = &rr->freq_range; |
822 | pr = &rr->power_rule; | 832 | pr = &rr->power_rule; |
823 | 833 | ||
@@ -859,7 +869,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
859 | 869 | ||
860 | flags = chan->orig_flags; | 870 | flags = chan->orig_flags; |
861 | 871 | ||
862 | r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq), | 872 | r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), |
863 | &max_bandwidth, ®_rule); | 873 | &max_bandwidth, ®_rule); |
864 | 874 | ||
865 | if (r) { | 875 | if (r) { |
@@ -952,6 +962,30 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | |||
952 | wiphy->reg_notifier(wiphy, setby); | 962 | wiphy->reg_notifier(wiphy, setby); |
953 | } | 963 | } |
954 | 964 | ||
965 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
966 | const struct ieee80211_regdomain *src_regd) | ||
967 | { | ||
968 | struct ieee80211_regdomain *regd; | ||
969 | int size_of_regd = 0; | ||
970 | unsigned int i; | ||
971 | |||
972 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
973 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
974 | |||
975 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
976 | if (!regd) | ||
977 | return -ENOMEM; | ||
978 | |||
979 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
980 | |||
981 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
982 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
983 | sizeof(struct ieee80211_reg_rule)); | ||
984 | |||
985 | *dst_regd = regd; | ||
986 | return 0; | ||
987 | } | ||
988 | |||
955 | /* Return value which can be used by ignore_request() to indicate | 989 | /* Return value which can be used by ignore_request() to indicate |
956 | * it has been determined we should intersect two regulatory domains */ | 990 | * it has been determined we should intersect two regulatory domains */ |
957 | #define REG_INTERSECT 1 | 991 | #define REG_INTERSECT 1 |
@@ -999,9 +1033,9 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
999 | } | 1033 | } |
1000 | return REG_INTERSECT; | 1034 | return REG_INTERSECT; |
1001 | case REGDOM_SET_BY_DRIVER: | 1035 | case REGDOM_SET_BY_DRIVER: |
1002 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | 1036 | if (last_request->initiator == REGDOM_SET_BY_CORE) |
1003 | return -EALREADY; | 1037 | return 0; |
1004 | return 0; | 1038 | return REG_INTERSECT; |
1005 | case REGDOM_SET_BY_USER: | 1039 | case REGDOM_SET_BY_USER: |
1006 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | 1040 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) |
1007 | return REG_INTERSECT; | 1041 | return REG_INTERSECT; |
@@ -1028,11 +1062,28 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1028 | 1062 | ||
1029 | r = ignore_request(wiphy, set_by, alpha2); | 1063 | r = ignore_request(wiphy, set_by, alpha2); |
1030 | 1064 | ||
1031 | if (r == REG_INTERSECT) | 1065 | if (r == REG_INTERSECT) { |
1066 | if (set_by == REGDOM_SET_BY_DRIVER) { | ||
1067 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | ||
1068 | if (r) | ||
1069 | return r; | ||
1070 | } | ||
1032 | intersect = true; | 1071 | intersect = true; |
1033 | else if (r) | 1072 | } else if (r) { |
1073 | /* If the regulatory domain being requested by the | ||
1074 | * driver has already been set just copy it to the | ||
1075 | * wiphy */ | ||
1076 | if (r == -EALREADY && set_by == REGDOM_SET_BY_DRIVER) { | ||
1077 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | ||
1078 | if (r) | ||
1079 | return r; | ||
1080 | r = -EALREADY; | ||
1081 | goto new_request; | ||
1082 | } | ||
1034 | return r; | 1083 | return r; |
1084 | } | ||
1035 | 1085 | ||
1086 | new_request: | ||
1036 | request = kzalloc(sizeof(struct regulatory_request), | 1087 | request = kzalloc(sizeof(struct regulatory_request), |
1037 | GFP_KERNEL); | 1088 | GFP_KERNEL); |
1038 | if (!request) | 1089 | if (!request) |
@@ -1048,6 +1099,11 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1048 | 1099 | ||
1049 | kfree(last_request); | 1100 | kfree(last_request); |
1050 | last_request = request; | 1101 | last_request = request; |
1102 | |||
1103 | /* When r == REG_INTERSECT we do need to call CRDA */ | ||
1104 | if (r < 0) | ||
1105 | return r; | ||
1106 | |||
1051 | /* | 1107 | /* |
1052 | * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled | 1108 | * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled |
1053 | * AND if CRDA is NOT present nothing will happen, if someone | 1109 | * AND if CRDA is NOT present nothing will happen, if someone |
@@ -1341,6 +1397,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
1341 | } | 1397 | } |
1342 | 1398 | ||
1343 | if (!last_request->intersect) { | 1399 | if (!last_request->intersect) { |
1400 | int r; | ||
1401 | |||
1402 | if (last_request->initiator != REGDOM_SET_BY_DRIVER) { | ||
1403 | reset_regdomains(); | ||
1404 | cfg80211_regdomain = rd; | ||
1405 | return 0; | ||
1406 | } | ||
1407 | |||
1408 | /* For a driver hint, lets copy the regulatory domain the | ||
1409 | * driver wanted to the wiphy to deal with conflicts */ | ||
1410 | |||
1411 | BUG_ON(last_request->wiphy->regd); | ||
1412 | |||
1413 | r = reg_copy_regd(&last_request->wiphy->regd, rd); | ||
1414 | if (r) | ||
1415 | return r; | ||
1416 | |||
1344 | reset_regdomains(); | 1417 | reset_regdomains(); |
1345 | cfg80211_regdomain = rd; | 1418 | cfg80211_regdomain = rd; |
1346 | return 0; | 1419 | return 0; |
@@ -1354,8 +1427,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
1354 | if (!intersected_rd) | 1427 | if (!intersected_rd) |
1355 | return -EINVAL; | 1428 | return -EINVAL; |
1356 | 1429 | ||
1357 | /* We can trash what CRDA provided now */ | 1430 | /* We can trash what CRDA provided now. |
1358 | kfree(rd); | 1431 | * However if a driver requested this specific regulatory |
1432 | * domain we keep it for its private use */ | ||
1433 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | ||
1434 | last_request->wiphy->regd = rd; | ||
1435 | else | ||
1436 | kfree(rd); | ||
1437 | |||
1359 | rd = NULL; | 1438 | rd = NULL; |
1360 | 1439 | ||
1361 | reset_regdomains(); | 1440 | reset_regdomains(); |
@@ -1439,6 +1518,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
1439 | /* Caller must hold cfg80211_drv_mutex */ | 1518 | /* Caller must hold cfg80211_drv_mutex */ |
1440 | void reg_device_remove(struct wiphy *wiphy) | 1519 | void reg_device_remove(struct wiphy *wiphy) |
1441 | { | 1520 | { |
1521 | kfree(wiphy->regd); | ||
1442 | if (!last_request || !last_request->wiphy) | 1522 | if (!last_request || !last_request->wiphy) |
1443 | return; | 1523 | return; |
1444 | if (last_request->wiphy != wiphy) | 1524 | if (last_request->wiphy != wiphy) |