Skip to content

Commit 09d989d

Browse files
Luis R. Rodriguezlinvjw
authored andcommitted
cfg80211: add regulatory hint disconnect support
This adds a new regulatory hint to be used when we know all devices have been disconnected and idle. This can happen when we suspend, for instance. When we disconnect we can no longer assume the same regulatory rules learned from a country IE or beacon hints are applicable so restore regulatory settings to an initial state. Since driver hints are cached on the wiphy that called the hint, those hints are not reproduced onto cfg80211 as the wiphy will respect its own wiphy->regd regardless. Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
1 parent a2bff26 commit 09d989d

File tree

4 files changed

+213
-3
lines changed

4 files changed

+213
-3
lines changed

include/net/regulatory.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum environment_cap {
3939
* 00 - World regulatory domain
4040
* 99 - built by driver but a specific alpha2 cannot be determined
4141
* 98 - result of an intersection between two regulatory domains
42+
* 97 - regulatory domain has not yet been configured
4243
* @intersect: indicates whether the wireless core should intersect
4344
* the requested regulatory domain with the presently set regulatory
4445
* domain.

net/wireless/reg.c

Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
134134
&world_regdom;
135135

136136
static char *ieee80211_regdom = "00";
137+
static char user_alpha2[2];
137138

138139
module_param(ieee80211_regdom, charp, 0444);
139140
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
@@ -252,6 +253,27 @@ static bool regdom_changes(const char *alpha2)
252253
return true;
253254
}
254255

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+
*/
261+
static 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+
255277
/**
256278
* country_ie_integrity_changes - tells us if the country IE has changed
257279
* @checksum: checksum of country IE of fields we are interested in
@@ -1646,7 +1668,7 @@ static int ignore_request(struct wiphy *wiphy,
16461668

16471669
switch (pending_request->initiator) {
16481670
case NL80211_REGDOM_SET_BY_CORE:
1649-
return -EINVAL;
1671+
return 0;
16501672
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
16511673

16521674
last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
@@ -1785,6 +1807,11 @@ static int __regulatory_hint(struct wiphy *wiphy,
17851807

17861808
pending_request = NULL;
17871809

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+
17881815
/* When r == REG_INTERSECT we do need to call CRDA */
17891816
if (r < 0) {
17901817
/*
@@ -1904,12 +1931,16 @@ static void queue_regulatory_request(struct regulatory_request *request)
19041931
schedule_work(&reg_work);
19051932
}
19061933

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+
*/
19081938
static int regulatory_hint_core(const char *alpha2)
19091939
{
19101940
struct regulatory_request *request;
19111941

1912-
BUG_ON(last_request);
1942+
kfree(last_request);
1943+
last_request = NULL;
19131944

19141945
request = kzalloc(sizeof(struct regulatory_request),
19151946
GFP_KERNEL);
@@ -2107,6 +2138,123 @@ void regulatory_hint_11d(struct wiphy *wiphy,
21072138
mutex_unlock(&reg_mutex);
21082139
}
21092140

2141+
static 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+
*/
2203+
static 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+
2251+
void 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+
21102258
static bool freq_is_chan_12_13_14(u16 freq)
21112259
{
21122260
if (freq == ieee80211_channel_to_frequency(12) ||
@@ -2496,6 +2644,9 @@ int regulatory_init(void)
24962644

24972645
cfg80211_regdomain = cfg80211_world_regdom;
24982646

2647+
user_alpha2[0] = '9';
2648+
user_alpha2[1] = '7';
2649+
24992650
/* We always try to get an update for the static regdomain */
25002651
err = regulatory_hint_core(cfg80211_regdomain->alpha2);
25012652
if (err) {

net/wireless/reg.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,22 @@ void regulatory_hint_11d(struct wiphy *wiphy,
6363
u8 *country_ie,
6464
u8 country_ie_len);
6565

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+
*/
82+
void regulatory_hint_disconnect(void);
83+
6684
#endif /* __NET_WIRELESS_REG_H */

net/wireless/sme.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,44 @@ struct cfg80211_conn {
3434
bool auto_auth, prev_bssid_valid;
3535
};
3636

37+
bool 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+
66+
static void disconnect_work(struct work_struct *work)
67+
{
68+
if (!cfg80211_is_all_idle())
69+
return;
70+
71+
regulatory_hint_disconnect();
72+
}
73+
74+
static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
3775

3876
static int cfg80211_conn_scan(struct wireless_dev *wdev)
3977
{
@@ -658,6 +696,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
658696
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
659697
wdev->wext.connect.ssid_len = 0;
660698
#endif
699+
700+
schedule_work(&cfg80211_disconnect_work);
661701
}
662702

663703
void cfg80211_disconnected(struct net_device *dev, u16 reason,

0 commit comments

Comments
 (0)