@@ -134,6 +134,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
134134 & world_regdom ;
135135
136136static char * ieee80211_regdom = "00" ;
137+ static char user_alpha2 [2 ];
137138
138139module_param (ieee80211_regdom , charp , 0444 );
139140MODULE_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+ */
19081938static 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+
21102258static 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 ) {
0 commit comments