aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/reg.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2009-02-03 15:41:58 -0500
committerDavid S. Miller <davem@davemloft.net>2009-02-03 15:41:58 -0500
commit1725d409caba16ea5fc694bd50e95e79e8ced11a (patch)
tree688fe26dd4ceda5364692f0ce307aadb6f04f331 /net/wireless/reg.c
parentb3ff29d2ccfe3af065a9b393699a8fbf2abd1b15 (diff)
parentb8abde45d7d6ab9e8ceced9b5990eeb1149d0b97 (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.c285
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 */
68struct 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 */
78static struct regulatory_request *last_request; 46static 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/** 761static 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 */
813static 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 = &regd->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}
813EXPORT_SYMBOL(freq_reg_info);
814
815int 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
853static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, 822static 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, &reg_rule); 840 &max_bandwidth, &reg_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 = &reg_rule->power_rule; 877 power_rule = &reg_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
952void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) 941void 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
955static 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, &reg_rule, regd);
973
974 if (r) {
975 chan->flags = IEEE80211_CHAN_DISABLED;
976 return;
977 }
978
979 power_rule = &reg_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
987static 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 */
1001void 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}
1010EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
1011
1012static 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(&regd->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
1152new_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
1072void regulatory_hint(struct wiphy *wiphy, const char *alpha2) 1186void 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}
1080EXPORT_SYMBOL(regulatory_hint); 1199EXPORT_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 */
1448void reg_device_remove(struct wiphy *wiphy) 1590void 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)