diff options
-rw-r--r-- | net/wireless/reg.c | 133 |
1 files changed, 69 insertions, 64 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cf4386fade8a..129586994662 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -65,6 +65,13 @@ | |||
65 | #define REG_DBG_PRINT(args...) | 65 | #define REG_DBG_PRINT(args...) |
66 | #endif | 66 | #endif |
67 | 67 | ||
68 | enum reg_request_treatment { | ||
69 | REG_REQ_OK, | ||
70 | REG_REQ_IGNORE, | ||
71 | REG_REQ_INTERSECT, | ||
72 | REG_REQ_ALREADY_SET, | ||
73 | }; | ||
74 | |||
68 | static struct regulatory_request core_request_world = { | 75 | static struct regulatory_request core_request_world = { |
69 | .initiator = NL80211_REGDOM_SET_BY_CORE, | 76 | .initiator = NL80211_REGDOM_SET_BY_CORE, |
70 | .alpha2[0] = '0', | 77 | .alpha2[0] = '0', |
@@ -925,16 +932,17 @@ bool reg_last_request_cell_base(void) | |||
925 | 932 | ||
926 | #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS | 933 | #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS |
927 | /* Core specific check */ | 934 | /* Core specific check */ |
928 | static int reg_ignore_cell_hint(struct regulatory_request *pending_request) | 935 | static enum reg_request_treatment |
936 | reg_ignore_cell_hint(struct regulatory_request *pending_request) | ||
929 | { | 937 | { |
930 | if (!reg_num_devs_support_basehint) | 938 | if (!reg_num_devs_support_basehint) |
931 | return -EOPNOTSUPP; | 939 | return REG_REQ_IGNORE; |
932 | 940 | ||
933 | if (reg_request_cell_base(last_request) && | 941 | if (reg_request_cell_base(last_request) && |
934 | !regdom_changes(pending_request->alpha2)) | 942 | !regdom_changes(pending_request->alpha2)) |
935 | return -EALREADY; | 943 | return REG_REQ_ALREADY_SET; |
936 | 944 | ||
937 | return 0; | 945 | return REG_REQ_OK; |
938 | } | 946 | } |
939 | 947 | ||
940 | /* Device specific check */ | 948 | /* Device specific check */ |
@@ -945,7 +953,7 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) | |||
945 | #else | 953 | #else |
946 | static int reg_ignore_cell_hint(struct regulatory_request *pending_request) | 954 | static int reg_ignore_cell_hint(struct regulatory_request *pending_request) |
947 | { | 955 | { |
948 | return -EOPNOTSUPP; | 956 | return REG_REQ_IGNORE; |
949 | } | 957 | } |
950 | 958 | ||
951 | static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) | 959 | static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) |
@@ -1308,15 +1316,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
1308 | } | 1316 | } |
1309 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1317 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
1310 | 1318 | ||
1311 | /* | ||
1312 | * Return value which can be used by ignore_request() to indicate | ||
1313 | * it has been determined we should intersect two regulatory domains | ||
1314 | */ | ||
1315 | #define REG_INTERSECT 1 | ||
1316 | |||
1317 | /* This has the logic which determines when a new request | 1319 | /* This has the logic which determines when a new request |
1318 | * should be ignored. */ | 1320 | * should be ignored. */ |
1319 | static int ignore_request(struct wiphy *wiphy, | 1321 | static enum reg_request_treatment |
1322 | get_reg_request_treatment(struct wiphy *wiphy, | ||
1320 | struct regulatory_request *pending_request) | 1323 | struct regulatory_request *pending_request) |
1321 | { | 1324 | { |
1322 | struct wiphy *last_wiphy = NULL; | 1325 | struct wiphy *last_wiphy = NULL; |
@@ -1325,17 +1328,17 @@ static int ignore_request(struct wiphy *wiphy, | |||
1325 | 1328 | ||
1326 | /* All initial requests are respected */ | 1329 | /* All initial requests are respected */ |
1327 | if (!last_request) | 1330 | if (!last_request) |
1328 | return 0; | 1331 | return REG_REQ_OK; |
1329 | 1332 | ||
1330 | switch (pending_request->initiator) { | 1333 | switch (pending_request->initiator) { |
1331 | case NL80211_REGDOM_SET_BY_CORE: | 1334 | case NL80211_REGDOM_SET_BY_CORE: |
1332 | return 0; | 1335 | return REG_REQ_OK; |
1333 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | 1336 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
1334 | if (reg_request_cell_base(last_request)) { | 1337 | if (reg_request_cell_base(last_request)) { |
1335 | /* Trust a Cell base station over the AP's country IE */ | 1338 | /* Trust a Cell base station over the AP's country IE */ |
1336 | if (regdom_changes(pending_request->alpha2)) | 1339 | if (regdom_changes(pending_request->alpha2)) |
1337 | return -EOPNOTSUPP; | 1340 | return REG_REQ_IGNORE; |
1338 | return -EALREADY; | 1341 | return REG_REQ_ALREADY_SET; |
1339 | } | 1342 | } |
1340 | 1343 | ||
1341 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 1344 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
@@ -1352,23 +1355,23 @@ static int ignore_request(struct wiphy *wiphy, | |||
1352 | * to be correct. Reject second one for now. | 1355 | * to be correct. Reject second one for now. |
1353 | */ | 1356 | */ |
1354 | if (regdom_changes(pending_request->alpha2)) | 1357 | if (regdom_changes(pending_request->alpha2)) |
1355 | return -EOPNOTSUPP; | 1358 | return REG_REQ_IGNORE; |
1356 | return -EALREADY; | 1359 | return REG_REQ_ALREADY_SET; |
1357 | } | 1360 | } |
1358 | /* | 1361 | /* |
1359 | * Two consecutive Country IE hints on the same wiphy. | 1362 | * Two consecutive Country IE hints on the same wiphy. |
1360 | * This should be picked up early by the driver/stack | 1363 | * This should be picked up early by the driver/stack |
1361 | */ | 1364 | */ |
1362 | if (WARN_ON(regdom_changes(pending_request->alpha2))) | 1365 | if (WARN_ON(regdom_changes(pending_request->alpha2))) |
1363 | return 0; | 1366 | return REG_REQ_OK; |
1364 | return -EALREADY; | 1367 | return REG_REQ_ALREADY_SET; |
1365 | } | 1368 | } |
1366 | return 0; | 1369 | return 0; |
1367 | case NL80211_REGDOM_SET_BY_DRIVER: | 1370 | case NL80211_REGDOM_SET_BY_DRIVER: |
1368 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1371 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { |
1369 | if (regdom_changes(pending_request->alpha2)) | 1372 | if (regdom_changes(pending_request->alpha2)) |
1370 | return 0; | 1373 | return REG_REQ_OK; |
1371 | return -EALREADY; | 1374 | return REG_REQ_ALREADY_SET; |
1372 | } | 1375 | } |
1373 | 1376 | ||
1374 | /* | 1377 | /* |
@@ -1378,25 +1381,25 @@ static int ignore_request(struct wiphy *wiphy, | |||
1378 | */ | 1381 | */ |
1379 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1382 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
1380 | !regdom_changes(pending_request->alpha2)) | 1383 | !regdom_changes(pending_request->alpha2)) |
1381 | return -EALREADY; | 1384 | return REG_REQ_ALREADY_SET; |
1382 | 1385 | ||
1383 | return REG_INTERSECT; | 1386 | return REG_REQ_INTERSECT; |
1384 | case NL80211_REGDOM_SET_BY_USER: | 1387 | case NL80211_REGDOM_SET_BY_USER: |
1385 | if (reg_request_cell_base(pending_request)) | 1388 | if (reg_request_cell_base(pending_request)) |
1386 | return reg_ignore_cell_hint(pending_request); | 1389 | return reg_ignore_cell_hint(pending_request); |
1387 | 1390 | ||
1388 | if (reg_request_cell_base(last_request)) | 1391 | if (reg_request_cell_base(last_request)) |
1389 | return -EOPNOTSUPP; | 1392 | return REG_REQ_IGNORE; |
1390 | 1393 | ||
1391 | if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) | 1394 | if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) |
1392 | return REG_INTERSECT; | 1395 | return REG_REQ_INTERSECT; |
1393 | /* | 1396 | /* |
1394 | * If the user knows better the user should set the regdom | 1397 | * If the user knows better the user should set the regdom |
1395 | * to their country before the IE is picked up | 1398 | * to their country before the IE is picked up |
1396 | */ | 1399 | */ |
1397 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && | 1400 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && |
1398 | last_request->intersect) | 1401 | last_request->intersect) |
1399 | return -EOPNOTSUPP; | 1402 | return REG_REQ_IGNORE; |
1400 | /* | 1403 | /* |
1401 | * Process user requests only after previous user/driver/core | 1404 | * Process user requests only after previous user/driver/core |
1402 | * requests have been processed | 1405 | * requests have been processed |
@@ -1405,15 +1408,15 @@ static int ignore_request(struct wiphy *wiphy, | |||
1405 | last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || | 1408 | last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || |
1406 | last_request->initiator == NL80211_REGDOM_SET_BY_USER) && | 1409 | last_request->initiator == NL80211_REGDOM_SET_BY_USER) && |
1407 | regdom_changes(last_request->alpha2)) | 1410 | regdom_changes(last_request->alpha2)) |
1408 | return -EAGAIN; | 1411 | return REG_REQ_IGNORE; |
1409 | 1412 | ||
1410 | if (!regdom_changes(pending_request->alpha2)) | 1413 | if (!regdom_changes(pending_request->alpha2)) |
1411 | return -EALREADY; | 1414 | return REG_REQ_ALREADY_SET; |
1412 | 1415 | ||
1413 | return 0; | 1416 | return REG_REQ_OK; |
1414 | } | 1417 | } |
1415 | 1418 | ||
1416 | return -EINVAL; | 1419 | return REG_REQ_IGNORE; |
1417 | } | 1420 | } |
1418 | 1421 | ||
1419 | static void reg_set_request_processed(void) | 1422 | static void reg_set_request_processed(void) |
@@ -1443,23 +1446,24 @@ static void reg_set_request_processed(void) | |||
1443 | * The Wireless subsystem can use this function to hint to the wireless core | 1446 | * The Wireless subsystem can use this function to hint to the wireless core |
1444 | * what it believes should be the current regulatory domain. | 1447 | * what it believes should be the current regulatory domain. |
1445 | * | 1448 | * |
1446 | * Returns zero if all went fine, %-EALREADY if a regulatory domain had | 1449 | * Returns one of the different reg request treatment values. |
1447 | * already been set or other standard error codes. | ||
1448 | * | 1450 | * |
1449 | * Caller must hold &cfg80211_mutex and ®_mutex | 1451 | * Caller must hold &cfg80211_mutex and ®_mutex |
1450 | */ | 1452 | */ |
1451 | static int __regulatory_hint(struct wiphy *wiphy, | 1453 | static enum reg_request_treatment |
1452 | struct regulatory_request *pending_request) | 1454 | __regulatory_hint(struct wiphy *wiphy, |
1455 | struct regulatory_request *pending_request) | ||
1453 | { | 1456 | { |
1454 | const struct ieee80211_regdomain *regd; | 1457 | const struct ieee80211_regdomain *regd; |
1455 | bool intersect = false; | 1458 | bool intersect = false; |
1456 | int r = 0; | 1459 | enum reg_request_treatment treatment; |
1457 | 1460 | ||
1458 | assert_cfg80211_lock(); | 1461 | assert_cfg80211_lock(); |
1459 | 1462 | ||
1460 | r = ignore_request(wiphy, pending_request); | 1463 | treatment = get_reg_request_treatment(wiphy, pending_request); |
1461 | 1464 | ||
1462 | if (r == REG_INTERSECT) { | 1465 | switch (treatment) { |
1466 | case REG_REQ_INTERSECT: | ||
1463 | if (pending_request->initiator == | 1467 | if (pending_request->initiator == |
1464 | NL80211_REGDOM_SET_BY_DRIVER) { | 1468 | NL80211_REGDOM_SET_BY_DRIVER) { |
1465 | regd = reg_copy_regd(cfg80211_regdomain); | 1469 | regd = reg_copy_regd(cfg80211_regdomain); |
@@ -1470,26 +1474,28 @@ static int __regulatory_hint(struct wiphy *wiphy, | |||
1470 | wiphy->regd = regd; | 1474 | wiphy->regd = regd; |
1471 | } | 1475 | } |
1472 | intersect = true; | 1476 | intersect = true; |
1473 | } else if (r) { | 1477 | break; |
1478 | case REG_REQ_OK: | ||
1479 | break; | ||
1480 | default: | ||
1474 | /* | 1481 | /* |
1475 | * If the regulatory domain being requested by the | 1482 | * If the regulatory domain being requested by the |
1476 | * driver has already been set just copy it to the | 1483 | * driver has already been set just copy it to the |
1477 | * wiphy | 1484 | * wiphy |
1478 | */ | 1485 | */ |
1479 | if (r == -EALREADY && | 1486 | if (treatment == REG_REQ_ALREADY_SET && |
1480 | pending_request->initiator == | 1487 | pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { |
1481 | NL80211_REGDOM_SET_BY_DRIVER) { | ||
1482 | regd = reg_copy_regd(cfg80211_regdomain); | 1488 | regd = reg_copy_regd(cfg80211_regdomain); |
1483 | if (IS_ERR(regd)) { | 1489 | if (IS_ERR(regd)) { |
1484 | kfree(pending_request); | 1490 | kfree(pending_request); |
1485 | return PTR_ERR(regd); | 1491 | return REG_REQ_IGNORE; |
1486 | } | 1492 | } |
1487 | r = -EALREADY; | 1493 | treatment = REG_REQ_ALREADY_SET; |
1488 | wiphy->regd = regd; | 1494 | wiphy->regd = regd; |
1489 | goto new_request; | 1495 | goto new_request; |
1490 | } | 1496 | } |
1491 | kfree(pending_request); | 1497 | kfree(pending_request); |
1492 | return r; | 1498 | return treatment; |
1493 | } | 1499 | } |
1494 | 1500 | ||
1495 | new_request: | 1501 | new_request: |
@@ -1506,28 +1512,29 @@ new_request: | |||
1506 | user_alpha2[1] = last_request->alpha2[1]; | 1512 | user_alpha2[1] = last_request->alpha2[1]; |
1507 | } | 1513 | } |
1508 | 1514 | ||
1509 | /* When r == REG_INTERSECT we do need to call CRDA */ | 1515 | /* When r == REG_REQ_INTERSECT we do need to call CRDA */ |
1510 | if (r < 0) { | 1516 | if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) { |
1511 | /* | 1517 | /* |
1512 | * Since CRDA will not be called in this case as we already | 1518 | * Since CRDA will not be called in this case as we already |
1513 | * have applied the requested regulatory domain before we just | 1519 | * have applied the requested regulatory domain before we just |
1514 | * inform userspace we have processed the request | 1520 | * inform userspace we have processed the request |
1515 | */ | 1521 | */ |
1516 | if (r == -EALREADY) { | 1522 | if (treatment == REG_REQ_ALREADY_SET) { |
1517 | nl80211_send_reg_change_event(last_request); | 1523 | nl80211_send_reg_change_event(last_request); |
1518 | reg_set_request_processed(); | 1524 | reg_set_request_processed(); |
1519 | } | 1525 | } |
1520 | return r; | 1526 | return treatment; |
1521 | } | 1527 | } |
1522 | 1528 | ||
1523 | return call_crda(last_request->alpha2); | 1529 | if (call_crda(last_request->alpha2)) |
1530 | return REG_REQ_IGNORE; | ||
1531 | return REG_REQ_OK; | ||
1524 | } | 1532 | } |
1525 | 1533 | ||
1526 | /* This processes *all* regulatory hints */ | 1534 | /* This processes *all* regulatory hints */ |
1527 | static void reg_process_hint(struct regulatory_request *reg_request, | 1535 | static void reg_process_hint(struct regulatory_request *reg_request, |
1528 | enum nl80211_reg_initiator reg_initiator) | 1536 | enum nl80211_reg_initiator reg_initiator) |
1529 | { | 1537 | { |
1530 | int r = 0; | ||
1531 | struct wiphy *wiphy = NULL; | 1538 | struct wiphy *wiphy = NULL; |
1532 | 1539 | ||
1533 | BUG_ON(!reg_request->alpha2); | 1540 | BUG_ON(!reg_request->alpha2); |
@@ -1540,20 +1547,18 @@ static void reg_process_hint(struct regulatory_request *reg_request, | |||
1540 | return; | 1547 | return; |
1541 | } | 1548 | } |
1542 | 1549 | ||
1543 | r = __regulatory_hint(wiphy, reg_request); | 1550 | switch (__regulatory_hint(wiphy, reg_request)) { |
1544 | /* This is required so that the orig_* parameters are saved */ | 1551 | case REG_REQ_ALREADY_SET: |
1545 | if (r == -EALREADY && wiphy && | 1552 | /* This is required so that the orig_* parameters are saved */ |
1546 | wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { | 1553 | if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) |
1547 | wiphy_update_regulatory(wiphy, reg_initiator); | 1554 | wiphy_update_regulatory(wiphy, reg_initiator); |
1548 | return; | 1555 | break; |
1556 | default: | ||
1557 | if (reg_initiator == NL80211_REGDOM_SET_BY_USER) | ||
1558 | schedule_delayed_work(®_timeout, | ||
1559 | msecs_to_jiffies(3142)); | ||
1560 | break; | ||
1549 | } | 1561 | } |
1550 | |||
1551 | /* | ||
1552 | * We only time out user hints, given that they should be the only | ||
1553 | * source of bogus requests. | ||
1554 | */ | ||
1555 | if (r != -EALREADY && reg_initiator == NL80211_REGDOM_SET_BY_USER) | ||
1556 | schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); | ||
1557 | } | 1562 | } |
1558 | 1563 | ||
1559 | /* | 1564 | /* |