diff options
author | David S. Miller <davem@davemloft.net> | 2009-02-03 15:41:58 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-02-03 15:41:58 -0500 |
commit | 1725d409caba16ea5fc694bd50e95e79e8ced11a (patch) | |
tree | 688fe26dd4ceda5364692f0ce307aadb6f04f331 /net/wireless/reg.c | |
parent | b3ff29d2ccfe3af065a9b393699a8fbf2abd1b15 (diff) | |
parent | b8abde45d7d6ab9e8ceced9b5990eeb1149d0b97 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 285 |
1 files changed, 214 insertions, 71 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 85c9034c59b2..f643d3981102 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -42,38 +42,6 @@ | |||
42 | #include "core.h" | 42 | #include "core.h" |
43 | #include "reg.h" | 43 | #include "reg.h" |
44 | 44 | ||
45 | /** | ||
46 | * struct regulatory_request - receipt of last regulatory request | ||
47 | * | ||
48 | * @wiphy: this is set if this request's initiator is | ||
49 | * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This | ||
50 | * can be used by the wireless core to deal with conflicts | ||
51 | * and potentially inform users of which devices specifically | ||
52 | * cased the conflicts. | ||
53 | * @initiator: indicates who sent this request, could be any of | ||
54 | * of those set in reg_set_by, %REGDOM_SET_BY_* | ||
55 | * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested | ||
56 | * regulatory domain. We have a few special codes: | ||
57 | * 00 - World regulatory domain | ||
58 | * 99 - built by driver but a specific alpha2 cannot be determined | ||
59 | * 98 - result of an intersection between two regulatory domains | ||
60 | * @intersect: indicates whether the wireless core should intersect | ||
61 | * the requested regulatory domain with the presently set regulatory | ||
62 | * domain. | ||
63 | * @country_ie_checksum: checksum of the last processed and accepted | ||
64 | * country IE | ||
65 | * @country_ie_env: lets us know if the AP is telling us we are outdoor, | ||
66 | * indoor, or if it doesn't matter | ||
67 | */ | ||
68 | struct regulatory_request { | ||
69 | struct wiphy *wiphy; | ||
70 | enum reg_set_by initiator; | ||
71 | char alpha2[2]; | ||
72 | bool intersect; | ||
73 | u32 country_ie_checksum; | ||
74 | enum environment_cap country_ie_env; | ||
75 | }; | ||
76 | |||
77 | /* Receipt of information from last regulatory request */ | 45 | /* Receipt of information from last regulatory request */ |
78 | static struct regulatory_request *last_request; | 46 | static struct regulatory_request *last_request; |
79 | 47 | ||
@@ -790,42 +758,35 @@ static u32 map_regdom_flags(u32 rd_flags) | |||
790 | return channel_flags; | 758 | return channel_flags; |
791 | } | 759 | } |
792 | 760 | ||
793 | /** | 761 | static int freq_reg_info_regd(struct wiphy *wiphy, |
794 | * freq_reg_info - get regulatory information for the given frequency | 762 | u32 center_freq, |
795 | * @center_freq: Frequency in KHz for which we want regulatory information for | 763 | u32 *bandwidth, |
796 | * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one | 764 | const struct ieee80211_reg_rule **reg_rule, |
797 | * you can set this to 0. If this frequency is allowed we then set | 765 | const struct ieee80211_regdomain *custom_regd) |
798 | * this value to the maximum allowed bandwidth. | ||
799 | * @reg_rule: the regulatory rule which we have for this frequency | ||
800 | * | ||
801 | * Use this function to get the regulatory rule for a specific frequency on | ||
802 | * a given wireless device. If the device has a specific regulatory domain | ||
803 | * it wants to follow we respect that unless a country IE has been received | ||
804 | * and processed already. | ||
805 | * | ||
806 | * Returns 0 if it was able to find a valid regulatory rule which does | ||
807 | * apply to the given center_freq otherwise it returns non-zero. It will | ||
808 | * also return -ERANGE if we determine the given center_freq does not even have | ||
809 | * a regulatory rule for a frequency range in the center_freq's band. See | ||
810 | * freq_in_rule_band() for our current definition of a band -- this is purely | ||
811 | * subjective and right now its 802.11 specific. | ||
812 | */ | ||
813 | static int freq_reg_info(u32 center_freq, u32 *bandwidth, | ||
814 | const struct ieee80211_reg_rule **reg_rule) | ||
815 | { | 766 | { |
816 | int i; | 767 | int i; |
817 | bool band_rule_found = false; | 768 | bool band_rule_found = false; |
769 | const struct ieee80211_regdomain *regd; | ||
818 | u32 max_bandwidth = 0; | 770 | u32 max_bandwidth = 0; |
819 | 771 | ||
820 | if (!cfg80211_regdomain) | 772 | regd = custom_regd ? custom_regd : cfg80211_regdomain; |
773 | |||
774 | /* Follow the driver's regulatory domain, if present, unless a country | ||
775 | * IE has been processed or a user wants to help complaince further */ | ||
776 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && | ||
777 | last_request->initiator != REGDOM_SET_BY_USER && | ||
778 | wiphy->regd) | ||
779 | regd = wiphy->regd; | ||
780 | |||
781 | if (!regd) | ||
821 | return -EINVAL; | 782 | return -EINVAL; |
822 | 783 | ||
823 | for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { | 784 | for (i = 0; i < regd->n_reg_rules; i++) { |
824 | const struct ieee80211_reg_rule *rr; | 785 | const struct ieee80211_reg_rule *rr; |
825 | const struct ieee80211_freq_range *fr = NULL; | 786 | const struct ieee80211_freq_range *fr = NULL; |
826 | const struct ieee80211_power_rule *pr = NULL; | 787 | const struct ieee80211_power_rule *pr = NULL; |
827 | 788 | ||
828 | rr = &cfg80211_regdomain->reg_rules[i]; | 789 | rr = ®d->reg_rules[i]; |
829 | fr = &rr->freq_range; | 790 | fr = &rr->freq_range; |
830 | pr = &rr->power_rule; | 791 | pr = &rr->power_rule; |
831 | 792 | ||
@@ -849,6 +810,14 @@ static int freq_reg_info(u32 center_freq, u32 *bandwidth, | |||
849 | 810 | ||
850 | return !max_bandwidth; | 811 | return !max_bandwidth; |
851 | } | 812 | } |
813 | EXPORT_SYMBOL(freq_reg_info); | ||
814 | |||
815 | int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, | ||
816 | const struct ieee80211_reg_rule **reg_rule) | ||
817 | { | ||
818 | return freq_reg_info_regd(wiphy, center_freq, | ||
819 | bandwidth, reg_rule, NULL); | ||
820 | } | ||
852 | 821 | ||
853 | static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | 822 | static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, |
854 | unsigned int chan_idx) | 823 | unsigned int chan_idx) |
@@ -867,7 +836,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
867 | 836 | ||
868 | flags = chan->orig_flags; | 837 | flags = chan->orig_flags; |
869 | 838 | ||
870 | r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq), | 839 | r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), |
871 | &max_bandwidth, ®_rule); | 840 | &max_bandwidth, ®_rule); |
872 | 841 | ||
873 | if (r) { | 842 | if (r) { |
@@ -907,6 +876,22 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
907 | 876 | ||
908 | power_rule = ®_rule->power_rule; | 877 | power_rule = ®_rule->power_rule; |
909 | 878 | ||
879 | if (last_request->initiator == REGDOM_SET_BY_DRIVER && | ||
880 | last_request->wiphy && last_request->wiphy == wiphy && | ||
881 | last_request->wiphy->strict_regulatory) { | ||
882 | /* This gaurantees the driver's requested regulatory domain | ||
883 | * will always be used as a base for further regulatory | ||
884 | * settings */ | ||
885 | chan->flags = chan->orig_flags = | ||
886 | map_regdom_flags(reg_rule->flags); | ||
887 | chan->max_antenna_gain = chan->orig_mag = | ||
888 | (int) MBI_TO_DBI(power_rule->max_antenna_gain); | ||
889 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | ||
890 | chan->max_power = chan->orig_mpwr = | ||
891 | (int) MBM_TO_DBM(power_rule->max_eirp); | ||
892 | return; | ||
893 | } | ||
894 | |||
910 | chan->flags = flags | map_regdom_flags(reg_rule->flags); | 895 | chan->flags = flags | map_regdom_flags(reg_rule->flags); |
911 | chan->max_antenna_gain = min(chan->orig_mag, | 896 | chan->max_antenna_gain = min(chan->orig_mag, |
912 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); | 897 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); |
@@ -935,7 +920,12 @@ static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) | |||
935 | if (!last_request) | 920 | if (!last_request) |
936 | return true; | 921 | return true; |
937 | if (setby == REGDOM_SET_BY_CORE && | 922 | if (setby == REGDOM_SET_BY_CORE && |
938 | wiphy->fw_handles_regulatory) | 923 | wiphy->custom_regulatory) |
924 | return true; | ||
925 | /* wiphy->regd will be set once the device has its own | ||
926 | * desired regulatory domain set */ | ||
927 | if (wiphy->strict_regulatory && !wiphy->regd && | ||
928 | !is_world_regdom(last_request->alpha2)) | ||
939 | return true; | 929 | return true; |
940 | return false; | 930 | return false; |
941 | } | 931 | } |
@@ -945,20 +935,103 @@ static void update_all_wiphy_regulatory(enum reg_set_by setby) | |||
945 | struct cfg80211_registered_device *drv; | 935 | struct cfg80211_registered_device *drv; |
946 | 936 | ||
947 | list_for_each_entry(drv, &cfg80211_drv_list, list) | 937 | list_for_each_entry(drv, &cfg80211_drv_list, list) |
948 | if (!ignore_reg_update(&drv->wiphy, setby)) | 938 | wiphy_update_regulatory(&drv->wiphy, setby); |
949 | wiphy_update_regulatory(&drv->wiphy, setby); | ||
950 | } | 939 | } |
951 | 940 | ||
952 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | 941 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) |
953 | { | 942 | { |
954 | enum ieee80211_band band; | 943 | enum ieee80211_band band; |
944 | |||
945 | if (ignore_reg_update(wiphy, setby)) | ||
946 | return; | ||
955 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 947 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
956 | if (wiphy->bands[band]) | 948 | if (wiphy->bands[band]) |
957 | handle_band(wiphy, band); | 949 | handle_band(wiphy, band); |
958 | if (wiphy->reg_notifier) | 950 | } |
959 | wiphy->reg_notifier(wiphy, setby); | 951 | if (wiphy->reg_notifier) |
952 | wiphy->reg_notifier(wiphy, last_request); | ||
953 | } | ||
954 | |||
955 | static void handle_channel_custom(struct wiphy *wiphy, | ||
956 | enum ieee80211_band band, | ||
957 | unsigned int chan_idx, | ||
958 | const struct ieee80211_regdomain *regd) | ||
959 | { | ||
960 | int r; | ||
961 | u32 max_bandwidth = 0; | ||
962 | const struct ieee80211_reg_rule *reg_rule = NULL; | ||
963 | const struct ieee80211_power_rule *power_rule = NULL; | ||
964 | struct ieee80211_supported_band *sband; | ||
965 | struct ieee80211_channel *chan; | ||
966 | |||
967 | sband = wiphy->bands[band]; | ||
968 | BUG_ON(chan_idx >= sband->n_channels); | ||
969 | chan = &sband->channels[chan_idx]; | ||
970 | |||
971 | r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), | ||
972 | &max_bandwidth, ®_rule, regd); | ||
973 | |||
974 | if (r) { | ||
975 | chan->flags = IEEE80211_CHAN_DISABLED; | ||
976 | return; | ||
977 | } | ||
978 | |||
979 | power_rule = ®_rule->power_rule; | ||
980 | |||
981 | chan->flags |= map_regdom_flags(reg_rule->flags); | ||
982 | chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); | ||
983 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | ||
984 | chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); | ||
985 | } | ||
986 | |||
987 | static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, | ||
988 | const struct ieee80211_regdomain *regd) | ||
989 | { | ||
990 | unsigned int i; | ||
991 | struct ieee80211_supported_band *sband; | ||
992 | |||
993 | BUG_ON(!wiphy->bands[band]); | ||
994 | sband = wiphy->bands[band]; | ||
995 | |||
996 | for (i = 0; i < sband->n_channels; i++) | ||
997 | handle_channel_custom(wiphy, band, i, regd); | ||
998 | } | ||
999 | |||
1000 | /* Used by drivers prior to wiphy registration */ | ||
1001 | void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | ||
1002 | const struct ieee80211_regdomain *regd) | ||
1003 | { | ||
1004 | enum ieee80211_band band; | ||
1005 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
1006 | if (wiphy->bands[band]) | ||
1007 | handle_band_custom(wiphy, band, regd); | ||
960 | } | 1008 | } |
961 | } | 1009 | } |
1010 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | ||
1011 | |||
1012 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
1013 | const struct ieee80211_regdomain *src_regd) | ||
1014 | { | ||
1015 | struct ieee80211_regdomain *regd; | ||
1016 | int size_of_regd = 0; | ||
1017 | unsigned int i; | ||
1018 | |||
1019 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
1020 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
1021 | |||
1022 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
1023 | if (!regd) | ||
1024 | return -ENOMEM; | ||
1025 | |||
1026 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
1027 | |||
1028 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
1029 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
1030 | sizeof(struct ieee80211_reg_rule)); | ||
1031 | |||
1032 | *dst_regd = regd; | ||
1033 | return 0; | ||
1034 | } | ||
962 | 1035 | ||
963 | /* Return value which can be used by ignore_request() to indicate | 1036 | /* Return value which can be used by ignore_request() to indicate |
964 | * it has been determined we should intersect two regulatory domains */ | 1037 | * it has been determined we should intersect two regulatory domains */ |
@@ -1007,9 +1080,14 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1007 | } | 1080 | } |
1008 | return REG_INTERSECT; | 1081 | return REG_INTERSECT; |
1009 | case REGDOM_SET_BY_DRIVER: | 1082 | case REGDOM_SET_BY_DRIVER: |
1010 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | 1083 | if (last_request->initiator == REGDOM_SET_BY_CORE) { |
1084 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
1085 | return 0; | ||
1086 | if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | ||
1087 | return 0; | ||
1011 | return -EALREADY; | 1088 | return -EALREADY; |
1012 | return 0; | 1089 | } |
1090 | return REG_INTERSECT; | ||
1013 | case REGDOM_SET_BY_USER: | 1091 | case REGDOM_SET_BY_USER: |
1014 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | 1092 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) |
1015 | return REG_INTERSECT; | 1093 | return REG_INTERSECT; |
@@ -1018,6 +1096,20 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1018 | if (last_request->initiator == REGDOM_SET_BY_USER && | 1096 | if (last_request->initiator == REGDOM_SET_BY_USER && |
1019 | last_request->intersect) | 1097 | last_request->intersect) |
1020 | return -EOPNOTSUPP; | 1098 | return -EOPNOTSUPP; |
1099 | /* Process user requests only after previous user/driver/core | ||
1100 | * requests have been processed */ | ||
1101 | if (last_request->initiator == REGDOM_SET_BY_CORE || | ||
1102 | last_request->initiator == REGDOM_SET_BY_DRIVER || | ||
1103 | last_request->initiator == REGDOM_SET_BY_USER) { | ||
1104 | if (!alpha2_equal(last_request->alpha2, | ||
1105 | cfg80211_regdomain->alpha2)) | ||
1106 | return -EAGAIN; | ||
1107 | } | ||
1108 | |||
1109 | if (!is_old_static_regdom(cfg80211_regdomain) && | ||
1110 | alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | ||
1111 | return -EALREADY; | ||
1112 | |||
1021 | return 0; | 1113 | return 0; |
1022 | } | 1114 | } |
1023 | 1115 | ||
@@ -1036,11 +1128,28 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1036 | 1128 | ||
1037 | r = ignore_request(wiphy, set_by, alpha2); | 1129 | r = ignore_request(wiphy, set_by, alpha2); |
1038 | 1130 | ||
1039 | if (r == REG_INTERSECT) | 1131 | if (r == REG_INTERSECT) { |
1132 | if (set_by == REGDOM_SET_BY_DRIVER) { | ||
1133 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | ||
1134 | if (r) | ||
1135 | return r; | ||
1136 | } | ||
1040 | intersect = true; | 1137 | intersect = true; |
1041 | else if (r) | 1138 | } else if (r) { |
1139 | /* If the regulatory domain being requested by the | ||
1140 | * driver has already been set just copy it to the | ||
1141 | * wiphy */ | ||
1142 | if (r == -EALREADY && set_by == REGDOM_SET_BY_DRIVER) { | ||
1143 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | ||
1144 | if (r) | ||
1145 | return r; | ||
1146 | r = -EALREADY; | ||
1147 | goto new_request; | ||
1148 | } | ||
1042 | return r; | 1149 | return r; |
1150 | } | ||
1043 | 1151 | ||
1152 | new_request: | ||
1044 | request = kzalloc(sizeof(struct regulatory_request), | 1153 | request = kzalloc(sizeof(struct regulatory_request), |
1045 | GFP_KERNEL); | 1154 | GFP_KERNEL); |
1046 | if (!request) | 1155 | if (!request) |
@@ -1056,6 +1165,11 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1056 | 1165 | ||
1057 | kfree(last_request); | 1166 | kfree(last_request); |
1058 | last_request = request; | 1167 | last_request = request; |
1168 | |||
1169 | /* When r == REG_INTERSECT we do need to call CRDA */ | ||
1170 | if (r < 0) | ||
1171 | return r; | ||
1172 | |||
1059 | /* | 1173 | /* |
1060 | * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled | 1174 | * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled |
1061 | * AND if CRDA is NOT present nothing will happen, if someone | 1175 | * AND if CRDA is NOT present nothing will happen, if someone |
@@ -1071,10 +1185,15 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1071 | 1185 | ||
1072 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) | 1186 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) |
1073 | { | 1187 | { |
1188 | int r; | ||
1074 | BUG_ON(!alpha2); | 1189 | BUG_ON(!alpha2); |
1075 | 1190 | ||
1076 | mutex_lock(&cfg80211_drv_mutex); | 1191 | mutex_lock(&cfg80211_drv_mutex); |
1077 | __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY); | 1192 | r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, |
1193 | alpha2, 0, ENVIRON_ANY); | ||
1194 | /* This is required so that the orig_* parameters are saved */ | ||
1195 | if (r == -EALREADY && wiphy->strict_regulatory) | ||
1196 | wiphy_update_regulatory(wiphy, REGDOM_SET_BY_DRIVER); | ||
1078 | mutex_unlock(&cfg80211_drv_mutex); | 1197 | mutex_unlock(&cfg80211_drv_mutex); |
1079 | } | 1198 | } |
1080 | EXPORT_SYMBOL(regulatory_hint); | 1199 | EXPORT_SYMBOL(regulatory_hint); |
@@ -1247,7 +1366,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
1247 | "domain intersected: \n"); | 1366 | "domain intersected: \n"); |
1248 | } else | 1367 | } else |
1249 | printk(KERN_INFO "cfg80211: Current regulatory " | 1368 | printk(KERN_INFO "cfg80211: Current regulatory " |
1250 | "intersected: \n"); | 1369 | "domain intersected: \n"); |
1251 | } else if (is_world_regdom(rd->alpha2)) | 1370 | } else if (is_world_regdom(rd->alpha2)) |
1252 | printk(KERN_INFO "cfg80211: World regulatory " | 1371 | printk(KERN_INFO "cfg80211: World regulatory " |
1253 | "domain updated:\n"); | 1372 | "domain updated:\n"); |
@@ -1349,6 +1468,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
1349 | } | 1468 | } |
1350 | 1469 | ||
1351 | if (!last_request->intersect) { | 1470 | if (!last_request->intersect) { |
1471 | int r; | ||
1472 | |||
1473 | if (last_request->initiator != REGDOM_SET_BY_DRIVER) { | ||
1474 | reset_regdomains(); | ||
1475 | cfg80211_regdomain = rd; | ||
1476 | return 0; | ||
1477 | } | ||
1478 | |||
1479 | /* For a driver hint, lets copy the regulatory domain the | ||
1480 | * driver wanted to the wiphy to deal with conflicts */ | ||
1481 | |||
1482 | BUG_ON(last_request->wiphy->regd); | ||
1483 | |||
1484 | r = reg_copy_regd(&last_request->wiphy->regd, rd); | ||
1485 | if (r) | ||
1486 | return r; | ||
1487 | |||
1352 | reset_regdomains(); | 1488 | reset_regdomains(); |
1353 | cfg80211_regdomain = rd; | 1489 | cfg80211_regdomain = rd; |
1354 | return 0; | 1490 | return 0; |
@@ -1362,8 +1498,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
1362 | if (!intersected_rd) | 1498 | if (!intersected_rd) |
1363 | return -EINVAL; | 1499 | return -EINVAL; |
1364 | 1500 | ||
1365 | /* We can trash what CRDA provided now */ | 1501 | /* We can trash what CRDA provided now. |
1366 | kfree(rd); | 1502 | * However if a driver requested this specific regulatory |
1503 | * domain we keep it for its private use */ | ||
1504 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | ||
1505 | last_request->wiphy->regd = rd; | ||
1506 | else | ||
1507 | kfree(rd); | ||
1508 | |||
1367 | rd = NULL; | 1509 | rd = NULL; |
1368 | 1510 | ||
1369 | reset_regdomains(); | 1511 | reset_regdomains(); |
@@ -1447,6 +1589,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
1447 | /* Caller must hold cfg80211_drv_mutex */ | 1589 | /* Caller must hold cfg80211_drv_mutex */ |
1448 | void reg_device_remove(struct wiphy *wiphy) | 1590 | void reg_device_remove(struct wiphy *wiphy) |
1449 | { | 1591 | { |
1592 | kfree(wiphy->regd); | ||
1450 | if (!last_request || !last_request->wiphy) | 1593 | if (!last_request || !last_request->wiphy) |
1451 | return; | 1594 | return; |
1452 | if (last_request->wiphy != wiphy) | 1595 | if (last_request->wiphy != wiphy) |