diff options
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 407 |
1 files changed, 307 insertions, 100 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index f180db0de66c..1ad0f39fe091 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -32,10 +32,14 @@ | |||
32 | * rely on some SHA1 checksum of the regdomain for example. | 32 | * rely on some SHA1 checksum of the regdomain for example. |
33 | * | 33 | * |
34 | */ | 34 | */ |
35 | |||
36 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
37 | |||
35 | #include <linux/kernel.h> | 38 | #include <linux/kernel.h> |
36 | #include <linux/slab.h> | 39 | #include <linux/slab.h> |
37 | #include <linux/list.h> | 40 | #include <linux/list.h> |
38 | #include <linux/random.h> | 41 | #include <linux/random.h> |
42 | #include <linux/ctype.h> | ||
39 | #include <linux/nl80211.h> | 43 | #include <linux/nl80211.h> |
40 | #include <linux/platform_device.h> | 44 | #include <linux/platform_device.h> |
41 | #include <net/cfg80211.h> | 45 | #include <net/cfg80211.h> |
@@ -47,7 +51,7 @@ | |||
47 | #ifdef CONFIG_CFG80211_REG_DEBUG | 51 | #ifdef CONFIG_CFG80211_REG_DEBUG |
48 | #define REG_DBG_PRINT(format, args...) \ | 52 | #define REG_DBG_PRINT(format, args...) \ |
49 | do { \ | 53 | do { \ |
50 | printk(KERN_DEBUG format , ## args); \ | 54 | printk(KERN_DEBUG pr_fmt(format), ##args); \ |
51 | } while (0) | 55 | } while (0) |
52 | #else | 56 | #else |
53 | #define REG_DBG_PRINT(args...) | 57 | #define REG_DBG_PRINT(args...) |
@@ -59,6 +63,10 @@ static struct regulatory_request *last_request; | |||
59 | /* To trigger userspace events */ | 63 | /* To trigger userspace events */ |
60 | static struct platform_device *reg_pdev; | 64 | static struct platform_device *reg_pdev; |
61 | 65 | ||
66 | static struct device_type reg_device_type = { | ||
67 | .uevent = reg_device_uevent, | ||
68 | }; | ||
69 | |||
62 | /* | 70 | /* |
63 | * Central wireless core regulatory domains, we only need two, | 71 | * Central wireless core regulatory domains, we only need two, |
64 | * the current one and a world regulatory domain in case we have no | 72 | * the current one and a world regulatory domain in case we have no |
@@ -73,7 +81,11 @@ const struct ieee80211_regdomain *cfg80211_regdomain; | |||
73 | * - last_request | 81 | * - last_request |
74 | */ | 82 | */ |
75 | static DEFINE_MUTEX(reg_mutex); | 83 | static DEFINE_MUTEX(reg_mutex); |
76 | #define assert_reg_lock() WARN_ON(!mutex_is_locked(®_mutex)) | 84 | |
85 | static inline void assert_reg_lock(void) | ||
86 | { | ||
87 | lockdep_assert_held(®_mutex); | ||
88 | } | ||
77 | 89 | ||
78 | /* Used to queue up regulatory hints */ | 90 | /* Used to queue up regulatory hints */ |
79 | static LIST_HEAD(reg_requests_list); | 91 | static LIST_HEAD(reg_requests_list); |
@@ -91,6 +103,12 @@ struct reg_beacon { | |||
91 | struct ieee80211_channel chan; | 103 | struct ieee80211_channel chan; |
92 | }; | 104 | }; |
93 | 105 | ||
106 | static void reg_todo(struct work_struct *work); | ||
107 | static DECLARE_WORK(reg_work, reg_todo); | ||
108 | |||
109 | static void reg_timeout_work(struct work_struct *work); | ||
110 | static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work); | ||
111 | |||
94 | /* We keep a static world regulatory domain in case of the absence of CRDA */ | 112 | /* We keep a static world regulatory domain in case of the absence of CRDA */ |
95 | static const struct ieee80211_regdomain world_regdom = { | 113 | static const struct ieee80211_regdomain world_regdom = { |
96 | .n_reg_rules = 5, | 114 | .n_reg_rules = 5, |
@@ -181,14 +199,6 @@ static bool is_alpha2_set(const char *alpha2) | |||
181 | return false; | 199 | return false; |
182 | } | 200 | } |
183 | 201 | ||
184 | static bool is_alpha_upper(char letter) | ||
185 | { | ||
186 | /* ASCII A - Z */ | ||
187 | if (letter >= 65 && letter <= 90) | ||
188 | return true; | ||
189 | return false; | ||
190 | } | ||
191 | |||
192 | static bool is_unknown_alpha2(const char *alpha2) | 202 | static bool is_unknown_alpha2(const char *alpha2) |
193 | { | 203 | { |
194 | if (!alpha2) | 204 | if (!alpha2) |
@@ -220,7 +230,7 @@ static bool is_an_alpha2(const char *alpha2) | |||
220 | { | 230 | { |
221 | if (!alpha2) | 231 | if (!alpha2) |
222 | return false; | 232 | return false; |
223 | if (is_alpha_upper(alpha2[0]) && is_alpha_upper(alpha2[1])) | 233 | if (isalpha(alpha2[0]) && isalpha(alpha2[1])) |
224 | return true; | 234 | return true; |
225 | return false; | 235 | return false; |
226 | } | 236 | } |
@@ -359,30 +369,21 @@ static inline void reg_regdb_query(const char *alpha2) {} | |||
359 | 369 | ||
360 | /* | 370 | /* |
361 | * This lets us keep regulatory code which is updated on a regulatory | 371 | * This lets us keep regulatory code which is updated on a regulatory |
362 | * basis in userspace. | 372 | * basis in userspace. Country information is filled in by |
373 | * reg_device_uevent | ||
363 | */ | 374 | */ |
364 | static int call_crda(const char *alpha2) | 375 | static int call_crda(const char *alpha2) |
365 | { | 376 | { |
366 | char country_env[9 + 2] = "COUNTRY="; | ||
367 | char *envp[] = { | ||
368 | country_env, | ||
369 | NULL | ||
370 | }; | ||
371 | |||
372 | if (!is_world_regdom((char *) alpha2)) | 377 | if (!is_world_regdom((char *) alpha2)) |
373 | printk(KERN_INFO "cfg80211: Calling CRDA for country: %c%c\n", | 378 | pr_info("Calling CRDA for country: %c%c\n", |
374 | alpha2[0], alpha2[1]); | 379 | alpha2[0], alpha2[1]); |
375 | else | 380 | else |
376 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " | 381 | pr_info("Calling CRDA to update world regulatory domain\n"); |
377 | "regulatory domain\n"); | ||
378 | 382 | ||
379 | /* query internal regulatory database (if it exists) */ | 383 | /* query internal regulatory database (if it exists) */ |
380 | reg_regdb_query(alpha2); | 384 | reg_regdb_query(alpha2); |
381 | 385 | ||
382 | country_env[8] = alpha2[0]; | 386 | return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); |
383 | country_env[9] = alpha2[1]; | ||
384 | |||
385 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); | ||
386 | } | 387 | } |
387 | 388 | ||
388 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ | 389 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ |
@@ -659,7 +660,8 @@ static int freq_reg_info_regd(struct wiphy *wiphy, | |||
659 | * Follow the driver's regulatory domain, if present, unless a country | 660 | * Follow the driver's regulatory domain, if present, unless a country |
660 | * IE has been processed or a user wants to help complaince further | 661 | * IE has been processed or a user wants to help complaince further |
661 | */ | 662 | */ |
662 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 663 | if (!custom_regd && |
664 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
663 | last_request->initiator != NL80211_REGDOM_SET_BY_USER && | 665 | last_request->initiator != NL80211_REGDOM_SET_BY_USER && |
664 | wiphy->regd) | 666 | wiphy->regd) |
665 | regd = wiphy->regd; | 667 | regd = wiphy->regd; |
@@ -670,11 +672,9 @@ static int freq_reg_info_regd(struct wiphy *wiphy, | |||
670 | for (i = 0; i < regd->n_reg_rules; i++) { | 672 | for (i = 0; i < regd->n_reg_rules; i++) { |
671 | const struct ieee80211_reg_rule *rr; | 673 | const struct ieee80211_reg_rule *rr; |
672 | const struct ieee80211_freq_range *fr = NULL; | 674 | const struct ieee80211_freq_range *fr = NULL; |
673 | const struct ieee80211_power_rule *pr = NULL; | ||
674 | 675 | ||
675 | rr = ®d->reg_rules[i]; | 676 | rr = ®d->reg_rules[i]; |
676 | fr = &rr->freq_range; | 677 | fr = &rr->freq_range; |
677 | pr = &rr->power_rule; | ||
678 | 678 | ||
679 | /* | 679 | /* |
680 | * We only need to know if one frequency rule was | 680 | * We only need to know if one frequency rule was |
@@ -714,6 +714,60 @@ int freq_reg_info(struct wiphy *wiphy, | |||
714 | } | 714 | } |
715 | EXPORT_SYMBOL(freq_reg_info); | 715 | EXPORT_SYMBOL(freq_reg_info); |
716 | 716 | ||
717 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
718 | static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) | ||
719 | { | ||
720 | switch (initiator) { | ||
721 | case NL80211_REGDOM_SET_BY_CORE: | ||
722 | return "Set by core"; | ||
723 | case NL80211_REGDOM_SET_BY_USER: | ||
724 | return "Set by user"; | ||
725 | case NL80211_REGDOM_SET_BY_DRIVER: | ||
726 | return "Set by driver"; | ||
727 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | ||
728 | return "Set by country IE"; | ||
729 | default: | ||
730 | WARN_ON(1); | ||
731 | return "Set by bug"; | ||
732 | } | ||
733 | } | ||
734 | |||
735 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | ||
736 | u32 desired_bw_khz, | ||
737 | const struct ieee80211_reg_rule *reg_rule) | ||
738 | { | ||
739 | const struct ieee80211_power_rule *power_rule; | ||
740 | const struct ieee80211_freq_range *freq_range; | ||
741 | char max_antenna_gain[32]; | ||
742 | |||
743 | power_rule = ®_rule->power_rule; | ||
744 | freq_range = ®_rule->freq_range; | ||
745 | |||
746 | if (!power_rule->max_antenna_gain) | ||
747 | snprintf(max_antenna_gain, 32, "N/A"); | ||
748 | else | ||
749 | snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); | ||
750 | |||
751 | REG_DBG_PRINT("Updating information on frequency %d MHz " | ||
752 | "for a %d MHz width channel with regulatory rule:\n", | ||
753 | chan->center_freq, | ||
754 | KHZ_TO_MHZ(desired_bw_khz)); | ||
755 | |||
756 | REG_DBG_PRINT("%d KHz - %d KHz @ KHz), (%s mBi, %d mBm)\n", | ||
757 | freq_range->start_freq_khz, | ||
758 | freq_range->end_freq_khz, | ||
759 | max_antenna_gain, | ||
760 | power_rule->max_eirp); | ||
761 | } | ||
762 | #else | ||
763 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | ||
764 | u32 desired_bw_khz, | ||
765 | const struct ieee80211_reg_rule *reg_rule) | ||
766 | { | ||
767 | return; | ||
768 | } | ||
769 | #endif | ||
770 | |||
717 | /* | 771 | /* |
718 | * Note that right now we assume the desired channel bandwidth | 772 | * Note that right now we assume the desired channel bandwidth |
719 | * is always 20 MHz for each individual channel (HT40 uses 20 MHz | 773 | * is always 20 MHz for each individual channel (HT40 uses 20 MHz |
@@ -723,7 +777,9 @@ EXPORT_SYMBOL(freq_reg_info); | |||
723 | * on the wiphy with the target_bw specified. Then we can simply use | 777 | * on the wiphy with the target_bw specified. Then we can simply use |
724 | * that below for the desired_bw_khz below. | 778 | * that below for the desired_bw_khz below. |
725 | */ | 779 | */ |
726 | static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | 780 | static void handle_channel(struct wiphy *wiphy, |
781 | enum nl80211_reg_initiator initiator, | ||
782 | enum ieee80211_band band, | ||
727 | unsigned int chan_idx) | 783 | unsigned int chan_idx) |
728 | { | 784 | { |
729 | int r; | 785 | int r; |
@@ -751,8 +807,27 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
751 | desired_bw_khz, | 807 | desired_bw_khz, |
752 | ®_rule); | 808 | ®_rule); |
753 | 809 | ||
754 | if (r) | 810 | if (r) { |
811 | /* | ||
812 | * We will disable all channels that do not match our | ||
813 | * received regulatory rule unless the hint is coming | ||
814 | * from a Country IE and the Country IE had no information | ||
815 | * about a band. The IEEE 802.11 spec allows for an AP | ||
816 | * to send only a subset of the regulatory rules allowed, | ||
817 | * so an AP in the US that only supports 2.4 GHz may only send | ||
818 | * a country IE with information for the 2.4 GHz band | ||
819 | * while 5 GHz is still supported. | ||
820 | */ | ||
821 | if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
822 | r == -ERANGE) | ||
823 | return; | ||
824 | |||
825 | REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); | ||
826 | chan->flags = IEEE80211_CHAN_DISABLED; | ||
755 | return; | 827 | return; |
828 | } | ||
829 | |||
830 | chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); | ||
756 | 831 | ||
757 | power_rule = ®_rule->power_rule; | 832 | power_rule = ®_rule->power_rule; |
758 | freq_range = ®_rule->freq_range; | 833 | freq_range = ®_rule->freq_range; |
@@ -764,7 +839,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
764 | request_wiphy && request_wiphy == wiphy && | 839 | request_wiphy && request_wiphy == wiphy && |
765 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { | 840 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { |
766 | /* | 841 | /* |
767 | * This gaurantees the driver's requested regulatory domain | 842 | * This guarantees the driver's requested regulatory domain |
768 | * will always be used as a base for further regulatory | 843 | * will always be used as a base for further regulatory |
769 | * settings | 844 | * settings |
770 | */ | 845 | */ |
@@ -787,7 +862,9 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
787 | chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); | 862 | chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); |
788 | } | 863 | } |
789 | 864 | ||
790 | static void handle_band(struct wiphy *wiphy, enum ieee80211_band band) | 865 | static void handle_band(struct wiphy *wiphy, |
866 | enum ieee80211_band band, | ||
867 | enum nl80211_reg_initiator initiator) | ||
791 | { | 868 | { |
792 | unsigned int i; | 869 | unsigned int i; |
793 | struct ieee80211_supported_band *sband; | 870 | struct ieee80211_supported_band *sband; |
@@ -796,24 +873,42 @@ static void handle_band(struct wiphy *wiphy, enum ieee80211_band band) | |||
796 | sband = wiphy->bands[band]; | 873 | sband = wiphy->bands[band]; |
797 | 874 | ||
798 | for (i = 0; i < sband->n_channels; i++) | 875 | for (i = 0; i < sband->n_channels; i++) |
799 | handle_channel(wiphy, band, i); | 876 | handle_channel(wiphy, initiator, band, i); |
800 | } | 877 | } |
801 | 878 | ||
802 | static bool ignore_reg_update(struct wiphy *wiphy, | 879 | static bool ignore_reg_update(struct wiphy *wiphy, |
803 | enum nl80211_reg_initiator initiator) | 880 | enum nl80211_reg_initiator initiator) |
804 | { | 881 | { |
805 | if (!last_request) | 882 | if (!last_request) { |
883 | REG_DBG_PRINT("Ignoring regulatory request %s since " | ||
884 | "last_request is not set\n", | ||
885 | reg_initiator_name(initiator)); | ||
806 | return true; | 886 | return true; |
887 | } | ||
888 | |||
807 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | 889 | if (initiator == NL80211_REGDOM_SET_BY_CORE && |
808 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) | 890 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { |
891 | REG_DBG_PRINT("Ignoring regulatory request %s " | ||
892 | "since the driver uses its own custom " | ||
893 | "regulatory domain ", | ||
894 | reg_initiator_name(initiator)); | ||
809 | return true; | 895 | return true; |
896 | } | ||
897 | |||
810 | /* | 898 | /* |
811 | * wiphy->regd will be set once the device has its own | 899 | * wiphy->regd will be set once the device has its own |
812 | * desired regulatory domain set | 900 | * desired regulatory domain set |
813 | */ | 901 | */ |
814 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && | 902 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && |
815 | !is_world_regdom(last_request->alpha2)) | 903 | initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
904 | !is_world_regdom(last_request->alpha2)) { | ||
905 | REG_DBG_PRINT("Ignoring regulatory request %s " | ||
906 | "since the driver requires its own regulaotry " | ||
907 | "domain to be set first", | ||
908 | reg_initiator_name(initiator)); | ||
816 | return true; | 909 | return true; |
910 | } | ||
911 | |||
817 | return false; | 912 | return false; |
818 | } | 913 | } |
819 | 914 | ||
@@ -1033,7 +1128,7 @@ void wiphy_update_regulatory(struct wiphy *wiphy, | |||
1033 | goto out; | 1128 | goto out; |
1034 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1129 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
1035 | if (wiphy->bands[band]) | 1130 | if (wiphy->bands[band]) |
1036 | handle_band(wiphy, band); | 1131 | handle_band(wiphy, band, initiator); |
1037 | } | 1132 | } |
1038 | out: | 1133 | out: |
1039 | reg_process_beacons(wiphy); | 1134 | reg_process_beacons(wiphy); |
@@ -1069,10 +1164,17 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1069 | regd); | 1164 | regd); |
1070 | 1165 | ||
1071 | if (r) { | 1166 | if (r) { |
1167 | REG_DBG_PRINT("Disabling freq %d MHz as custom " | ||
1168 | "regd has no rule that fits a %d MHz " | ||
1169 | "wide channel\n", | ||
1170 | chan->center_freq, | ||
1171 | KHZ_TO_MHZ(desired_bw_khz)); | ||
1072 | chan->flags = IEEE80211_CHAN_DISABLED; | 1172 | chan->flags = IEEE80211_CHAN_DISABLED; |
1073 | return; | 1173 | return; |
1074 | } | 1174 | } |
1075 | 1175 | ||
1176 | chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); | ||
1177 | |||
1076 | power_rule = ®_rule->power_rule; | 1178 | power_rule = ®_rule->power_rule; |
1077 | freq_range = ®_rule->freq_range; | 1179 | freq_range = ®_rule->freq_range; |
1078 | 1180 | ||
@@ -1170,7 +1272,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
1170 | return 0; | 1272 | return 0; |
1171 | return -EALREADY; | 1273 | return -EALREADY; |
1172 | } | 1274 | } |
1173 | return REG_INTERSECT; | 1275 | return 0; |
1174 | case NL80211_REGDOM_SET_BY_DRIVER: | 1276 | case NL80211_REGDOM_SET_BY_DRIVER: |
1175 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1277 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { |
1176 | if (regdom_changes(pending_request->alpha2)) | 1278 | if (regdom_changes(pending_request->alpha2)) |
@@ -1218,6 +1320,24 @@ static int ignore_request(struct wiphy *wiphy, | |||
1218 | return -EINVAL; | 1320 | return -EINVAL; |
1219 | } | 1321 | } |
1220 | 1322 | ||
1323 | static void reg_set_request_processed(void) | ||
1324 | { | ||
1325 | bool need_more_processing = false; | ||
1326 | |||
1327 | last_request->processed = true; | ||
1328 | |||
1329 | spin_lock(®_requests_lock); | ||
1330 | if (!list_empty(®_requests_list)) | ||
1331 | need_more_processing = true; | ||
1332 | spin_unlock(®_requests_lock); | ||
1333 | |||
1334 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) | ||
1335 | cancel_delayed_work_sync(®_timeout); | ||
1336 | |||
1337 | if (need_more_processing) | ||
1338 | schedule_work(®_work); | ||
1339 | } | ||
1340 | |||
1221 | /** | 1341 | /** |
1222 | * __regulatory_hint - hint to the wireless core a regulatory domain | 1342 | * __regulatory_hint - hint to the wireless core a regulatory domain |
1223 | * @wiphy: if the hint comes from country information from an AP, this | 1343 | * @wiphy: if the hint comes from country information from an AP, this |
@@ -1293,8 +1413,10 @@ new_request: | |||
1293 | * have applied the requested regulatory domain before we just | 1413 | * have applied the requested regulatory domain before we just |
1294 | * inform userspace we have processed the request | 1414 | * inform userspace we have processed the request |
1295 | */ | 1415 | */ |
1296 | if (r == -EALREADY) | 1416 | if (r == -EALREADY) { |
1297 | nl80211_send_reg_change_event(last_request); | 1417 | nl80211_send_reg_change_event(last_request); |
1418 | reg_set_request_processed(); | ||
1419 | } | ||
1298 | return r; | 1420 | return r; |
1299 | } | 1421 | } |
1300 | 1422 | ||
@@ -1310,45 +1432,70 @@ static void reg_process_hint(struct regulatory_request *reg_request) | |||
1310 | 1432 | ||
1311 | BUG_ON(!reg_request->alpha2); | 1433 | BUG_ON(!reg_request->alpha2); |
1312 | 1434 | ||
1313 | mutex_lock(&cfg80211_mutex); | ||
1314 | mutex_lock(®_mutex); | ||
1315 | |||
1316 | if (wiphy_idx_valid(reg_request->wiphy_idx)) | 1435 | if (wiphy_idx_valid(reg_request->wiphy_idx)) |
1317 | wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); | 1436 | wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); |
1318 | 1437 | ||
1319 | if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1438 | if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
1320 | !wiphy) { | 1439 | !wiphy) { |
1321 | kfree(reg_request); | 1440 | kfree(reg_request); |
1322 | goto out; | 1441 | return; |
1323 | } | 1442 | } |
1324 | 1443 | ||
1325 | r = __regulatory_hint(wiphy, reg_request); | 1444 | r = __regulatory_hint(wiphy, reg_request); |
1326 | /* This is required so that the orig_* parameters are saved */ | 1445 | /* This is required so that the orig_* parameters are saved */ |
1327 | if (r == -EALREADY && wiphy && | 1446 | if (r == -EALREADY && wiphy && |
1328 | wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) | 1447 | wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { |
1329 | wiphy_update_regulatory(wiphy, initiator); | 1448 | wiphy_update_regulatory(wiphy, initiator); |
1330 | out: | 1449 | return; |
1331 | mutex_unlock(®_mutex); | 1450 | } |
1332 | mutex_unlock(&cfg80211_mutex); | 1451 | |
1452 | /* | ||
1453 | * We only time out user hints, given that they should be the only | ||
1454 | * source of bogus requests. | ||
1455 | */ | ||
1456 | if (r != -EALREADY && | ||
1457 | reg_request->initiator == NL80211_REGDOM_SET_BY_USER) | ||
1458 | schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); | ||
1333 | } | 1459 | } |
1334 | 1460 | ||
1335 | /* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */ | 1461 | /* |
1462 | * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* | ||
1463 | * Regulatory hints come on a first come first serve basis and we | ||
1464 | * must process each one atomically. | ||
1465 | */ | ||
1336 | static void reg_process_pending_hints(void) | 1466 | static void reg_process_pending_hints(void) |
1337 | { | 1467 | { |
1338 | struct regulatory_request *reg_request; | 1468 | struct regulatory_request *reg_request; |
1339 | 1469 | ||
1470 | mutex_lock(&cfg80211_mutex); | ||
1471 | mutex_lock(®_mutex); | ||
1472 | |||
1473 | /* When last_request->processed becomes true this will be rescheduled */ | ||
1474 | if (last_request && !last_request->processed) { | ||
1475 | REG_DBG_PRINT("Pending regulatory request, waiting " | ||
1476 | "for it to be processed..."); | ||
1477 | goto out; | ||
1478 | } | ||
1479 | |||
1340 | spin_lock(®_requests_lock); | 1480 | spin_lock(®_requests_lock); |
1341 | while (!list_empty(®_requests_list)) { | ||
1342 | reg_request = list_first_entry(®_requests_list, | ||
1343 | struct regulatory_request, | ||
1344 | list); | ||
1345 | list_del_init(®_request->list); | ||
1346 | 1481 | ||
1482 | if (list_empty(®_requests_list)) { | ||
1347 | spin_unlock(®_requests_lock); | 1483 | spin_unlock(®_requests_lock); |
1348 | reg_process_hint(reg_request); | 1484 | goto out; |
1349 | spin_lock(®_requests_lock); | ||
1350 | } | 1485 | } |
1486 | |||
1487 | reg_request = list_first_entry(®_requests_list, | ||
1488 | struct regulatory_request, | ||
1489 | list); | ||
1490 | list_del_init(®_request->list); | ||
1491 | |||
1351 | spin_unlock(®_requests_lock); | 1492 | spin_unlock(®_requests_lock); |
1493 | |||
1494 | reg_process_hint(reg_request); | ||
1495 | |||
1496 | out: | ||
1497 | mutex_unlock(®_mutex); | ||
1498 | mutex_unlock(&cfg80211_mutex); | ||
1352 | } | 1499 | } |
1353 | 1500 | ||
1354 | /* Processes beacon hints -- this has nothing to do with country IEs */ | 1501 | /* Processes beacon hints -- this has nothing to do with country IEs */ |
@@ -1395,10 +1542,13 @@ static void reg_todo(struct work_struct *work) | |||
1395 | reg_process_pending_beacon_hints(); | 1542 | reg_process_pending_beacon_hints(); |
1396 | } | 1543 | } |
1397 | 1544 | ||
1398 | static DECLARE_WORK(reg_work, reg_todo); | ||
1399 | |||
1400 | static void queue_regulatory_request(struct regulatory_request *request) | 1545 | static void queue_regulatory_request(struct regulatory_request *request) |
1401 | { | 1546 | { |
1547 | if (isalpha(request->alpha2[0])) | ||
1548 | request->alpha2[0] = toupper(request->alpha2[0]); | ||
1549 | if (isalpha(request->alpha2[1])) | ||
1550 | request->alpha2[1] = toupper(request->alpha2[1]); | ||
1551 | |||
1402 | spin_lock(®_requests_lock); | 1552 | spin_lock(®_requests_lock); |
1403 | list_add_tail(&request->list, ®_requests_list); | 1553 | list_add_tail(&request->list, ®_requests_list); |
1404 | spin_unlock(®_requests_lock); | 1554 | spin_unlock(®_requests_lock); |
@@ -1426,12 +1576,7 @@ static int regulatory_hint_core(const char *alpha2) | |||
1426 | request->alpha2[1] = alpha2[1]; | 1576 | request->alpha2[1] = alpha2[1]; |
1427 | request->initiator = NL80211_REGDOM_SET_BY_CORE; | 1577 | request->initiator = NL80211_REGDOM_SET_BY_CORE; |
1428 | 1578 | ||
1429 | /* | 1579 | queue_regulatory_request(request); |
1430 | * This ensures last_request is populated once modules | ||
1431 | * come swinging in and calling regulatory hints and | ||
1432 | * wiphy_apply_custom_regulatory(). | ||
1433 | */ | ||
1434 | reg_process_hint(request); | ||
1435 | 1580 | ||
1436 | return 0; | 1581 | return 0; |
1437 | } | 1582 | } |
@@ -1557,7 +1702,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) | |||
1557 | if (is_user_regdom_saved()) { | 1702 | if (is_user_regdom_saved()) { |
1558 | /* Unless we're asked to ignore it and reset it */ | 1703 | /* Unless we're asked to ignore it and reset it */ |
1559 | if (reset_user) { | 1704 | if (reset_user) { |
1560 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | 1705 | REG_DBG_PRINT("Restoring regulatory settings " |
1561 | "including user preference\n"); | 1706 | "including user preference\n"); |
1562 | user_alpha2[0] = '9'; | 1707 | user_alpha2[0] = '9'; |
1563 | user_alpha2[1] = '7'; | 1708 | user_alpha2[1] = '7'; |
@@ -1568,7 +1713,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) | |||
1568 | * back as they were for a full restore. | 1713 | * back as they were for a full restore. |
1569 | */ | 1714 | */ |
1570 | if (!is_world_regdom(ieee80211_regdom)) { | 1715 | if (!is_world_regdom(ieee80211_regdom)) { |
1571 | REG_DBG_PRINT("cfg80211: Keeping preference on " | 1716 | REG_DBG_PRINT("Keeping preference on " |
1572 | "module parameter ieee80211_regdom: %c%c\n", | 1717 | "module parameter ieee80211_regdom: %c%c\n", |
1573 | ieee80211_regdom[0], | 1718 | ieee80211_regdom[0], |
1574 | ieee80211_regdom[1]); | 1719 | ieee80211_regdom[1]); |
@@ -1576,7 +1721,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) | |||
1576 | alpha2[1] = ieee80211_regdom[1]; | 1721 | alpha2[1] = ieee80211_regdom[1]; |
1577 | } | 1722 | } |
1578 | } else { | 1723 | } else { |
1579 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | 1724 | REG_DBG_PRINT("Restoring regulatory settings " |
1580 | "while preserving user preference for: %c%c\n", | 1725 | "while preserving user preference for: %c%c\n", |
1581 | user_alpha2[0], | 1726 | user_alpha2[0], |
1582 | user_alpha2[1]); | 1727 | user_alpha2[1]); |
@@ -1584,14 +1729,14 @@ static void restore_alpha2(char *alpha2, bool reset_user) | |||
1584 | alpha2[1] = user_alpha2[1]; | 1729 | alpha2[1] = user_alpha2[1]; |
1585 | } | 1730 | } |
1586 | } else if (!is_world_regdom(ieee80211_regdom)) { | 1731 | } else if (!is_world_regdom(ieee80211_regdom)) { |
1587 | REG_DBG_PRINT("cfg80211: Keeping preference on " | 1732 | REG_DBG_PRINT("Keeping preference on " |
1588 | "module parameter ieee80211_regdom: %c%c\n", | 1733 | "module parameter ieee80211_regdom: %c%c\n", |
1589 | ieee80211_regdom[0], | 1734 | ieee80211_regdom[0], |
1590 | ieee80211_regdom[1]); | 1735 | ieee80211_regdom[1]); |
1591 | alpha2[0] = ieee80211_regdom[0]; | 1736 | alpha2[0] = ieee80211_regdom[0]; |
1592 | alpha2[1] = ieee80211_regdom[1]; | 1737 | alpha2[1] = ieee80211_regdom[1]; |
1593 | } else | 1738 | } else |
1594 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings\n"); | 1739 | REG_DBG_PRINT("Restoring regulatory settings\n"); |
1595 | } | 1740 | } |
1596 | 1741 | ||
1597 | /* | 1742 | /* |
@@ -1613,6 +1758,8 @@ static void restore_regulatory_settings(bool reset_user) | |||
1613 | { | 1758 | { |
1614 | char alpha2[2]; | 1759 | char alpha2[2]; |
1615 | struct reg_beacon *reg_beacon, *btmp; | 1760 | struct reg_beacon *reg_beacon, *btmp; |
1761 | struct regulatory_request *reg_request, *tmp; | ||
1762 | LIST_HEAD(tmp_reg_req_list); | ||
1616 | 1763 | ||
1617 | mutex_lock(&cfg80211_mutex); | 1764 | mutex_lock(&cfg80211_mutex); |
1618 | mutex_lock(®_mutex); | 1765 | mutex_lock(®_mutex); |
@@ -1620,6 +1767,25 @@ static void restore_regulatory_settings(bool reset_user) | |||
1620 | reset_regdomains(); | 1767 | reset_regdomains(); |
1621 | restore_alpha2(alpha2, reset_user); | 1768 | restore_alpha2(alpha2, reset_user); |
1622 | 1769 | ||
1770 | /* | ||
1771 | * If there's any pending requests we simply | ||
1772 | * stash them to a temporary pending queue and | ||
1773 | * add then after we've restored regulatory | ||
1774 | * settings. | ||
1775 | */ | ||
1776 | spin_lock(®_requests_lock); | ||
1777 | if (!list_empty(®_requests_list)) { | ||
1778 | list_for_each_entry_safe(reg_request, tmp, | ||
1779 | ®_requests_list, list) { | ||
1780 | if (reg_request->initiator != | ||
1781 | NL80211_REGDOM_SET_BY_USER) | ||
1782 | continue; | ||
1783 | list_del(®_request->list); | ||
1784 | list_add_tail(®_request->list, &tmp_reg_req_list); | ||
1785 | } | ||
1786 | } | ||
1787 | spin_unlock(®_requests_lock); | ||
1788 | |||
1623 | /* Clear beacon hints */ | 1789 | /* Clear beacon hints */ |
1624 | spin_lock_bh(®_pending_beacons_lock); | 1790 | spin_lock_bh(®_pending_beacons_lock); |
1625 | if (!list_empty(®_pending_beacons)) { | 1791 | if (!list_empty(®_pending_beacons)) { |
@@ -1654,21 +1820,44 @@ static void restore_regulatory_settings(bool reset_user) | |||
1654 | */ | 1820 | */ |
1655 | if (is_an_alpha2(alpha2)) | 1821 | if (is_an_alpha2(alpha2)) |
1656 | regulatory_hint_user(user_alpha2); | 1822 | regulatory_hint_user(user_alpha2); |
1657 | } | ||
1658 | 1823 | ||
1824 | if (list_empty(&tmp_reg_req_list)) | ||
1825 | return; | ||
1826 | |||
1827 | mutex_lock(&cfg80211_mutex); | ||
1828 | mutex_lock(®_mutex); | ||
1829 | |||
1830 | spin_lock(®_requests_lock); | ||
1831 | list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { | ||
1832 | REG_DBG_PRINT("Adding request for country %c%c back " | ||
1833 | "into the queue\n", | ||
1834 | reg_request->alpha2[0], | ||
1835 | reg_request->alpha2[1]); | ||
1836 | list_del(®_request->list); | ||
1837 | list_add_tail(®_request->list, ®_requests_list); | ||
1838 | } | ||
1839 | spin_unlock(®_requests_lock); | ||
1840 | |||
1841 | mutex_unlock(®_mutex); | ||
1842 | mutex_unlock(&cfg80211_mutex); | ||
1843 | |||
1844 | REG_DBG_PRINT("Kicking the queue\n"); | ||
1845 | |||
1846 | schedule_work(®_work); | ||
1847 | } | ||
1659 | 1848 | ||
1660 | void regulatory_hint_disconnect(void) | 1849 | void regulatory_hint_disconnect(void) |
1661 | { | 1850 | { |
1662 | REG_DBG_PRINT("cfg80211: All devices are disconnected, going to " | 1851 | REG_DBG_PRINT("All devices are disconnected, going to " |
1663 | "restore regulatory settings\n"); | 1852 | "restore regulatory settings\n"); |
1664 | restore_regulatory_settings(false); | 1853 | restore_regulatory_settings(false); |
1665 | } | 1854 | } |
1666 | 1855 | ||
1667 | static bool freq_is_chan_12_13_14(u16 freq) | 1856 | static bool freq_is_chan_12_13_14(u16 freq) |
1668 | { | 1857 | { |
1669 | if (freq == ieee80211_channel_to_frequency(12) || | 1858 | if (freq == ieee80211_channel_to_frequency(12, IEEE80211_BAND_2GHZ) || |
1670 | freq == ieee80211_channel_to_frequency(13) || | 1859 | freq == ieee80211_channel_to_frequency(13, IEEE80211_BAND_2GHZ) || |
1671 | freq == ieee80211_channel_to_frequency(14)) | 1860 | freq == ieee80211_channel_to_frequency(14, IEEE80211_BAND_2GHZ)) |
1672 | return true; | 1861 | return true; |
1673 | return false; | 1862 | return false; |
1674 | } | 1863 | } |
@@ -1689,7 +1878,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, | |||
1689 | if (!reg_beacon) | 1878 | if (!reg_beacon) |
1690 | return -ENOMEM; | 1879 | return -ENOMEM; |
1691 | 1880 | ||
1692 | REG_DBG_PRINT("cfg80211: Found new beacon on " | 1881 | REG_DBG_PRINT("Found new beacon on " |
1693 | "frequency: %d MHz (Ch %d) on %s\n", | 1882 | "frequency: %d MHz (Ch %d) on %s\n", |
1694 | beacon_chan->center_freq, | 1883 | beacon_chan->center_freq, |
1695 | ieee80211_frequency_to_channel(beacon_chan->center_freq), | 1884 | ieee80211_frequency_to_channel(beacon_chan->center_freq), |
@@ -1719,8 +1908,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) | |||
1719 | const struct ieee80211_freq_range *freq_range = NULL; | 1908 | const struct ieee80211_freq_range *freq_range = NULL; |
1720 | const struct ieee80211_power_rule *power_rule = NULL; | 1909 | const struct ieee80211_power_rule *power_rule = NULL; |
1721 | 1910 | ||
1722 | printk(KERN_INFO " (start_freq - end_freq @ bandwidth), " | 1911 | pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n"); |
1723 | "(max_antenna_gain, max_eirp)\n"); | ||
1724 | 1912 | ||
1725 | for (i = 0; i < rd->n_reg_rules; i++) { | 1913 | for (i = 0; i < rd->n_reg_rules; i++) { |
1726 | reg_rule = &rd->reg_rules[i]; | 1914 | reg_rule = &rd->reg_rules[i]; |
@@ -1732,16 +1920,14 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) | |||
1732 | * in certain regions | 1920 | * in certain regions |
1733 | */ | 1921 | */ |
1734 | if (power_rule->max_antenna_gain) | 1922 | if (power_rule->max_antenna_gain) |
1735 | printk(KERN_INFO " (%d KHz - %d KHz @ %d KHz), " | 1923 | pr_info(" (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n", |
1736 | "(%d mBi, %d mBm)\n", | ||
1737 | freq_range->start_freq_khz, | 1924 | freq_range->start_freq_khz, |
1738 | freq_range->end_freq_khz, | 1925 | freq_range->end_freq_khz, |
1739 | freq_range->max_bandwidth_khz, | 1926 | freq_range->max_bandwidth_khz, |
1740 | power_rule->max_antenna_gain, | 1927 | power_rule->max_antenna_gain, |
1741 | power_rule->max_eirp); | 1928 | power_rule->max_eirp); |
1742 | else | 1929 | else |
1743 | printk(KERN_INFO " (%d KHz - %d KHz @ %d KHz), " | 1930 | pr_info(" (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n", |
1744 | "(N/A, %d mBm)\n", | ||
1745 | freq_range->start_freq_khz, | 1931 | freq_range->start_freq_khz, |
1746 | freq_range->end_freq_khz, | 1932 | freq_range->end_freq_khz, |
1747 | freq_range->max_bandwidth_khz, | 1933 | freq_range->max_bandwidth_khz, |
@@ -1760,27 +1946,20 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
1760 | rdev = cfg80211_rdev_by_wiphy_idx( | 1946 | rdev = cfg80211_rdev_by_wiphy_idx( |
1761 | last_request->wiphy_idx); | 1947 | last_request->wiphy_idx); |
1762 | if (rdev) { | 1948 | if (rdev) { |
1763 | printk(KERN_INFO "cfg80211: Current regulatory " | 1949 | pr_info("Current regulatory domain updated by AP to: %c%c\n", |
1764 | "domain updated by AP to: %c%c\n", | ||
1765 | rdev->country_ie_alpha2[0], | 1950 | rdev->country_ie_alpha2[0], |
1766 | rdev->country_ie_alpha2[1]); | 1951 | rdev->country_ie_alpha2[1]); |
1767 | } else | 1952 | } else |
1768 | printk(KERN_INFO "cfg80211: Current regulatory " | 1953 | pr_info("Current regulatory domain intersected:\n"); |
1769 | "domain intersected:\n"); | ||
1770 | } else | 1954 | } else |
1771 | printk(KERN_INFO "cfg80211: Current regulatory " | 1955 | pr_info("Current regulatory domain intersected:\n"); |
1772 | "domain intersected:\n"); | ||
1773 | } else if (is_world_regdom(rd->alpha2)) | 1956 | } else if (is_world_regdom(rd->alpha2)) |
1774 | printk(KERN_INFO "cfg80211: World regulatory " | 1957 | pr_info("World regulatory domain updated:\n"); |
1775 | "domain updated:\n"); | ||
1776 | else { | 1958 | else { |
1777 | if (is_unknown_alpha2(rd->alpha2)) | 1959 | if (is_unknown_alpha2(rd->alpha2)) |
1778 | printk(KERN_INFO "cfg80211: Regulatory domain " | 1960 | pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); |
1779 | "changed to driver built-in settings " | ||
1780 | "(unknown country)\n"); | ||
1781 | else | 1961 | else |
1782 | printk(KERN_INFO "cfg80211: Regulatory domain " | 1962 | pr_info("Regulatory domain changed to country: %c%c\n", |
1783 | "changed to country: %c%c\n", | ||
1784 | rd->alpha2[0], rd->alpha2[1]); | 1963 | rd->alpha2[0], rd->alpha2[1]); |
1785 | } | 1964 | } |
1786 | print_rd_rules(rd); | 1965 | print_rd_rules(rd); |
@@ -1788,8 +1967,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
1788 | 1967 | ||
1789 | static void print_regdomain_info(const struct ieee80211_regdomain *rd) | 1968 | static void print_regdomain_info(const struct ieee80211_regdomain *rd) |
1790 | { | 1969 | { |
1791 | printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", | 1970 | pr_info("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); |
1792 | rd->alpha2[0], rd->alpha2[1]); | ||
1793 | print_rd_rules(rd); | 1971 | print_rd_rules(rd); |
1794 | } | 1972 | } |
1795 | 1973 | ||
@@ -1840,8 +2018,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
1840 | return -EINVAL; | 2018 | return -EINVAL; |
1841 | 2019 | ||
1842 | if (!is_valid_rd(rd)) { | 2020 | if (!is_valid_rd(rd)) { |
1843 | printk(KERN_ERR "cfg80211: Invalid " | 2021 | pr_err("Invalid regulatory domain detected:\n"); |
1844 | "regulatory domain detected:\n"); | ||
1845 | print_regdomain_info(rd); | 2022 | print_regdomain_info(rd); |
1846 | return -EINVAL; | 2023 | return -EINVAL; |
1847 | } | 2024 | } |
@@ -1957,11 +2134,32 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
1957 | 2134 | ||
1958 | nl80211_send_reg_change_event(last_request); | 2135 | nl80211_send_reg_change_event(last_request); |
1959 | 2136 | ||
2137 | reg_set_request_processed(); | ||
2138 | |||
1960 | mutex_unlock(®_mutex); | 2139 | mutex_unlock(®_mutex); |
1961 | 2140 | ||
1962 | return r; | 2141 | return r; |
1963 | } | 2142 | } |
1964 | 2143 | ||
2144 | #ifdef CONFIG_HOTPLUG | ||
2145 | int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
2146 | { | ||
2147 | if (last_request && !last_request->processed) { | ||
2148 | if (add_uevent_var(env, "COUNTRY=%c%c", | ||
2149 | last_request->alpha2[0], | ||
2150 | last_request->alpha2[1])) | ||
2151 | return -ENOMEM; | ||
2152 | } | ||
2153 | |||
2154 | return 0; | ||
2155 | } | ||
2156 | #else | ||
2157 | int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
2158 | { | ||
2159 | return -ENODEV; | ||
2160 | } | ||
2161 | #endif /* CONFIG_HOTPLUG */ | ||
2162 | |||
1965 | /* Caller must hold cfg80211_mutex */ | 2163 | /* Caller must hold cfg80211_mutex */ |
1966 | void reg_device_remove(struct wiphy *wiphy) | 2164 | void reg_device_remove(struct wiphy *wiphy) |
1967 | { | 2165 | { |
@@ -1985,6 +2183,13 @@ out: | |||
1985 | mutex_unlock(®_mutex); | 2183 | mutex_unlock(®_mutex); |
1986 | } | 2184 | } |
1987 | 2185 | ||
2186 | static void reg_timeout_work(struct work_struct *work) | ||
2187 | { | ||
2188 | REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " | ||
2189 | "restoring regulatory settings"); | ||
2190 | restore_regulatory_settings(true); | ||
2191 | } | ||
2192 | |||
1988 | int __init regulatory_init(void) | 2193 | int __init regulatory_init(void) |
1989 | { | 2194 | { |
1990 | int err = 0; | 2195 | int err = 0; |
@@ -1993,6 +2198,8 @@ int __init regulatory_init(void) | |||
1993 | if (IS_ERR(reg_pdev)) | 2198 | if (IS_ERR(reg_pdev)) |
1994 | return PTR_ERR(reg_pdev); | 2199 | return PTR_ERR(reg_pdev); |
1995 | 2200 | ||
2201 | reg_pdev->dev.type = ®_device_type; | ||
2202 | |||
1996 | spin_lock_init(®_requests_lock); | 2203 | spin_lock_init(®_requests_lock); |
1997 | spin_lock_init(®_pending_beacons_lock); | 2204 | spin_lock_init(®_pending_beacons_lock); |
1998 | 2205 | ||
@@ -2013,8 +2220,7 @@ int __init regulatory_init(void) | |||
2013 | * early boot for call_usermodehelper(). For now treat these | 2220 | * early boot for call_usermodehelper(). For now treat these |
2014 | * errors as non-fatal. | 2221 | * errors as non-fatal. |
2015 | */ | 2222 | */ |
2016 | printk(KERN_ERR "cfg80211: kobject_uevent_env() was unable " | 2223 | pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); |
2017 | "to call CRDA during init"); | ||
2018 | #ifdef CONFIG_CFG80211_REG_DEBUG | 2224 | #ifdef CONFIG_CFG80211_REG_DEBUG |
2019 | /* We want to find out exactly why when debugging */ | 2225 | /* We want to find out exactly why when debugging */ |
2020 | WARN_ON(err); | 2226 | WARN_ON(err); |
@@ -2037,6 +2243,7 @@ void /* __init_or_exit */ regulatory_exit(void) | |||
2037 | struct reg_beacon *reg_beacon, *btmp; | 2243 | struct reg_beacon *reg_beacon, *btmp; |
2038 | 2244 | ||
2039 | cancel_work_sync(®_work); | 2245 | cancel_work_sync(®_work); |
2246 | cancel_delayed_work_sync(®_timeout); | ||
2040 | 2247 | ||
2041 | mutex_lock(&cfg80211_mutex); | 2248 | mutex_lock(&cfg80211_mutex); |
2042 | mutex_lock(®_mutex); | 2249 | mutex_lock(®_mutex); |