diff options
-rw-r--r-- | drivers/net/wireless/ath9k/main.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_mac.c | 6 | ||||
-rw-r--r-- | include/net/cfg80211.h | 2 | ||||
-rw-r--r-- | include/net/wireless.h | 9 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 30 | ||||
-rw-r--r-- | net/wireless/reg.c | 178 | ||||
-rw-r--r-- | net/wireless/reg.h | 2 |
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); |
177 | disable_int: | 177 | disable_int: |
178 | zd_chip_disable_int(chip); | 178 | zd_chip_disable_int(chip); |
179 | out: | 179 | out: |
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 | */ |
408 | struct regulatory_request { | 409 | struct 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 | ||
417 | struct ieee80211_freq_range { | 419 | struct 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 | */ |
405 | extern void regulatory_hint(struct wiphy *wiphy, const char *alpha2); | 412 | extern 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; | ||
1944 | out: | ||
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 */ |
65 | static const struct ieee80211_regdomain *country_ie_regdomain; | 65 | static const struct ieee80211_regdomain *country_ie_regdomain; |
66 | 66 | ||
67 | static LIST_HEAD(reg_requests_list); | ||
68 | static 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 */ |
68 | static const struct ieee80211_regdomain world_regdom = { | 71 | static 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 */ | ||
1202 | static 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); | ||
1228 | out: | ||
1229 | mutex_unlock(&cfg80211_mutex); | ||
1230 | |||
1231 | if (r == -EALREADY) | ||
1232 | r = 0; | ||
1233 | |||
1234 | return r; | ||
1235 | } | ||
1236 | |||
1237 | static void reg_process_pending_hints(void) | ||
1238 | { | ||
1239 | struct regulatory_request *reg_request; | ||
1240 | int r; | ||
1241 | |||
1242 | spin_lock(®_requests_lock); | ||
1243 | while (!list_empty(®_requests_list)) { | ||
1244 | reg_request = list_first_entry(®_requests_list, | ||
1245 | struct regulatory_request, | ||
1246 | list); | ||
1247 | list_del_init(®_request->list); | ||
1248 | spin_unlock(®_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(®_requests_lock); | ||
1263 | } | ||
1264 | spin_unlock(®_requests_lock); | ||
1265 | } | ||
1266 | |||
1267 | static void reg_todo(struct work_struct *work) | ||
1268 | { | ||
1269 | reg_process_pending_hints(); | ||
1270 | } | ||
1271 | |||
1272 | static DECLARE_WORK(reg_work, reg_todo); | ||
1273 | |||
1274 | static void queue_regulatory_request(struct regulatory_request *request) | ||
1275 | { | ||
1276 | spin_lock(®_requests_lock); | ||
1277 | list_add_tail(&request->list, ®_requests_list); | ||
1278 | spin_unlock(®_requests_lock); | ||
1279 | |||
1280 | schedule_work(®_work); | ||
1281 | } | ||
1282 | |||
1283 | /* Core regulatory hint -- happens once during cfg80211_init() */ | ||
1198 | static int regulatory_hint_core(const char *alpha2) | 1284 | static 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 | ||
1218 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) | 1304 | /* User hints */ |
1305 | int 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 */ | ||
1326 | int 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 | } |
1231 | EXPORT_SYMBOL(regulatory_hint); | 1350 | EXPORT_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 | ||
1355 | free_rd_out: | 1487 | free_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(®_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 | ||
1701 | void regulatory_exit(void) | 1835 | void regulatory_exit(void) |
1702 | { | 1836 | { |
1837 | struct regulatory_request *reg_request, *tmp; | ||
1838 | |||
1839 | cancel_work_sync(®_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(®_requests_lock); | ||
1853 | if (!list_empty(®_requests_list)) { | ||
1854 | list_for_each_entry_safe(reg_request, tmp, | ||
1855 | ®_requests_list, list) { | ||
1856 | list_del(®_request->list); | ||
1857 | kfree(reg_request); | ||
1858 | } | ||
1859 | } | ||
1860 | spin_unlock(®_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; | |||
6 | bool is_world_regdom(const char *alpha2); | 6 | bool is_world_regdom(const char *alpha2); |
7 | bool reg_is_valid_request(const char *alpha2); | 7 | bool reg_is_valid_request(const char *alpha2); |
8 | 8 | ||
9 | int regulatory_hint_user(const char *alpha2); | ||
10 | |||
9 | void reg_device_remove(struct wiphy *wiphy); | 11 | void reg_device_remove(struct wiphy *wiphy); |
10 | 12 | ||
11 | int regulatory_init(void); | 13 | int regulatory_init(void); |