diff options
Diffstat (limited to 'net/wireless/reg.c')
| -rw-r--r-- | net/wireless/reg.c | 145 |
1 files changed, 96 insertions, 49 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 48dfc7b4e981..0e347f888fe9 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
| @@ -82,17 +82,12 @@ | |||
| 82 | * be intersected with the current one. | 82 | * be intersected with the current one. |
| 83 | * @REG_REQ_ALREADY_SET: the regulatory request will not change the current | 83 | * @REG_REQ_ALREADY_SET: the regulatory request will not change the current |
| 84 | * regulatory settings, and no further processing is required. | 84 | * regulatory settings, and no further processing is required. |
| 85 | * @REG_REQ_USER_HINT_HANDLED: a non alpha2 user hint was handled and no | ||
| 86 | * further processing is required, i.e., not need to update last_request | ||
| 87 | * etc. This should be used for user hints that do not provide an alpha2 | ||
| 88 | * but some other type of regulatory hint, i.e., indoor operation. | ||
| 89 | */ | 85 | */ |
| 90 | enum reg_request_treatment { | 86 | enum reg_request_treatment { |
| 91 | REG_REQ_OK, | 87 | REG_REQ_OK, |
| 92 | REG_REQ_IGNORE, | 88 | REG_REQ_IGNORE, |
| 93 | REG_REQ_INTERSECT, | 89 | REG_REQ_INTERSECT, |
| 94 | REG_REQ_ALREADY_SET, | 90 | REG_REQ_ALREADY_SET, |
| 95 | REG_REQ_USER_HINT_HANDLED, | ||
| 96 | }; | 91 | }; |
| 97 | 92 | ||
| 98 | static struct regulatory_request core_request_world = { | 93 | static struct regulatory_request core_request_world = { |
| @@ -133,9 +128,17 @@ static int reg_num_devs_support_basehint; | |||
| 133 | * State variable indicating if the platform on which the devices | 128 | * State variable indicating if the platform on which the devices |
| 134 | * are attached is operating in an indoor environment. The state variable | 129 | * are attached is operating in an indoor environment. The state variable |
| 135 | * is relevant for all registered devices. | 130 | * is relevant for all registered devices. |
| 136 | * (protected by RTNL) | ||
| 137 | */ | 131 | */ |
| 138 | static bool reg_is_indoor; | 132 | static bool reg_is_indoor; |
| 133 | static spinlock_t reg_indoor_lock; | ||
| 134 | |||
| 135 | /* Used to track the userspace process controlling the indoor setting */ | ||
| 136 | static u32 reg_is_indoor_portid; | ||
| 137 | |||
| 138 | /* Max number of consecutive attempts to communicate with CRDA */ | ||
| 139 | #define REG_MAX_CRDA_TIMEOUTS 10 | ||
| 140 | |||
| 141 | static u32 reg_crda_timeouts; | ||
| 139 | 142 | ||
| 140 | static const struct ieee80211_regdomain *get_cfg80211_regdom(void) | 143 | static const struct ieee80211_regdomain *get_cfg80211_regdom(void) |
| 141 | { | 144 | { |
| @@ -487,7 +490,7 @@ static void reg_regdb_search(struct work_struct *work) | |||
| 487 | mutex_unlock(®_regdb_search_mutex); | 490 | mutex_unlock(®_regdb_search_mutex); |
| 488 | 491 | ||
| 489 | if (!IS_ERR_OR_NULL(regdom)) | 492 | if (!IS_ERR_OR_NULL(regdom)) |
| 490 | set_regdom(regdom); | 493 | set_regdom(regdom, REGD_SOURCE_INTERNAL_DB); |
| 491 | 494 | ||
| 492 | rtnl_unlock(); | 495 | rtnl_unlock(); |
| 493 | } | 496 | } |
| @@ -537,15 +540,20 @@ static int call_crda(const char *alpha2) | |||
| 537 | snprintf(country, sizeof(country), "COUNTRY=%c%c", | 540 | snprintf(country, sizeof(country), "COUNTRY=%c%c", |
| 538 | alpha2[0], alpha2[1]); | 541 | alpha2[0], alpha2[1]); |
| 539 | 542 | ||
| 543 | /* query internal regulatory database (if it exists) */ | ||
| 544 | reg_regdb_query(alpha2); | ||
| 545 | |||
| 546 | if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) { | ||
| 547 | pr_info("Exceeded CRDA call max attempts. Not calling CRDA\n"); | ||
| 548 | return -EINVAL; | ||
| 549 | } | ||
| 550 | |||
| 540 | if (!is_world_regdom((char *) alpha2)) | 551 | if (!is_world_regdom((char *) alpha2)) |
| 541 | pr_info("Calling CRDA for country: %c%c\n", | 552 | pr_info("Calling CRDA for country: %c%c\n", |
| 542 | alpha2[0], alpha2[1]); | 553 | alpha2[0], alpha2[1]); |
| 543 | else | 554 | else |
| 544 | pr_info("Calling CRDA to update world regulatory domain\n"); | 555 | pr_info("Calling CRDA to update world regulatory domain\n"); |
| 545 | 556 | ||
| 546 | /* query internal regulatory database (if it exists) */ | ||
| 547 | reg_regdb_query(alpha2); | ||
| 548 | |||
| 549 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, env); | 557 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, env); |
| 550 | } | 558 | } |
| 551 | 559 | ||
| @@ -554,6 +562,9 @@ reg_call_crda(struct regulatory_request *request) | |||
| 554 | { | 562 | { |
| 555 | if (call_crda(request->alpha2)) | 563 | if (call_crda(request->alpha2)) |
| 556 | return REG_REQ_IGNORE; | 564 | return REG_REQ_IGNORE; |
| 565 | |||
| 566 | queue_delayed_work(system_power_efficient_wq, | ||
| 567 | ®_timeout, msecs_to_jiffies(3142)); | ||
| 557 | return REG_REQ_OK; | 568 | return REG_REQ_OK; |
| 558 | } | 569 | } |
| 559 | 570 | ||
| @@ -1248,13 +1259,6 @@ static bool reg_request_cell_base(struct regulatory_request *request) | |||
| 1248 | return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; | 1259 | return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; |
| 1249 | } | 1260 | } |
| 1250 | 1261 | ||
| 1251 | static bool reg_request_indoor(struct regulatory_request *request) | ||
| 1252 | { | ||
| 1253 | if (request->initiator != NL80211_REGDOM_SET_BY_USER) | ||
| 1254 | return false; | ||
| 1255 | return request->user_reg_hint_type == NL80211_USER_REG_HINT_INDOOR; | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | bool reg_last_request_cell_base(void) | 1262 | bool reg_last_request_cell_base(void) |
| 1259 | { | 1263 | { |
| 1260 | return reg_request_cell_base(get_last_request()); | 1264 | return reg_request_cell_base(get_last_request()); |
| @@ -1800,8 +1804,7 @@ static void reg_set_request_processed(void) | |||
| 1800 | need_more_processing = true; | 1804 | need_more_processing = true; |
| 1801 | spin_unlock(®_requests_lock); | 1805 | spin_unlock(®_requests_lock); |
| 1802 | 1806 | ||
| 1803 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) | 1807 | cancel_delayed_work(®_timeout); |
| 1804 | cancel_delayed_work(®_timeout); | ||
| 1805 | 1808 | ||
| 1806 | if (need_more_processing) | 1809 | if (need_more_processing) |
| 1807 | schedule_work(®_work); | 1810 | schedule_work(®_work); |
| @@ -1833,11 +1836,6 @@ __reg_process_hint_user(struct regulatory_request *user_request) | |||
| 1833 | { | 1836 | { |
| 1834 | struct regulatory_request *lr = get_last_request(); | 1837 | struct regulatory_request *lr = get_last_request(); |
| 1835 | 1838 | ||
| 1836 | if (reg_request_indoor(user_request)) { | ||
| 1837 | reg_is_indoor = true; | ||
| 1838 | return REG_REQ_USER_HINT_HANDLED; | ||
| 1839 | } | ||
| 1840 | |||
| 1841 | if (reg_request_cell_base(user_request)) | 1839 | if (reg_request_cell_base(user_request)) |
| 1842 | return reg_ignore_cell_hint(user_request); | 1840 | return reg_ignore_cell_hint(user_request); |
| 1843 | 1841 | ||
| @@ -1885,8 +1883,7 @@ reg_process_hint_user(struct regulatory_request *user_request) | |||
| 1885 | 1883 | ||
| 1886 | treatment = __reg_process_hint_user(user_request); | 1884 | treatment = __reg_process_hint_user(user_request); |
| 1887 | if (treatment == REG_REQ_IGNORE || | 1885 | if (treatment == REG_REQ_IGNORE || |
| 1888 | treatment == REG_REQ_ALREADY_SET || | 1886 | treatment == REG_REQ_ALREADY_SET) { |
| 1889 | treatment == REG_REQ_USER_HINT_HANDLED) { | ||
| 1890 | reg_free_request(user_request); | 1887 | reg_free_request(user_request); |
| 1891 | return treatment; | 1888 | return treatment; |
| 1892 | } | 1889 | } |
| @@ -1947,7 +1944,6 @@ reg_process_hint_driver(struct wiphy *wiphy, | |||
| 1947 | case REG_REQ_OK: | 1944 | case REG_REQ_OK: |
| 1948 | break; | 1945 | break; |
| 1949 | case REG_REQ_IGNORE: | 1946 | case REG_REQ_IGNORE: |
| 1950 | case REG_REQ_USER_HINT_HANDLED: | ||
| 1951 | reg_free_request(driver_request); | 1947 | reg_free_request(driver_request); |
| 1952 | return treatment; | 1948 | return treatment; |
| 1953 | case REG_REQ_INTERSECT: | 1949 | case REG_REQ_INTERSECT: |
| @@ -2047,7 +2043,6 @@ reg_process_hint_country_ie(struct wiphy *wiphy, | |||
| 2047 | case REG_REQ_OK: | 2043 | case REG_REQ_OK: |
| 2048 | break; | 2044 | break; |
| 2049 | case REG_REQ_IGNORE: | 2045 | case REG_REQ_IGNORE: |
| 2050 | case REG_REQ_USER_HINT_HANDLED: | ||
| 2051 | /* fall through */ | 2046 | /* fall through */ |
| 2052 | case REG_REQ_ALREADY_SET: | 2047 | case REG_REQ_ALREADY_SET: |
| 2053 | reg_free_request(country_ie_request); | 2048 | reg_free_request(country_ie_request); |
| @@ -2086,11 +2081,8 @@ static void reg_process_hint(struct regulatory_request *reg_request) | |||
| 2086 | case NL80211_REGDOM_SET_BY_USER: | 2081 | case NL80211_REGDOM_SET_BY_USER: |
| 2087 | treatment = reg_process_hint_user(reg_request); | 2082 | treatment = reg_process_hint_user(reg_request); |
| 2088 | if (treatment == REG_REQ_IGNORE || | 2083 | if (treatment == REG_REQ_IGNORE || |
| 2089 | treatment == REG_REQ_ALREADY_SET || | 2084 | treatment == REG_REQ_ALREADY_SET) |
| 2090 | treatment == REG_REQ_USER_HINT_HANDLED) | ||
| 2091 | return; | 2085 | return; |
| 2092 | queue_delayed_work(system_power_efficient_wq, | ||
| 2093 | ®_timeout, msecs_to_jiffies(3142)); | ||
| 2094 | return; | 2086 | return; |
| 2095 | case NL80211_REGDOM_SET_BY_DRIVER: | 2087 | case NL80211_REGDOM_SET_BY_DRIVER: |
| 2096 | if (!wiphy) | 2088 | if (!wiphy) |
| @@ -2177,6 +2169,13 @@ static void reg_process_pending_hints(void) | |||
| 2177 | } | 2169 | } |
| 2178 | 2170 | ||
| 2179 | reg_process_hint(reg_request); | 2171 | reg_process_hint(reg_request); |
| 2172 | |||
| 2173 | lr = get_last_request(); | ||
| 2174 | |||
| 2175 | spin_lock(®_requests_lock); | ||
| 2176 | if (!list_empty(®_requests_list) && lr && lr->processed) | ||
| 2177 | schedule_work(®_work); | ||
| 2178 | spin_unlock(®_requests_lock); | ||
| 2180 | } | 2179 | } |
| 2181 | 2180 | ||
| 2182 | /* Processes beacon hints -- this has nothing to do with country IEs */ | 2181 | /* Processes beacon hints -- this has nothing to do with country IEs */ |
| @@ -2304,27 +2303,58 @@ int regulatory_hint_user(const char *alpha2, | |||
| 2304 | request->initiator = NL80211_REGDOM_SET_BY_USER; | 2303 | request->initiator = NL80211_REGDOM_SET_BY_USER; |
| 2305 | request->user_reg_hint_type = user_reg_hint_type; | 2304 | request->user_reg_hint_type = user_reg_hint_type; |
| 2306 | 2305 | ||
| 2306 | /* Allow calling CRDA again */ | ||
| 2307 | reg_crda_timeouts = 0; | ||
| 2308 | |||
| 2307 | queue_regulatory_request(request); | 2309 | queue_regulatory_request(request); |
| 2308 | 2310 | ||
| 2309 | return 0; | 2311 | return 0; |
| 2310 | } | 2312 | } |
| 2311 | 2313 | ||
| 2312 | int regulatory_hint_indoor_user(void) | 2314 | int regulatory_hint_indoor(bool is_indoor, u32 portid) |
| 2313 | { | 2315 | { |
| 2314 | struct regulatory_request *request; | 2316 | spin_lock(®_indoor_lock); |
| 2315 | 2317 | ||
| 2316 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | 2318 | /* It is possible that more than one user space process is trying to |
| 2317 | if (!request) | 2319 | * configure the indoor setting. To handle such cases, clear the indoor |
| 2318 | return -ENOMEM; | 2320 | * setting in case that some process does not think that the device |
| 2321 | * is operating in an indoor environment. In addition, if a user space | ||
| 2322 | * process indicates that it is controlling the indoor setting, save its | ||
| 2323 | * portid, i.e., make it the owner. | ||
| 2324 | */ | ||
| 2325 | reg_is_indoor = is_indoor; | ||
| 2326 | if (reg_is_indoor) { | ||
| 2327 | if (!reg_is_indoor_portid) | ||
| 2328 | reg_is_indoor_portid = portid; | ||
| 2329 | } else { | ||
| 2330 | reg_is_indoor_portid = 0; | ||
| 2331 | } | ||
| 2319 | 2332 | ||
| 2320 | request->wiphy_idx = WIPHY_IDX_INVALID; | 2333 | spin_unlock(®_indoor_lock); |
| 2321 | request->initiator = NL80211_REGDOM_SET_BY_USER; | 2334 | |
| 2322 | request->user_reg_hint_type = NL80211_USER_REG_HINT_INDOOR; | 2335 | if (!is_indoor) |
| 2323 | queue_regulatory_request(request); | 2336 | reg_check_channels(); |
| 2324 | 2337 | ||
| 2325 | return 0; | 2338 | return 0; |
| 2326 | } | 2339 | } |
| 2327 | 2340 | ||
| 2341 | void regulatory_netlink_notify(u32 portid) | ||
| 2342 | { | ||
| 2343 | spin_lock(®_indoor_lock); | ||
| 2344 | |||
| 2345 | if (reg_is_indoor_portid != portid) { | ||
| 2346 | spin_unlock(®_indoor_lock); | ||
| 2347 | return; | ||
| 2348 | } | ||
| 2349 | |||
| 2350 | reg_is_indoor = false; | ||
| 2351 | reg_is_indoor_portid = 0; | ||
| 2352 | |||
| 2353 | spin_unlock(®_indoor_lock); | ||
| 2354 | |||
| 2355 | reg_check_channels(); | ||
| 2356 | } | ||
| 2357 | |||
| 2328 | /* Driver hints */ | 2358 | /* Driver hints */ |
| 2329 | int regulatory_hint(struct wiphy *wiphy, const char *alpha2) | 2359 | int regulatory_hint(struct wiphy *wiphy, const char *alpha2) |
| 2330 | { | 2360 | { |
| @@ -2345,6 +2375,9 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) | |||
| 2345 | request->alpha2[1] = alpha2[1]; | 2375 | request->alpha2[1] = alpha2[1]; |
| 2346 | request->initiator = NL80211_REGDOM_SET_BY_DRIVER; | 2376 | request->initiator = NL80211_REGDOM_SET_BY_DRIVER; |
| 2347 | 2377 | ||
| 2378 | /* Allow calling CRDA again */ | ||
| 2379 | reg_crda_timeouts = 0; | ||
| 2380 | |||
| 2348 | queue_regulatory_request(request); | 2381 | queue_regulatory_request(request); |
| 2349 | 2382 | ||
| 2350 | return 0; | 2383 | return 0; |
| @@ -2398,6 +2431,9 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band, | |||
| 2398 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; | 2431 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; |
| 2399 | request->country_ie_env = env; | 2432 | request->country_ie_env = env; |
| 2400 | 2433 | ||
| 2434 | /* Allow calling CRDA again */ | ||
| 2435 | reg_crda_timeouts = 0; | ||
| 2436 | |||
| 2401 | queue_regulatory_request(request); | 2437 | queue_regulatory_request(request); |
| 2402 | request = NULL; | 2438 | request = NULL; |
| 2403 | out: | 2439 | out: |
| @@ -2486,13 +2522,22 @@ static void restore_regulatory_settings(bool reset_user) | |||
| 2486 | char alpha2[2]; | 2522 | char alpha2[2]; |
| 2487 | char world_alpha2[2]; | 2523 | char world_alpha2[2]; |
| 2488 | struct reg_beacon *reg_beacon, *btmp; | 2524 | struct reg_beacon *reg_beacon, *btmp; |
| 2489 | struct regulatory_request *reg_request, *tmp; | ||
| 2490 | LIST_HEAD(tmp_reg_req_list); | 2525 | LIST_HEAD(tmp_reg_req_list); |
| 2491 | struct cfg80211_registered_device *rdev; | 2526 | struct cfg80211_registered_device *rdev; |
| 2492 | 2527 | ||
| 2493 | ASSERT_RTNL(); | 2528 | ASSERT_RTNL(); |
| 2494 | 2529 | ||
| 2495 | reg_is_indoor = false; | 2530 | /* |
| 2531 | * Clear the indoor setting in case that it is not controlled by user | ||
| 2532 | * space, as otherwise there is no guarantee that the device is still | ||
| 2533 | * operating in an indoor environment. | ||
| 2534 | */ | ||
| 2535 | spin_lock(®_indoor_lock); | ||
| 2536 | if (reg_is_indoor && !reg_is_indoor_portid) { | ||
| 2537 | reg_is_indoor = false; | ||
| 2538 | reg_check_channels(); | ||
| 2539 | } | ||
| 2540 | spin_unlock(®_indoor_lock); | ||
| 2496 | 2541 | ||
| 2497 | reset_regdomains(true, &world_regdom); | 2542 | reset_regdomains(true, &world_regdom); |
| 2498 | restore_alpha2(alpha2, reset_user); | 2543 | restore_alpha2(alpha2, reset_user); |
| @@ -2504,11 +2549,7 @@ static void restore_regulatory_settings(bool reset_user) | |||
| 2504 | * settings. | 2549 | * settings. |
| 2505 | */ | 2550 | */ |
| 2506 | spin_lock(®_requests_lock); | 2551 | spin_lock(®_requests_lock); |
| 2507 | list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { | 2552 | list_splice_tail_init(®_requests_list, &tmp_reg_req_list); |
| 2508 | if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER) | ||
| 2509 | continue; | ||
| 2510 | list_move_tail(®_request->list, &tmp_reg_req_list); | ||
| 2511 | } | ||
| 2512 | spin_unlock(®_requests_lock); | 2553 | spin_unlock(®_requests_lock); |
| 2513 | 2554 | ||
| 2514 | /* Clear beacon hints */ | 2555 | /* Clear beacon hints */ |
| @@ -2871,7 +2912,8 @@ static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd, | |||
| 2871 | * multiple drivers can be ironed out later. Caller must've already | 2912 | * multiple drivers can be ironed out later. Caller must've already |
| 2872 | * kmalloc'd the rd structure. | 2913 | * kmalloc'd the rd structure. |
| 2873 | */ | 2914 | */ |
| 2874 | int set_regdom(const struct ieee80211_regdomain *rd) | 2915 | int set_regdom(const struct ieee80211_regdomain *rd, |
| 2916 | enum ieee80211_regd_source regd_src) | ||
| 2875 | { | 2917 | { |
| 2876 | struct regulatory_request *lr; | 2918 | struct regulatory_request *lr; |
| 2877 | bool user_reset = false; | 2919 | bool user_reset = false; |
| @@ -2882,6 +2924,9 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
| 2882 | return -EINVAL; | 2924 | return -EINVAL; |
| 2883 | } | 2925 | } |
| 2884 | 2926 | ||
| 2927 | if (regd_src == REGD_SOURCE_CRDA) | ||
| 2928 | reg_crda_timeouts = 0; | ||
| 2929 | |||
| 2885 | lr = get_last_request(); | 2930 | lr = get_last_request(); |
| 2886 | 2931 | ||
| 2887 | /* Note that this doesn't update the wiphys, this is done below */ | 2932 | /* Note that this doesn't update the wiphys, this is done below */ |
| @@ -3041,6 +3086,7 @@ static void reg_timeout_work(struct work_struct *work) | |||
| 3041 | { | 3086 | { |
| 3042 | REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); | 3087 | REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); |
| 3043 | rtnl_lock(); | 3088 | rtnl_lock(); |
| 3089 | reg_crda_timeouts++; | ||
| 3044 | restore_regulatory_settings(true); | 3090 | restore_regulatory_settings(true); |
| 3045 | rtnl_unlock(); | 3091 | rtnl_unlock(); |
| 3046 | } | 3092 | } |
| @@ -3089,6 +3135,7 @@ int __init regulatory_init(void) | |||
| 3089 | 3135 | ||
| 3090 | spin_lock_init(®_requests_lock); | 3136 | spin_lock_init(®_requests_lock); |
| 3091 | spin_lock_init(®_pending_beacons_lock); | 3137 | spin_lock_init(®_pending_beacons_lock); |
| 3138 | spin_lock_init(®_indoor_lock); | ||
| 3092 | 3139 | ||
| 3093 | reg_regdb_size_check(); | 3140 | reg_regdb_size_check(); |
| 3094 | 3141 | ||
