aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/reg.c
diff options
context:
space:
mode:
authorLuis R. Rodriguez <lrodriguez@atheros.com>2009-01-07 20:43:34 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-01-29 15:59:59 -0500
commit3e0c3ff36c4c7b9e39af7d600e399664ca04e817 (patch)
tree837fa4d480d5f18bfb7bdb5f59c0caf5341d38eb /net/wireless/reg.c
parent560e28e14f69ad3440a6e8c283dcfd37e1e41c2d (diff)
cfg80211: allow multiple driver regulatory_hints()
We add support for multiple drivers to provide a regulatory_hint() on a system by adding a wiphy specific regulatory domain cache. This allows drivers to keep around cache their own regulatory domain structure queried from CRDA. We handle conflicts by intersecting multiple regulatory domains, each driver will stick to its own regulatory domain though unless a country IE has been received and processed. If the user already requested a regulatory domain and a driver requests the same regulatory domain then simply copy to the driver's regd the same regulatory domain and do not call CRDA, do not collect $200. Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Acked-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r--net/wireless/reg.c104
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 */
805static int freq_reg_info(u32 center_freq, u32 *bandwidth, 806static 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 = &regd->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, &reg_rule); 873 &max_bandwidth, &reg_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
965static 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(&regd->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
1086new_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 */
1440void reg_device_remove(struct wiphy *wiphy) 1519void 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)