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