diff options
Diffstat (limited to 'net/mac80211/util.c')
-rw-r--r-- | net/mac80211/util.c | 320 |
1 files changed, 315 insertions, 5 deletions
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 9f9b9bd3fd44..676dc0967f37 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -76,7 +76,7 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, | |||
76 | } | 76 | } |
77 | 77 | ||
78 | if (ieee80211_is_ctl(fc)) { | 78 | if (ieee80211_is_ctl(fc)) { |
79 | if(ieee80211_is_pspoll(fc)) | 79 | if (ieee80211_is_pspoll(fc)) |
80 | return hdr->addr1; | 80 | return hdr->addr1; |
81 | 81 | ||
82 | if (ieee80211_is_back_req(fc)) { | 82 | if (ieee80211_is_back_req(fc)) { |
@@ -642,6 +642,17 @@ void ieee80211_iterate_active_interfaces_rtnl( | |||
642 | } | 642 | } |
643 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); | 643 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); |
644 | 644 | ||
645 | struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) | ||
646 | { | ||
647 | struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); | ||
648 | |||
649 | if (!ieee80211_sdata_running(sdata) || | ||
650 | !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) | ||
651 | return NULL; | ||
652 | return &sdata->vif; | ||
653 | } | ||
654 | EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); | ||
655 | |||
645 | /* | 656 | /* |
646 | * Nothing should have been stuffed into the workqueue during | 657 | * Nothing should have been stuffed into the workqueue during |
647 | * the suspend->resume cycle. If this WARN is seen then there | 658 | * the suspend->resume cycle. If this WARN is seen then there |
@@ -1451,6 +1462,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1451 | struct sta_info *sta; | 1462 | struct sta_info *sta; |
1452 | int res, i; | 1463 | int res, i; |
1453 | bool reconfig_due_to_wowlan = false; | 1464 | bool reconfig_due_to_wowlan = false; |
1465 | struct ieee80211_sub_if_data *sched_scan_sdata; | ||
1466 | bool sched_scan_stopped = false; | ||
1454 | 1467 | ||
1455 | #ifdef CONFIG_PM | 1468 | #ifdef CONFIG_PM |
1456 | if (local->suspended) | 1469 | if (local->suspended) |
@@ -1754,6 +1767,27 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1754 | #else | 1767 | #else |
1755 | WARN_ON(1); | 1768 | WARN_ON(1); |
1756 | #endif | 1769 | #endif |
1770 | |||
1771 | /* | ||
1772 | * Reconfigure sched scan if it was interrupted by FW restart or | ||
1773 | * suspend. | ||
1774 | */ | ||
1775 | mutex_lock(&local->mtx); | ||
1776 | sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, | ||
1777 | lockdep_is_held(&local->mtx)); | ||
1778 | if (sched_scan_sdata && local->sched_scan_req) | ||
1779 | /* | ||
1780 | * Sched scan stopped, but we don't want to report it. Instead, | ||
1781 | * we're trying to reschedule. | ||
1782 | */ | ||
1783 | if (__ieee80211_request_sched_scan_start(sched_scan_sdata, | ||
1784 | local->sched_scan_req)) | ||
1785 | sched_scan_stopped = true; | ||
1786 | mutex_unlock(&local->mtx); | ||
1787 | |||
1788 | if (sched_scan_stopped) | ||
1789 | cfg80211_sched_scan_stopped(local->hw.wiphy); | ||
1790 | |||
1757 | return 0; | 1791 | return 0; |
1758 | } | 1792 | } |
1759 | 1793 | ||
@@ -1804,6 +1838,26 @@ void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) | |||
1804 | mutex_unlock(&local->chanctx_mtx); | 1838 | mutex_unlock(&local->chanctx_mtx); |
1805 | } | 1839 | } |
1806 | 1840 | ||
1841 | void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) | ||
1842 | { | ||
1843 | struct ieee80211_local *local = sdata->local; | ||
1844 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
1845 | struct ieee80211_chanctx *chanctx; | ||
1846 | |||
1847 | mutex_lock(&local->chanctx_mtx); | ||
1848 | |||
1849 | chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | ||
1850 | lockdep_is_held(&local->chanctx_mtx)); | ||
1851 | |||
1852 | if (WARN_ON_ONCE(!chanctx_conf)) | ||
1853 | goto unlock; | ||
1854 | |||
1855 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); | ||
1856 | ieee80211_recalc_chanctx_min_def(local, chanctx); | ||
1857 | unlock: | ||
1858 | mutex_unlock(&local->chanctx_mtx); | ||
1859 | } | ||
1860 | |||
1807 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | 1861 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) |
1808 | { | 1862 | { |
1809 | int i; | 1863 | int i; |
@@ -2259,19 +2313,28 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, | |||
2259 | void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) | 2313 | void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) |
2260 | { | 2314 | { |
2261 | struct ieee80211_sub_if_data *sdata; | 2315 | struct ieee80211_sub_if_data *sdata; |
2316 | struct cfg80211_chan_def chandef; | ||
2262 | 2317 | ||
2318 | mutex_lock(&local->mtx); | ||
2263 | mutex_lock(&local->iflist_mtx); | 2319 | mutex_lock(&local->iflist_mtx); |
2264 | list_for_each_entry(sdata, &local->interfaces, list) { | 2320 | list_for_each_entry(sdata, &local->interfaces, list) { |
2265 | cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); | 2321 | /* it might be waiting for the local->mtx, but then |
2322 | * by the time it gets it, sdata->wdev.cac_started | ||
2323 | * will no longer be true | ||
2324 | */ | ||
2325 | cancel_delayed_work(&sdata->dfs_cac_timer_work); | ||
2266 | 2326 | ||
2267 | if (sdata->wdev.cac_started) { | 2327 | if (sdata->wdev.cac_started) { |
2328 | chandef = sdata->vif.bss_conf.chandef; | ||
2268 | ieee80211_vif_release_channel(sdata); | 2329 | ieee80211_vif_release_channel(sdata); |
2269 | cfg80211_cac_event(sdata->dev, | 2330 | cfg80211_cac_event(sdata->dev, |
2331 | &chandef, | ||
2270 | NL80211_RADAR_CAC_ABORTED, | 2332 | NL80211_RADAR_CAC_ABORTED, |
2271 | GFP_KERNEL); | 2333 | GFP_KERNEL); |
2272 | } | 2334 | } |
2273 | } | 2335 | } |
2274 | mutex_unlock(&local->iflist_mtx); | 2336 | mutex_unlock(&local->iflist_mtx); |
2337 | mutex_unlock(&local->mtx); | ||
2275 | } | 2338 | } |
2276 | 2339 | ||
2277 | void ieee80211_dfs_radar_detected_work(struct work_struct *work) | 2340 | void ieee80211_dfs_radar_detected_work(struct work_struct *work) |
@@ -2445,7 +2508,6 @@ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, | |||
2445 | 2508 | ||
2446 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | 2509 | if (ieee80211_vif_is_mesh(&sdata->vif)) { |
2447 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | 2510 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
2448 | __le16 pre_value; | ||
2449 | 2511 | ||
2450 | skb_put(skb, 8); | 2512 | skb_put(skb, 8); |
2451 | *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ | 2513 | *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ |
@@ -2457,11 +2519,259 @@ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, | |||
2457 | WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; | 2519 | WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; |
2458 | put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ | 2520 | put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ |
2459 | pos += 2; | 2521 | pos += 2; |
2460 | pre_value = cpu_to_le16(ifmsh->pre_value); | 2522 | put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */ |
2461 | memcpy(pos, &pre_value, 2); /* Precedence Value */ | ||
2462 | pos += 2; | 2523 | pos += 2; |
2463 | } | 2524 | } |
2464 | 2525 | ||
2465 | ieee80211_tx_skb(sdata, skb); | 2526 | ieee80211_tx_skb(sdata, skb); |
2466 | return 0; | 2527 | return 0; |
2467 | } | 2528 | } |
2529 | |||
2530 | bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs) | ||
2531 | { | ||
2532 | return !(cs == NULL || cs->cipher == 0 || | ||
2533 | cs->hdr_len < cs->pn_len + cs->pn_off || | ||
2534 | cs->hdr_len <= cs->key_idx_off || | ||
2535 | cs->key_idx_shift > 7 || | ||
2536 | cs->key_idx_mask == 0); | ||
2537 | } | ||
2538 | |||
2539 | bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n) | ||
2540 | { | ||
2541 | int i; | ||
2542 | |||
2543 | /* Ensure we have enough iftype bitmap space for all iftype values */ | ||
2544 | WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype)); | ||
2545 | |||
2546 | for (i = 0; i < n; i++) | ||
2547 | if (!ieee80211_cs_valid(&cs[i])) | ||
2548 | return false; | ||
2549 | |||
2550 | return true; | ||
2551 | } | ||
2552 | |||
2553 | const struct ieee80211_cipher_scheme * | ||
2554 | ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, | ||
2555 | enum nl80211_iftype iftype) | ||
2556 | { | ||
2557 | const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes; | ||
2558 | int n = local->hw.n_cipher_schemes; | ||
2559 | int i; | ||
2560 | const struct ieee80211_cipher_scheme *cs = NULL; | ||
2561 | |||
2562 | for (i = 0; i < n; i++) { | ||
2563 | if (l[i].cipher == cipher) { | ||
2564 | cs = &l[i]; | ||
2565 | break; | ||
2566 | } | ||
2567 | } | ||
2568 | |||
2569 | if (!cs || !(cs->iftype & BIT(iftype))) | ||
2570 | return NULL; | ||
2571 | |||
2572 | return cs; | ||
2573 | } | ||
2574 | |||
2575 | int ieee80211_cs_headroom(struct ieee80211_local *local, | ||
2576 | struct cfg80211_crypto_settings *crypto, | ||
2577 | enum nl80211_iftype iftype) | ||
2578 | { | ||
2579 | const struct ieee80211_cipher_scheme *cs; | ||
2580 | int headroom = IEEE80211_ENCRYPT_HEADROOM; | ||
2581 | int i; | ||
2582 | |||
2583 | for (i = 0; i < crypto->n_ciphers_pairwise; i++) { | ||
2584 | cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i], | ||
2585 | iftype); | ||
2586 | |||
2587 | if (cs && headroom < cs->hdr_len) | ||
2588 | headroom = cs->hdr_len; | ||
2589 | } | ||
2590 | |||
2591 | cs = ieee80211_cs_get(local, crypto->cipher_group, iftype); | ||
2592 | if (cs && headroom < cs->hdr_len) | ||
2593 | headroom = cs->hdr_len; | ||
2594 | |||
2595 | return headroom; | ||
2596 | } | ||
2597 | |||
2598 | static bool | ||
2599 | ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i) | ||
2600 | { | ||
2601 | s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1); | ||
2602 | int skip; | ||
2603 | |||
2604 | if (end > 0) | ||
2605 | return false; | ||
2606 | |||
2607 | /* End time is in the past, check for repetitions */ | ||
2608 | skip = DIV_ROUND_UP(-end, data->desc[i].interval); | ||
2609 | if (data->count[i] < 255) { | ||
2610 | if (data->count[i] <= skip) { | ||
2611 | data->count[i] = 0; | ||
2612 | return false; | ||
2613 | } | ||
2614 | |||
2615 | data->count[i] -= skip; | ||
2616 | } | ||
2617 | |||
2618 | data->desc[i].start += skip * data->desc[i].interval; | ||
2619 | |||
2620 | return true; | ||
2621 | } | ||
2622 | |||
2623 | static bool | ||
2624 | ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf, | ||
2625 | s32 *offset) | ||
2626 | { | ||
2627 | bool ret = false; | ||
2628 | int i; | ||
2629 | |||
2630 | for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { | ||
2631 | s32 cur; | ||
2632 | |||
2633 | if (!data->count[i]) | ||
2634 | continue; | ||
2635 | |||
2636 | if (ieee80211_extend_noa_desc(data, tsf + *offset, i)) | ||
2637 | ret = true; | ||
2638 | |||
2639 | cur = data->desc[i].start - tsf; | ||
2640 | if (cur > *offset) | ||
2641 | continue; | ||
2642 | |||
2643 | cur = data->desc[i].start + data->desc[i].duration - tsf; | ||
2644 | if (cur > *offset) | ||
2645 | *offset = cur; | ||
2646 | } | ||
2647 | |||
2648 | return ret; | ||
2649 | } | ||
2650 | |||
2651 | static u32 | ||
2652 | ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf) | ||
2653 | { | ||
2654 | s32 offset = 0; | ||
2655 | int tries = 0; | ||
2656 | /* | ||
2657 | * arbitrary limit, used to avoid infinite loops when combined NoA | ||
2658 | * descriptors cover the full time period. | ||
2659 | */ | ||
2660 | int max_tries = 5; | ||
2661 | |||
2662 | ieee80211_extend_absent_time(data, tsf, &offset); | ||
2663 | do { | ||
2664 | if (!ieee80211_extend_absent_time(data, tsf, &offset)) | ||
2665 | break; | ||
2666 | |||
2667 | tries++; | ||
2668 | } while (tries < max_tries); | ||
2669 | |||
2670 | return offset; | ||
2671 | } | ||
2672 | |||
2673 | void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf) | ||
2674 | { | ||
2675 | u32 next_offset = BIT(31) - 1; | ||
2676 | int i; | ||
2677 | |||
2678 | data->absent = 0; | ||
2679 | data->has_next_tsf = false; | ||
2680 | for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { | ||
2681 | s32 start; | ||
2682 | |||
2683 | if (!data->count[i]) | ||
2684 | continue; | ||
2685 | |||
2686 | ieee80211_extend_noa_desc(data, tsf, i); | ||
2687 | start = data->desc[i].start - tsf; | ||
2688 | if (start <= 0) | ||
2689 | data->absent |= BIT(i); | ||
2690 | |||
2691 | if (next_offset > start) | ||
2692 | next_offset = start; | ||
2693 | |||
2694 | data->has_next_tsf = true; | ||
2695 | } | ||
2696 | |||
2697 | if (data->absent) | ||
2698 | next_offset = ieee80211_get_noa_absent_time(data, tsf); | ||
2699 | |||
2700 | data->next_tsf = tsf + next_offset; | ||
2701 | } | ||
2702 | EXPORT_SYMBOL(ieee80211_update_p2p_noa); | ||
2703 | |||
2704 | int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, | ||
2705 | struct ieee80211_noa_data *data, u32 tsf) | ||
2706 | { | ||
2707 | int ret = 0; | ||
2708 | int i; | ||
2709 | |||
2710 | memset(data, 0, sizeof(*data)); | ||
2711 | |||
2712 | for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { | ||
2713 | const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i]; | ||
2714 | |||
2715 | if (!desc->count || !desc->duration) | ||
2716 | continue; | ||
2717 | |||
2718 | data->count[i] = desc->count; | ||
2719 | data->desc[i].start = le32_to_cpu(desc->start_time); | ||
2720 | data->desc[i].duration = le32_to_cpu(desc->duration); | ||
2721 | data->desc[i].interval = le32_to_cpu(desc->interval); | ||
2722 | |||
2723 | if (data->count[i] > 1 && | ||
2724 | data->desc[i].interval < data->desc[i].duration) | ||
2725 | continue; | ||
2726 | |||
2727 | ieee80211_extend_noa_desc(data, tsf, i); | ||
2728 | ret++; | ||
2729 | } | ||
2730 | |||
2731 | if (ret) | ||
2732 | ieee80211_update_p2p_noa(data, tsf); | ||
2733 | |||
2734 | return ret; | ||
2735 | } | ||
2736 | EXPORT_SYMBOL(ieee80211_parse_p2p_noa); | ||
2737 | |||
2738 | void ieee80211_recalc_dtim(struct ieee80211_local *local, | ||
2739 | struct ieee80211_sub_if_data *sdata) | ||
2740 | { | ||
2741 | u64 tsf = drv_get_tsf(local, sdata); | ||
2742 | u64 dtim_count = 0; | ||
2743 | u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024; | ||
2744 | u8 dtim_period = sdata->vif.bss_conf.dtim_period; | ||
2745 | struct ps_data *ps; | ||
2746 | u8 bcns_from_dtim; | ||
2747 | |||
2748 | if (tsf == -1ULL || !beacon_int || !dtim_period) | ||
2749 | return; | ||
2750 | |||
2751 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
2752 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | ||
2753 | if (!sdata->bss) | ||
2754 | return; | ||
2755 | |||
2756 | ps = &sdata->bss->ps; | ||
2757 | } else if (ieee80211_vif_is_mesh(&sdata->vif)) { | ||
2758 | ps = &sdata->u.mesh.ps; | ||
2759 | } else { | ||
2760 | return; | ||
2761 | } | ||
2762 | |||
2763 | /* | ||
2764 | * actually finds last dtim_count, mac80211 will update in | ||
2765 | * __beacon_add_tim(). | ||
2766 | * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period | ||
2767 | */ | ||
2768 | do_div(tsf, beacon_int); | ||
2769 | bcns_from_dtim = do_div(tsf, dtim_period); | ||
2770 | /* just had a DTIM */ | ||
2771 | if (!bcns_from_dtim) | ||
2772 | dtim_count = 0; | ||
2773 | else | ||
2774 | dtim_count = dtim_period - bcns_from_dtim; | ||
2775 | |||
2776 | ps->dtim_count = dtim_count; | ||
2777 | } | ||