aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/regulatory.h1
-rw-r--r--net/wireless/reg.c157
-rw-r--r--net/wireless/reg.h18
-rw-r--r--net/wireless/sme.c40
4 files changed, 213 insertions, 3 deletions
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index 47995b81c5d7..f873ee37f7e4 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -39,6 +39,7 @@ enum environment_cap {
39 * 00 - World regulatory domain 39 * 00 - World regulatory domain
40 * 99 - built by driver but a specific alpha2 cannot be determined 40 * 99 - built by driver but a specific alpha2 cannot be determined
41 * 98 - result of an intersection between two regulatory domains 41 * 98 - result of an intersection between two regulatory domains
42 * 97 - regulatory domain has not yet been configured
42 * @intersect: indicates whether the wireless core should intersect 43 * @intersect: indicates whether the wireless core should intersect
43 * the requested regulatory domain with the presently set regulatory 44 * the requested regulatory domain with the presently set regulatory
44 * domain. 45 * domain.
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 5dcda28b6f04..ed89c59bb431 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -134,6 +134,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
134 &world_regdom; 134 &world_regdom;
135 135
136static char *ieee80211_regdom = "00"; 136static char *ieee80211_regdom = "00";
137static char user_alpha2[2];
137 138
138module_param(ieee80211_regdom, charp, 0444); 139module_param(ieee80211_regdom, charp, 0444);
139MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); 140MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
@@ -252,6 +253,27 @@ static bool regdom_changes(const char *alpha2)
252 return true; 253 return true;
253} 254}
254 255
256/*
257 * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets
258 * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER
259 * has ever been issued.
260 */
261static bool is_user_regdom_saved(void)
262{
263 if (user_alpha2[0] == '9' && user_alpha2[1] == '7')
264 return false;
265
266 /* This would indicate a mistake on the design */
267 if (WARN((!is_world_regdom(user_alpha2) &&
268 !is_an_alpha2(user_alpha2)),
269 "Unexpected user alpha2: %c%c\n",
270 user_alpha2[0],
271 user_alpha2[1]))
272 return false;
273
274 return true;
275}
276
255/** 277/**
256 * country_ie_integrity_changes - tells us if the country IE has changed 278 * country_ie_integrity_changes - tells us if the country IE has changed
257 * @checksum: checksum of country IE of fields we are interested in 279 * @checksum: checksum of country IE of fields we are interested in
@@ -1646,7 +1668,7 @@ static int ignore_request(struct wiphy *wiphy,
1646 1668
1647 switch (pending_request->initiator) { 1669 switch (pending_request->initiator) {
1648 case NL80211_REGDOM_SET_BY_CORE: 1670 case NL80211_REGDOM_SET_BY_CORE:
1649 return -EINVAL; 1671 return 0;
1650 case NL80211_REGDOM_SET_BY_COUNTRY_IE: 1672 case NL80211_REGDOM_SET_BY_COUNTRY_IE:
1651 1673
1652 last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); 1674 last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
@@ -1785,6 +1807,11 @@ new_request:
1785 1807
1786 pending_request = NULL; 1808 pending_request = NULL;
1787 1809
1810 if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
1811 user_alpha2[0] = last_request->alpha2[0];
1812 user_alpha2[1] = last_request->alpha2[1];
1813 }
1814
1788 /* When r == REG_INTERSECT we do need to call CRDA */ 1815 /* When r == REG_INTERSECT we do need to call CRDA */
1789 if (r < 0) { 1816 if (r < 0) {
1790 /* 1817 /*
@@ -1904,12 +1931,16 @@ static void queue_regulatory_request(struct regulatory_request *request)
1904 schedule_work(&reg_work); 1931 schedule_work(&reg_work);
1905} 1932}
1906 1933
1907/* Core regulatory hint -- happens once during cfg80211_init() */ 1934/*
1935 * Core regulatory hint -- happens during cfg80211_init()
1936 * and when we restore regulatory settings.
1937 */
1908static int regulatory_hint_core(const char *alpha2) 1938static int regulatory_hint_core(const char *alpha2)
1909{ 1939{
1910 struct regulatory_request *request; 1940 struct regulatory_request *request;
1911 1941
1912 BUG_ON(last_request); 1942 kfree(last_request);
1943 last_request = NULL;
1913 1944
1914 request = kzalloc(sizeof(struct regulatory_request), 1945 request = kzalloc(sizeof(struct regulatory_request),
1915 GFP_KERNEL); 1946 GFP_KERNEL);
@@ -2107,6 +2138,123 @@ out:
2107 mutex_unlock(&reg_mutex); 2138 mutex_unlock(&reg_mutex);
2108} 2139}
2109 2140
2141static void restore_alpha2(char *alpha2, bool reset_user)
2142{
2143 /* indicates there is no alpha2 to consider for restoration */
2144 alpha2[0] = '9';
2145 alpha2[1] = '7';
2146
2147 /* The user setting has precedence over the module parameter */
2148 if (is_user_regdom_saved()) {
2149 /* Unless we're asked to ignore it and reset it */
2150 if (reset_user) {
2151 REG_DBG_PRINT("cfg80211: Restoring regulatory settings "
2152 "including user preference\n");
2153 user_alpha2[0] = '9';
2154 user_alpha2[1] = '7';
2155
2156 /*
2157 * If we're ignoring user settings, we still need to
2158 * check the module parameter to ensure we put things
2159 * back as they were for a full restore.
2160 */
2161 if (!is_world_regdom(ieee80211_regdom)) {
2162 REG_DBG_PRINT("cfg80211: Keeping preference on "
2163 "module parameter ieee80211_regdom: %c%c\n",
2164 ieee80211_regdom[0],
2165 ieee80211_regdom[1]);
2166 alpha2[0] = ieee80211_regdom[0];
2167 alpha2[1] = ieee80211_regdom[1];
2168 }
2169 } else {
2170 REG_DBG_PRINT("cfg80211: Restoring regulatory settings "
2171 "while preserving user preference for: %c%c\n",
2172 user_alpha2[0],
2173 user_alpha2[1]);
2174 alpha2[0] = user_alpha2[0];
2175 alpha2[1] = user_alpha2[1];
2176 }
2177 } else if (!is_world_regdom(ieee80211_regdom)) {
2178 REG_DBG_PRINT("cfg80211: Keeping preference on "
2179 "module parameter ieee80211_regdom: %c%c\n",
2180 ieee80211_regdom[0],
2181 ieee80211_regdom[1]);
2182 alpha2[0] = ieee80211_regdom[0];
2183 alpha2[1] = ieee80211_regdom[1];
2184 } else
2185 REG_DBG_PRINT("cfg80211: Restoring regulatory settings\n");
2186}
2187
2188/*
2189 * Restoring regulatory settings involves ingoring any
2190 * possibly stale country IE information and user regulatory
2191 * settings if so desired, this includes any beacon hints
2192 * learned as we could have traveled outside to another country
2193 * after disconnection. To restore regulatory settings we do
2194 * exactly what we did at bootup:
2195 *
2196 * - send a core regulatory hint
2197 * - send a user regulatory hint if applicable
2198 *
2199 * Device drivers that send a regulatory hint for a specific country
2200 * keep their own regulatory domain on wiphy->regd so that does does
2201 * not need to be remembered.
2202 */
2203static void restore_regulatory_settings(bool reset_user)
2204{
2205 char alpha2[2];
2206 struct reg_beacon *reg_beacon, *btmp;
2207
2208 mutex_lock(&cfg80211_mutex);
2209 mutex_lock(&reg_mutex);
2210
2211 reset_regdomains();
2212 restore_alpha2(alpha2, reset_user);
2213
2214 /* Clear beacon hints */
2215 spin_lock_bh(&reg_pending_beacons_lock);
2216 if (!list_empty(&reg_pending_beacons)) {
2217 list_for_each_entry_safe(reg_beacon, btmp,
2218 &reg_pending_beacons, list) {
2219 list_del(&reg_beacon->list);
2220 kfree(reg_beacon);
2221 }
2222 }
2223 spin_unlock_bh(&reg_pending_beacons_lock);
2224
2225 if (!list_empty(&reg_beacon_list)) {
2226 list_for_each_entry_safe(reg_beacon, btmp,
2227 &reg_beacon_list, list) {
2228 list_del(&reg_beacon->list);
2229 kfree(reg_beacon);
2230 }
2231 }
2232
2233 /* First restore to the basic regulatory settings */
2234 cfg80211_regdomain = cfg80211_world_regdom;
2235
2236 mutex_unlock(&reg_mutex);
2237 mutex_unlock(&cfg80211_mutex);
2238
2239 regulatory_hint_core(cfg80211_regdomain->alpha2);
2240
2241 /*
2242 * This restores the ieee80211_regdom module parameter
2243 * preference or the last user requested regulatory
2244 * settings, user regulatory settings takes precedence.
2245 */
2246 if (is_an_alpha2(alpha2))
2247 regulatory_hint_user(user_alpha2);
2248}
2249
2250
2251void regulatory_hint_disconnect(void)
2252{
2253 REG_DBG_PRINT("cfg80211: All devices are disconnected, going to "
2254 "restore regulatory settings\n");
2255 restore_regulatory_settings(false);
2256}
2257
2110static bool freq_is_chan_12_13_14(u16 freq) 2258static bool freq_is_chan_12_13_14(u16 freq)
2111{ 2259{
2112 if (freq == ieee80211_channel_to_frequency(12) || 2260 if (freq == ieee80211_channel_to_frequency(12) ||
@@ -2496,6 +2644,9 @@ int regulatory_init(void)
2496 2644
2497 cfg80211_regdomain = cfg80211_world_regdom; 2645 cfg80211_regdomain = cfg80211_world_regdom;
2498 2646
2647 user_alpha2[0] = '9';
2648 user_alpha2[1] = '7';
2649
2499 /* We always try to get an update for the static regdomain */ 2650 /* We always try to get an update for the static regdomain */
2500 err = regulatory_hint_core(cfg80211_regdomain->alpha2); 2651 err = regulatory_hint_core(cfg80211_regdomain->alpha2);
2501 if (err) { 2652 if (err) {
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 3018508226ab..b26224a9f3bc 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -63,4 +63,22 @@ void regulatory_hint_11d(struct wiphy *wiphy,
63 u8 *country_ie, 63 u8 *country_ie,
64 u8 country_ie_len); 64 u8 country_ie_len);
65 65
66/**
67 * regulatory_hint_disconnect - informs all devices have been disconneted
68 *
69 * Regulotory rules can be enhanced further upon scanning and upon
70 * connection to an AP. These rules become stale if we disconnect
71 * and go to another country, whether or not we suspend and resume.
72 * If we suspend, go to another country and resume we'll automatically
73 * get disconnected shortly after resuming and things will be reset as well.
74 * This routine is a helper to restore regulatory settings to how they were
75 * prior to our first connect attempt. This includes ignoring country IE and
76 * beacon regulatory hints. The ieee80211_regdom module parameter will always
77 * be respected but if a user had set the regulatory domain that will take
78 * precedence.
79 *
80 * Must be called from process context.
81 */
82void regulatory_hint_disconnect(void);
83
66#endif /* __NET_WIRELESS_REG_H */ 84#endif /* __NET_WIRELESS_REG_H */
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 745c37e7992e..17fde0da1b08 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -34,6 +34,44 @@ struct cfg80211_conn {
34 bool auto_auth, prev_bssid_valid; 34 bool auto_auth, prev_bssid_valid;
35}; 35};
36 36
37bool cfg80211_is_all_idle(void)
38{
39 struct cfg80211_registered_device *rdev;
40 struct wireless_dev *wdev;
41 bool is_all_idle = true;
42
43 mutex_lock(&cfg80211_mutex);
44
45 /*
46 * All devices must be idle as otherwise if you are actively
47 * scanning some new beacon hints could be learned and would
48 * count as new regulatory hints.
49 */
50 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
51 cfg80211_lock_rdev(rdev);
52 list_for_each_entry(wdev, &rdev->netdev_list, list) {
53 wdev_lock(wdev);
54 if (wdev->sme_state != CFG80211_SME_IDLE)
55 is_all_idle = false;
56 wdev_unlock(wdev);
57 }
58 cfg80211_unlock_rdev(rdev);
59 }
60
61 mutex_unlock(&cfg80211_mutex);
62
63 return is_all_idle;
64}
65
66static void disconnect_work(struct work_struct *work)
67{
68 if (!cfg80211_is_all_idle())
69 return;
70
71 regulatory_hint_disconnect();
72}
73
74static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
37 75
38static int cfg80211_conn_scan(struct wireless_dev *wdev) 76static int cfg80211_conn_scan(struct wireless_dev *wdev)
39{ 77{
@@ -658,6 +696,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
658 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 696 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
659 wdev->wext.connect.ssid_len = 0; 697 wdev->wext.connect.ssid_len = 0;
660#endif 698#endif
699
700 schedule_work(&cfg80211_disconnect_work);
661} 701}
662 702
663void cfg80211_disconnected(struct net_device *dev, u16 reason, 703void cfg80211_disconnected(struct net_device *dev, u16 reason,