aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath9k/main.c8
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.c6
-rw-r--r--include/net/cfg80211.h2
-rw-r--r--include/net/wireless.h9
-rw-r--r--net/wireless/nl80211.c30
-rw-r--r--net/wireless/reg.c178
-rw-r--r--net/wireless/reg.h2
7 files changed, 194 insertions, 41 deletions
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c
index b47cbe9e7a5a..1e3824215ac9 100644
--- a/drivers/net/wireless/ath9k/main.c
+++ b/drivers/net/wireless/ath9k/main.c
@@ -1656,8 +1656,12 @@ int ath_attach(u16 devid, struct ath_softc *sc)
1656 1656
1657 error = ieee80211_register_hw(hw); 1657 error = ieee80211_register_hw(hw);
1658 1658
1659 if (!ath9k_is_world_regd(sc->sc_ah)) 1659 if (!ath9k_is_world_regd(sc->sc_ah)) {
1660 regulatory_hint(hw->wiphy, sc->sc_ah->regulatory.alpha2); 1660 error = regulatory_hint(hw->wiphy,
1661 sc->sc_ah->regulatory.alpha2);
1662 if (error)
1663 goto error_attach;
1664 }
1661 1665
1662 /* Initialize LED control */ 1666 /* Initialize LED control */
1663 ath_init_leds(sc); 1667 ath_init_leds(sc);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 7579af27edbd..da9214e33a5f 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -170,10 +170,10 @@ int zd_mac_init_hw(struct ieee80211_hw *hw)
170 goto disable_int; 170 goto disable_int;
171 171
172 r = zd_reg2alpha2(mac->regdomain, alpha2); 172 r = zd_reg2alpha2(mac->regdomain, alpha2);
173 if (!r) 173 if (r)
174 regulatory_hint(hw->wiphy, alpha2); 174 goto disable_int;
175 175
176 r = 0; 176 r = regulatory_hint(hw->wiphy, alpha2);
177disable_int: 177disable_int:
178 zd_chip_disable_int(chip); 178 zd_chip_disable_int(chip);
179out: 179out:
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index bb9129fc61ca..75fa556728ce 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -404,6 +404,7 @@ enum environment_cap {
404 * country IE 404 * country IE
405 * @country_ie_env: lets us know if the AP is telling us we are outdoor, 405 * @country_ie_env: lets us know if the AP is telling us we are outdoor,
406 * indoor, or if it doesn't matter 406 * indoor, or if it doesn't matter
407 * @list: used to insert into the reg_requests_list linked list
407 */ 408 */
408struct regulatory_request { 409struct regulatory_request {
409 int wiphy_idx; 410 int wiphy_idx;
@@ -412,6 +413,7 @@ struct regulatory_request {
412 bool intersect; 413 bool intersect;
413 u32 country_ie_checksum; 414 u32 country_ie_checksum;
414 enum environment_cap country_ie_env; 415 enum environment_cap country_ie_env;
416 struct list_head list;
415}; 417};
416 418
417struct ieee80211_freq_range { 419struct ieee80211_freq_range {
diff --git a/include/net/wireless.h b/include/net/wireless.h
index d815aa8b4534..1f4707d18adb 100644
--- a/include/net/wireless.h
+++ b/include/net/wireless.h
@@ -401,8 +401,15 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
401 * domain should be in or by providing a completely build regulatory domain. 401 * domain should be in or by providing a completely build regulatory domain.
402 * If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried 402 * If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried
403 * for a regulatory domain structure for the respective country. 403 * for a regulatory domain structure for the respective country.
404 *
405 * The wiphy must have been registered to cfg80211 prior to this call.
406 * For cfg80211 drivers this means you must first use wiphy_register(),
407 * for mac80211 drivers you must first use ieee80211_register_hw().
408 *
409 * Drivers should check the return value, its possible you can get
410 * an -ENOMEM.
404 */ 411 */
405extern void regulatory_hint(struct wiphy *wiphy, const char *alpha2); 412extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
406 413
407/** 414/**
408 * regulatory_hint_11d - hints a country IE as a regulatory domain 415 * regulatory_hint_11d - hints a country IE as a regulatory domain
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e0d3879b8852..97f69bed3fe2 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1915,34 +1915,24 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
1915 */ 1915 */
1916 mutex_lock(&cfg80211_mutex); 1916 mutex_lock(&cfg80211_mutex);
1917 if (unlikely(!cfg80211_regdomain)) { 1917 if (unlikely(!cfg80211_regdomain)) {
1918 r = -EINPROGRESS; 1918 mutex_unlock(&cfg80211_mutex);
1919 goto out; 1919 return -EINPROGRESS;
1920 } 1920 }
1921 mutex_unlock(&cfg80211_mutex);
1921 1922
1922 if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) { 1923 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
1923 r = -EINVAL; 1924 return -EINVAL;
1924 goto out;
1925 }
1926 1925
1927 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); 1926 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
1928 1927
1929#ifdef CONFIG_WIRELESS_OLD_REGULATORY 1928#ifdef CONFIG_WIRELESS_OLD_REGULATORY
1930 /* We ignore world regdom requests with the old regdom setup */ 1929 /* We ignore world regdom requests with the old regdom setup */
1931 if (is_world_regdom(data)) { 1930 if (is_world_regdom(data))
1932 r = -EINVAL; 1931 return -EINVAL;
1933 goto out;
1934 }
1935#endif 1932#endif
1936 r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY); 1933
1937 /* 1934 r = regulatory_hint_user(data);
1938 * This means the regulatory domain was already set, however 1935
1939 * we don't want to confuse userspace with a "successful error"
1940 * message so lets just treat it as a success
1941 */
1942 if (r == -EALREADY)
1943 r = 0;
1944out:
1945 mutex_unlock(&cfg80211_mutex);
1946 return r; 1936 return r;
1947} 1937}
1948 1938
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index af762be3f0a1..0b8c4b86789a 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -64,6 +64,9 @@ const struct ieee80211_regdomain *cfg80211_regdomain;
64 * what it thinks should apply for the same country */ 64 * what it thinks should apply for the same country */
65static const struct ieee80211_regdomain *country_ie_regdomain; 65static const struct ieee80211_regdomain *country_ie_regdomain;
66 66
67static LIST_HEAD(reg_requests_list);
68static spinlock_t reg_requests_lock;
69
67/* We keep a static world regulatory domain in case of the absence of CRDA */ 70/* We keep a static world regulatory domain in case of the absence of CRDA */
68static const struct ieee80211_regdomain world_regdom = { 71static const struct ieee80211_regdomain world_regdom = {
69 .n_reg_rules = 1, 72 .n_reg_rules = 1,
@@ -831,7 +834,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
831 const struct ieee80211_power_rule *power_rule = NULL; 834 const struct ieee80211_power_rule *power_rule = NULL;
832 struct ieee80211_supported_band *sband; 835 struct ieee80211_supported_band *sband;
833 struct ieee80211_channel *chan; 836 struct ieee80211_channel *chan;
834 struct wiphy *request_wiphy; 837 struct wiphy *request_wiphy = NULL;
835 838
836 assert_cfg80211_lock(); 839 assert_cfg80211_lock();
837 840
@@ -1195,6 +1198,89 @@ new_request:
1195 return call_crda(alpha2); 1198 return call_crda(alpha2);
1196} 1199}
1197 1200
1201/* This currently only processes user and driver regulatory hints */
1202static int reg_process_hint(struct regulatory_request *reg_request)
1203{
1204 int r = 0;
1205 struct wiphy *wiphy = NULL;
1206
1207 BUG_ON(!reg_request->alpha2);
1208
1209 mutex_lock(&cfg80211_mutex);
1210
1211 if (wiphy_idx_valid(reg_request->wiphy_idx))
1212 wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
1213
1214 if (reg_request->initiator == REGDOM_SET_BY_DRIVER &&
1215 !wiphy) {
1216 r = -ENODEV;
1217 goto out;
1218 }
1219
1220 r = __regulatory_hint(wiphy,
1221 reg_request->initiator,
1222 reg_request->alpha2,
1223 reg_request->country_ie_checksum,
1224 reg_request->country_ie_env);
1225 /* This is required so that the orig_* parameters are saved */
1226 if (r == -EALREADY && wiphy && wiphy->strict_regulatory)
1227 wiphy_update_regulatory(wiphy, reg_request->initiator);
1228out:
1229 mutex_unlock(&cfg80211_mutex);
1230
1231 if (r == -EALREADY)
1232 r = 0;
1233
1234 return r;
1235}
1236
1237static void reg_process_pending_hints(void)
1238 {
1239 struct regulatory_request *reg_request;
1240 int r;
1241
1242 spin_lock(&reg_requests_lock);
1243 while (!list_empty(&reg_requests_list)) {
1244 reg_request = list_first_entry(&reg_requests_list,
1245 struct regulatory_request,
1246 list);
1247 list_del_init(&reg_request->list);
1248 spin_unlock(&reg_requests_lock);
1249
1250 r = reg_process_hint(reg_request);
1251#ifdef CONFIG_CFG80211_REG_DEBUG
1252 if (r && (reg_request->initiator == REGDOM_SET_BY_DRIVER ||
1253 reg_request->initiator == REGDOM_SET_BY_COUNTRY_IE))
1254 printk(KERN_ERR "cfg80211: wiphy_idx %d sent a "
1255 "regulatory hint for %c%c but now has "
1256 "gone fishing, ignoring request\n",
1257 reg_request->wiphy_idx,
1258 reg_request->alpha2[0],
1259 reg_request->alpha2[1]);
1260#endif
1261 kfree(reg_request);
1262 spin_lock(&reg_requests_lock);
1263 }
1264 spin_unlock(&reg_requests_lock);
1265}
1266
1267static void reg_todo(struct work_struct *work)
1268{
1269 reg_process_pending_hints();
1270}
1271
1272static DECLARE_WORK(reg_work, reg_todo);
1273
1274static void queue_regulatory_request(struct regulatory_request *request)
1275{
1276 spin_lock(&reg_requests_lock);
1277 list_add_tail(&request->list, &reg_requests_list);
1278 spin_unlock(&reg_requests_lock);
1279
1280 schedule_work(&reg_work);
1281}
1282
1283/* Core regulatory hint -- happens once during cfg80211_init() */
1198static int regulatory_hint_core(const char *alpha2) 1284static int regulatory_hint_core(const char *alpha2)
1199{ 1285{
1200 struct regulatory_request *request; 1286 struct regulatory_request *request;
@@ -1210,23 +1296,56 @@ static int regulatory_hint_core(const char *alpha2)
1210 request->alpha2[1] = alpha2[1]; 1296 request->alpha2[1] = alpha2[1];
1211 request->initiator = REGDOM_SET_BY_CORE; 1297 request->initiator = REGDOM_SET_BY_CORE;
1212 1298
1213 last_request = request; 1299 queue_regulatory_request(request);
1214 1300
1215 return call_crda(alpha2); 1301 return 0;
1216} 1302}
1217 1303
1218void regulatory_hint(struct wiphy *wiphy, const char *alpha2) 1304/* User hints */
1305int regulatory_hint_user(const char *alpha2)
1219{ 1306{
1220 int r; 1307 struct regulatory_request *request;
1308
1221 BUG_ON(!alpha2); 1309 BUG_ON(!alpha2);
1222 1310
1223 mutex_lock(&cfg80211_mutex); 1311 request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
1224 r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, 1312 if (!request)
1225 alpha2, 0, ENVIRON_ANY); 1313 return -ENOMEM;
1226 /* This is required so that the orig_* parameters are saved */ 1314
1227 if (r == -EALREADY && wiphy->strict_regulatory) 1315 request->wiphy_idx = WIPHY_IDX_STALE;
1228 wiphy_update_regulatory(wiphy, REGDOM_SET_BY_DRIVER); 1316 request->alpha2[0] = alpha2[0];
1229 mutex_unlock(&cfg80211_mutex); 1317 request->alpha2[1] = alpha2[1];
1318 request->initiator = REGDOM_SET_BY_USER,
1319
1320 queue_regulatory_request(request);
1321
1322 return 0;
1323}
1324
1325/* Driver hints */
1326int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
1327{
1328 struct regulatory_request *request;
1329
1330 BUG_ON(!alpha2);
1331 BUG_ON(!wiphy);
1332
1333 request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
1334 if (!request)
1335 return -ENOMEM;
1336
1337 request->wiphy_idx = get_wiphy_idx(wiphy);
1338
1339 /* Must have registered wiphy first */
1340 BUG_ON(!wiphy_idx_valid(request->wiphy_idx));
1341
1342 request->alpha2[0] = alpha2[0];
1343 request->alpha2[1] = alpha2[1];
1344 request->initiator = REGDOM_SET_BY_DRIVER;
1345
1346 queue_regulatory_request(request);
1347
1348 return 0;
1230} 1349}
1231EXPORT_SYMBOL(regulatory_hint); 1350EXPORT_SYMBOL(regulatory_hint);
1232 1351
@@ -1260,6 +1379,7 @@ void regulatory_hint_11d(struct wiphy *wiphy,
1260 char alpha2[2]; 1379 char alpha2[2];
1261 u32 checksum = 0; 1380 u32 checksum = 0;
1262 enum environment_cap env = ENVIRON_ANY; 1381 enum environment_cap env = ENVIRON_ANY;
1382 struct regulatory_request *request;
1263 1383
1264 mutex_lock(&cfg80211_mutex); 1384 mutex_lock(&cfg80211_mutex);
1265 1385
@@ -1343,14 +1463,26 @@ void regulatory_hint_11d(struct wiphy *wiphy,
1343 if (WARN_ON(reg_same_country_ie_hint(wiphy, checksum))) 1463 if (WARN_ON(reg_same_country_ie_hint(wiphy, checksum)))
1344 goto free_rd_out; 1464 goto free_rd_out;
1345 1465
1466 request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
1467 if (!request)
1468 goto free_rd_out;
1469
1346 /* We keep this around for when CRDA comes back with a response so 1470 /* We keep this around for when CRDA comes back with a response so
1347 * we can intersect with that */ 1471 * we can intersect with that */
1348 country_ie_regdomain = rd; 1472 country_ie_regdomain = rd;
1349 1473
1350 __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE, 1474 request->wiphy_idx = get_wiphy_idx(wiphy);
1351 country_ie_regdomain->alpha2, checksum, env); 1475 request->alpha2[0] = rd->alpha2[0];
1476 request->alpha2[1] = rd->alpha2[1];
1477 request->initiator = REGDOM_SET_BY_COUNTRY_IE;
1478 request->country_ie_checksum = checksum;
1479 request->country_ie_env = env;
1480
1481 mutex_unlock(&cfg80211_mutex);
1352 1482
1353 goto out; 1483 queue_regulatory_request(request);
1484
1485 return;
1354 1486
1355free_rd_out: 1487free_rd_out:
1356 kfree(rd); 1488 kfree(rd);
@@ -1661,6 +1793,8 @@ int regulatory_init(void)
1661 if (IS_ERR(reg_pdev)) 1793 if (IS_ERR(reg_pdev))
1662 return PTR_ERR(reg_pdev); 1794 return PTR_ERR(reg_pdev);
1663 1795
1796 spin_lock_init(&reg_requests_lock);
1797
1664#ifdef CONFIG_WIRELESS_OLD_REGULATORY 1798#ifdef CONFIG_WIRELESS_OLD_REGULATORY
1665 cfg80211_regdomain = static_regdom(ieee80211_regdom); 1799 cfg80211_regdomain = static_regdom(ieee80211_regdom);
1666 1800
@@ -1700,6 +1834,10 @@ int regulatory_init(void)
1700 1834
1701void regulatory_exit(void) 1835void regulatory_exit(void)
1702{ 1836{
1837 struct regulatory_request *reg_request, *tmp;
1838
1839 cancel_work_sync(&reg_work);
1840
1703 mutex_lock(&cfg80211_mutex); 1841 mutex_lock(&cfg80211_mutex);
1704 1842
1705 reset_regdomains(); 1843 reset_regdomains();
@@ -1711,5 +1849,15 @@ void regulatory_exit(void)
1711 1849
1712 platform_device_unregister(reg_pdev); 1850 platform_device_unregister(reg_pdev);
1713 1851
1852 spin_lock(&reg_requests_lock);
1853 if (!list_empty(&reg_requests_list)) {
1854 list_for_each_entry_safe(reg_request, tmp,
1855 &reg_requests_list, list) {
1856 list_del(&reg_request->list);
1857 kfree(reg_request);
1858 }
1859 }
1860 spin_unlock(&reg_requests_lock);
1861
1714 mutex_unlock(&cfg80211_mutex); 1862 mutex_unlock(&cfg80211_mutex);
1715} 1863}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index fe8c83f34fb7..4730def5a69d 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -6,6 +6,8 @@ extern const struct ieee80211_regdomain *cfg80211_regdomain;
6bool is_world_regdom(const char *alpha2); 6bool is_world_regdom(const char *alpha2);
7bool reg_is_valid_request(const char *alpha2); 7bool reg_is_valid_request(const char *alpha2);
8 8
9int regulatory_hint_user(const char *alpha2);
10
9void reg_device_remove(struct wiphy *wiphy); 11void reg_device_remove(struct wiphy *wiphy);
10 12
11int regulatory_init(void); 13int regulatory_init(void);