.. | .. |
---|
5 | 5 | * |
---|
6 | 6 | * GPL LICENSE SUMMARY |
---|
7 | 7 | * |
---|
8 | | - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. |
---|
9 | 8 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
---|
10 | 9 | * Copyright(c) 2017 Intel Deutschland GmbH |
---|
11 | | - * Copyright(c) 2018 Intel Corporation |
---|
| 10 | + * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation |
---|
12 | 11 | * |
---|
13 | 12 | * This program is free software; you can redistribute it and/or modify |
---|
14 | 13 | * it under the terms of version 2 of the GNU General Public License as |
---|
.. | .. |
---|
28 | 27 | * |
---|
29 | 28 | * BSD LICENSE |
---|
30 | 29 | * |
---|
31 | | - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. |
---|
32 | 30 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
---|
33 | 31 | * Copyright(c) 2017 Intel Deutschland GmbH |
---|
34 | | - * Copyright(c) 2018 Intel Corporation |
---|
| 32 | + * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation |
---|
35 | 33 | * All rights reserved. |
---|
36 | 34 | * |
---|
37 | 35 | * Redistribution and use in source and binary forms, with or without |
---|
.. | .. |
---|
85 | 83 | { |
---|
86 | 84 | lockdep_assert_held(&mvm->time_event_lock); |
---|
87 | 85 | |
---|
88 | | - if (!te_data->vif) |
---|
| 86 | + if (!te_data || !te_data->vif) |
---|
89 | 87 | return; |
---|
90 | 88 | |
---|
91 | 89 | list_del(&te_data->list); |
---|
.. | .. |
---|
100 | 98 | struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk); |
---|
101 | 99 | |
---|
102 | 100 | /* |
---|
103 | | - * Clear the ROC_RUNNING /ROC_AUX_RUNNING status bit. |
---|
| 101 | + * Clear the ROC_RUNNING status bit. |
---|
104 | 102 | * This will cause the TX path to drop offchannel transmissions. |
---|
105 | 103 | * That would also be done by mac80211, but it is racy, in particular |
---|
106 | 104 | * in the case that the time event actually completed in the firmware |
---|
107 | 105 | * (which is handled in iwl_mvm_te_handle_notif). |
---|
108 | 106 | */ |
---|
109 | | - if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) |
---|
110 | | - iwl_mvm_unref(mvm, IWL_MVM_REF_ROC); |
---|
111 | | - if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) |
---|
112 | | - iwl_mvm_unref(mvm, IWL_MVM_REF_ROC_AUX); |
---|
| 107 | + clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); |
---|
113 | 108 | |
---|
114 | 109 | synchronize_net(); |
---|
115 | 110 | |
---|
.. | .. |
---|
118 | 113 | * event finishes or is canceled, so that frames queued for it |
---|
119 | 114 | * won't get stuck on the queue and be transmitted in the next |
---|
120 | 115 | * time event. |
---|
121 | | - * We have to send the command asynchronously since this cannot |
---|
122 | | - * be under the mutex for locking reasons, but that's not an |
---|
123 | | - * issue as it will have to complete before the next command is |
---|
124 | | - * executed, and a new time event means a new command. |
---|
125 | 116 | */ |
---|
126 | | - iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true, CMD_ASYNC); |
---|
127 | 117 | |
---|
128 | | - /* Do the same for the P2P device queue (STA) */ |
---|
| 118 | + mutex_lock(&mvm->mutex); |
---|
129 | 119 | if (test_and_clear_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) { |
---|
130 | 120 | struct iwl_mvm_vif *mvmvif; |
---|
131 | 121 | |
---|
.. | .. |
---|
138 | 128 | |
---|
139 | 129 | if (!WARN_ON(!mvm->p2p_device_vif)) { |
---|
140 | 130 | mvmvif = iwl_mvm_vif_from_mac80211(mvm->p2p_device_vif); |
---|
141 | | - iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true, |
---|
142 | | - CMD_ASYNC); |
---|
| 131 | + iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true); |
---|
143 | 132 | } |
---|
144 | 133 | } |
---|
| 134 | + |
---|
| 135 | + /* |
---|
| 136 | + * Clear the ROC_AUX_RUNNING status bit. |
---|
| 137 | + * This will cause the TX path to drop offchannel transmissions. |
---|
| 138 | + * That would also be done by mac80211, but it is racy, in particular |
---|
| 139 | + * in the case that the time event actually completed in the firmware |
---|
| 140 | + * (which is handled in iwl_mvm_te_handle_notif). |
---|
| 141 | + */ |
---|
| 142 | + if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) { |
---|
| 143 | + /* do the same in case of hot spot 2.0 */ |
---|
| 144 | + iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true); |
---|
| 145 | + |
---|
| 146 | + /* In newer version of this command an aux station is added only |
---|
| 147 | + * in cases of dedicated tx queue and need to be removed in end |
---|
| 148 | + * of use */ |
---|
| 149 | + if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, |
---|
| 150 | + ADD_STA, 0) >= 12) |
---|
| 151 | + iwl_mvm_rm_aux_sta(mvm); |
---|
| 152 | + } |
---|
| 153 | + |
---|
| 154 | + mutex_unlock(&mvm->mutex); |
---|
145 | 155 | } |
---|
146 | 156 | |
---|
147 | 157 | static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) |
---|
.. | .. |
---|
174 | 184 | * So we just do nothing here and the switch |
---|
175 | 185 | * will be performed on the last TBTT. |
---|
176 | 186 | */ |
---|
177 | | - if (!ieee80211_csa_is_complete(csa_vif)) { |
---|
| 187 | + if (!ieee80211_beacon_cntdwn_is_complete(csa_vif)) { |
---|
178 | 188 | IWL_WARN(mvm, "CSA NOA started too early\n"); |
---|
179 | 189 | goto out_unlock; |
---|
180 | 190 | } |
---|
.. | .. |
---|
234 | 244 | break; |
---|
235 | 245 | } |
---|
236 | 246 | iwl_mvm_csa_client_absent(mvm, te_data->vif); |
---|
| 247 | + cancel_delayed_work(&mvmvif->csa_work); |
---|
237 | 248 | ieee80211_chswitch_done(te_data->vif, true); |
---|
238 | 249 | break; |
---|
239 | 250 | default: |
---|
.. | .. |
---|
254 | 265 | struct iwl_fw_dbg_trigger_time_event *te_trig; |
---|
255 | 266 | int i; |
---|
256 | 267 | |
---|
257 | | - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT)) |
---|
| 268 | + trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, |
---|
| 269 | + ieee80211_vif_to_wdev(te_data->vif), |
---|
| 270 | + FW_DBG_TRIGGER_TIME_EVENT); |
---|
| 271 | + if (!trig) |
---|
258 | 272 | return; |
---|
259 | 273 | |
---|
260 | | - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT); |
---|
261 | 274 | te_trig = (void *)trig->data; |
---|
262 | | - |
---|
263 | | - if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt, |
---|
264 | | - ieee80211_vif_to_wdev(te_data->vif), |
---|
265 | | - trig)) |
---|
266 | | - return; |
---|
267 | 275 | |
---|
268 | 276 | for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) { |
---|
269 | 277 | u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id); |
---|
.. | .. |
---|
337 | 345 | switch (te_data->vif->type) { |
---|
338 | 346 | case NL80211_IFTYPE_P2P_DEVICE: |
---|
339 | 347 | ieee80211_remain_on_channel_expired(mvm->hw); |
---|
| 348 | + set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); |
---|
340 | 349 | iwl_mvm_roc_finished(mvm); |
---|
341 | 350 | break; |
---|
342 | 351 | case NL80211_IFTYPE_STATION: |
---|
.. | .. |
---|
345 | 354 | * and know the dtim period. |
---|
346 | 355 | */ |
---|
347 | 356 | iwl_mvm_te_check_disconnect(mvm, te_data->vif, |
---|
| 357 | + !te_data->vif->bss_conf.assoc ? |
---|
| 358 | + "Not associated and the time event is over already..." : |
---|
348 | 359 | "No beacon heard and the time event is over already..."); |
---|
349 | 360 | break; |
---|
350 | 361 | default: |
---|
.. | .. |
---|
358 | 369 | |
---|
359 | 370 | if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { |
---|
360 | 371 | set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); |
---|
361 | | - iwl_mvm_ref(mvm, IWL_MVM_REF_ROC); |
---|
362 | 372 | ieee80211_ready_on_channel(mvm->hw); |
---|
363 | 373 | } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) { |
---|
364 | 374 | iwl_mvm_te_handle_notify_csa(mvm, te_data, notif); |
---|
.. | .. |
---|
406 | 416 | } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) { |
---|
407 | 417 | set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); |
---|
408 | 418 | te_data->running = true; |
---|
409 | | - iwl_mvm_ref(mvm, IWL_MVM_REF_ROC_AUX); |
---|
410 | 419 | ieee80211_ready_on_channel(mvm->hw); /* Start TE */ |
---|
411 | 420 | } else { |
---|
412 | 421 | IWL_DEBUG_TE(mvm, |
---|
.. | .. |
---|
643 | 652 | } |
---|
644 | 653 | } |
---|
645 | 654 | |
---|
| 655 | +static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, |
---|
| 656 | + struct iwl_mvm_vif *mvmvif) |
---|
| 657 | +{ |
---|
| 658 | + struct iwl_mvm_session_prot_cmd cmd = { |
---|
| 659 | + .id_and_color = |
---|
| 660 | + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, |
---|
| 661 | + mvmvif->color)), |
---|
| 662 | + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), |
---|
| 663 | + .conf_id = cpu_to_le32(mvmvif->time_event_data.id), |
---|
| 664 | + }; |
---|
| 665 | + int ret; |
---|
| 666 | + |
---|
| 667 | + ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD, |
---|
| 668 | + MAC_CONF_GROUP, 0), |
---|
| 669 | + 0, sizeof(cmd), &cmd); |
---|
| 670 | + if (ret) |
---|
| 671 | + IWL_ERR(mvm, |
---|
| 672 | + "Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret); |
---|
| 673 | +} |
---|
| 674 | + |
---|
646 | 675 | static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, |
---|
647 | 676 | struct iwl_mvm_time_event_data *te_data, |
---|
648 | 677 | u32 *uid) |
---|
649 | 678 | { |
---|
650 | 679 | u32 id; |
---|
| 680 | + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); |
---|
651 | 681 | |
---|
652 | 682 | /* |
---|
653 | 683 | * It is possible that by the time we got to this point the time |
---|
.. | .. |
---|
665 | 695 | iwl_mvm_te_clear_data(mvm, te_data); |
---|
666 | 696 | spin_unlock_bh(&mvm->time_event_lock); |
---|
667 | 697 | |
---|
668 | | - /* |
---|
669 | | - * It is possible that by the time we try to remove it, the time event |
---|
670 | | - * has already ended and removed. In such a case there is no need to |
---|
671 | | - * send a removal command. |
---|
| 698 | + /* When session protection is supported, the te_data->id field |
---|
| 699 | + * is reused to save session protection's configuration. |
---|
672 | 700 | */ |
---|
673 | | - if (id == TE_MAX) { |
---|
674 | | - IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); |
---|
| 701 | + if (fw_has_capa(&mvm->fw->ucode_capa, |
---|
| 702 | + IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { |
---|
| 703 | + if (mvmvif && id < SESSION_PROTECT_CONF_MAX_ID) { |
---|
| 704 | + /* Session protection is still ongoing. Cancel it */ |
---|
| 705 | + iwl_mvm_cancel_session_protection(mvm, mvmvif); |
---|
| 706 | + if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { |
---|
| 707 | + set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); |
---|
| 708 | + iwl_mvm_roc_finished(mvm); |
---|
| 709 | + } |
---|
| 710 | + } |
---|
675 | 711 | return false; |
---|
| 712 | + } else { |
---|
| 713 | + /* It is possible that by the time we try to remove it, the |
---|
| 714 | + * time event has already ended and removed. In such a case |
---|
| 715 | + * there is no need to send a removal command. |
---|
| 716 | + */ |
---|
| 717 | + if (id == TE_MAX) { |
---|
| 718 | + IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); |
---|
| 719 | + return false; |
---|
| 720 | + } |
---|
676 | 721 | } |
---|
677 | 722 | |
---|
678 | 723 | return true; |
---|
.. | .. |
---|
689 | 734 | struct iwl_mvm_time_event_data *te_data) |
---|
690 | 735 | { |
---|
691 | 736 | struct iwl_hs20_roc_req aux_cmd = {}; |
---|
| 737 | + u16 len = sizeof(aux_cmd) - iwl_mvm_chan_info_padding(mvm); |
---|
| 738 | + |
---|
692 | 739 | u32 uid; |
---|
693 | 740 | int ret; |
---|
694 | 741 | |
---|
.. | .. |
---|
702 | 749 | IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n", |
---|
703 | 750 | le32_to_cpu(aux_cmd.event_unique_id)); |
---|
704 | 751 | ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, |
---|
705 | | - sizeof(aux_cmd), &aux_cmd); |
---|
| 752 | + len, &aux_cmd); |
---|
706 | 753 | |
---|
707 | 754 | if (WARN_ON(ret)) |
---|
708 | 755 | return; |
---|
.. | .. |
---|
737 | 784 | return; |
---|
738 | 785 | } |
---|
739 | 786 | |
---|
| 787 | +/* |
---|
| 788 | + * When the firmware supports the session protection API, |
---|
| 789 | + * this is not needed since it'll automatically remove the |
---|
| 790 | + * session protection after association + beacon reception. |
---|
| 791 | + */ |
---|
740 | 792 | void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, |
---|
741 | 793 | struct ieee80211_vif *vif) |
---|
742 | 794 | { |
---|
.. | .. |
---|
760 | 812 | iwl_mvm_remove_time_event(mvm, mvmvif, te_data); |
---|
761 | 813 | } |
---|
762 | 814 | |
---|
| 815 | +void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, |
---|
| 816 | + struct iwl_rx_cmd_buffer *rxb) |
---|
| 817 | +{ |
---|
| 818 | + struct iwl_rx_packet *pkt = rxb_addr(rxb); |
---|
| 819 | + struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data; |
---|
| 820 | + struct ieee80211_vif *vif; |
---|
| 821 | + struct iwl_mvm_vif *mvmvif; |
---|
| 822 | + |
---|
| 823 | + rcu_read_lock(); |
---|
| 824 | + vif = iwl_mvm_rcu_dereference_vif_id(mvm, le32_to_cpu(notif->mac_id), |
---|
| 825 | + true); |
---|
| 826 | + |
---|
| 827 | + if (!vif) |
---|
| 828 | + goto out_unlock; |
---|
| 829 | + |
---|
| 830 | + mvmvif = iwl_mvm_vif_from_mac80211(vif); |
---|
| 831 | + |
---|
| 832 | + /* The vif is not a P2P_DEVICE, maintain its time_event_data */ |
---|
| 833 | + if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { |
---|
| 834 | + struct iwl_mvm_time_event_data *te_data = |
---|
| 835 | + &mvmvif->time_event_data; |
---|
| 836 | + |
---|
| 837 | + if (!le32_to_cpu(notif->status)) { |
---|
| 838 | + iwl_mvm_te_check_disconnect(mvm, vif, |
---|
| 839 | + "Session protection failure"); |
---|
| 840 | + spin_lock_bh(&mvm->time_event_lock); |
---|
| 841 | + iwl_mvm_te_clear_data(mvm, te_data); |
---|
| 842 | + spin_unlock_bh(&mvm->time_event_lock); |
---|
| 843 | + } |
---|
| 844 | + |
---|
| 845 | + if (le32_to_cpu(notif->start)) { |
---|
| 846 | + spin_lock_bh(&mvm->time_event_lock); |
---|
| 847 | + te_data->running = le32_to_cpu(notif->start); |
---|
| 848 | + te_data->end_jiffies = |
---|
| 849 | + TU_TO_EXP_TIME(te_data->duration); |
---|
| 850 | + spin_unlock_bh(&mvm->time_event_lock); |
---|
| 851 | + } else { |
---|
| 852 | + /* |
---|
| 853 | + * By now, we should have finished association |
---|
| 854 | + * and know the dtim period. |
---|
| 855 | + */ |
---|
| 856 | + iwl_mvm_te_check_disconnect(mvm, vif, |
---|
| 857 | + !vif->bss_conf.assoc ? |
---|
| 858 | + "Not associated and the session protection is over already..." : |
---|
| 859 | + "No beacon heard and the session protection is over already..."); |
---|
| 860 | + spin_lock_bh(&mvm->time_event_lock); |
---|
| 861 | + iwl_mvm_te_clear_data(mvm, te_data); |
---|
| 862 | + spin_unlock_bh(&mvm->time_event_lock); |
---|
| 863 | + } |
---|
| 864 | + |
---|
| 865 | + goto out_unlock; |
---|
| 866 | + } |
---|
| 867 | + |
---|
| 868 | + if (!le32_to_cpu(notif->status) || !le32_to_cpu(notif->start)) { |
---|
| 869 | + /* End TE, notify mac80211 */ |
---|
| 870 | + mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID; |
---|
| 871 | + ieee80211_remain_on_channel_expired(mvm->hw); |
---|
| 872 | + set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); |
---|
| 873 | + iwl_mvm_roc_finished(mvm); |
---|
| 874 | + } else if (le32_to_cpu(notif->start)) { |
---|
| 875 | + if (WARN_ON(mvmvif->time_event_data.id != |
---|
| 876 | + le32_to_cpu(notif->conf_id))) |
---|
| 877 | + goto out_unlock; |
---|
| 878 | + set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); |
---|
| 879 | + ieee80211_ready_on_channel(mvm->hw); /* Start TE */ |
---|
| 880 | + } |
---|
| 881 | + |
---|
| 882 | + out_unlock: |
---|
| 883 | + rcu_read_unlock(); |
---|
| 884 | +} |
---|
| 885 | + |
---|
| 886 | +static int |
---|
| 887 | +iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm, |
---|
| 888 | + struct ieee80211_vif *vif, |
---|
| 889 | + int duration, |
---|
| 890 | + enum ieee80211_roc_type type) |
---|
| 891 | +{ |
---|
| 892 | + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
---|
| 893 | + struct iwl_mvm_session_prot_cmd cmd = { |
---|
| 894 | + .id_and_color = |
---|
| 895 | + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, |
---|
| 896 | + mvmvif->color)), |
---|
| 897 | + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), |
---|
| 898 | + .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), |
---|
| 899 | + }; |
---|
| 900 | + |
---|
| 901 | + lockdep_assert_held(&mvm->mutex); |
---|
| 902 | + |
---|
| 903 | + /* The time_event_data.id field is reused to save session |
---|
| 904 | + * protection's configuration. |
---|
| 905 | + */ |
---|
| 906 | + switch (type) { |
---|
| 907 | + case IEEE80211_ROC_TYPE_NORMAL: |
---|
| 908 | + mvmvif->time_event_data.id = |
---|
| 909 | + SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV; |
---|
| 910 | + break; |
---|
| 911 | + case IEEE80211_ROC_TYPE_MGMT_TX: |
---|
| 912 | + mvmvif->time_event_data.id = |
---|
| 913 | + SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION; |
---|
| 914 | + break; |
---|
| 915 | + default: |
---|
| 916 | + WARN_ONCE(1, "Got an invalid ROC type\n"); |
---|
| 917 | + return -EINVAL; |
---|
| 918 | + } |
---|
| 919 | + |
---|
| 920 | + cmd.conf_id = cpu_to_le32(mvmvif->time_event_data.id); |
---|
| 921 | + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD, |
---|
| 922 | + MAC_CONF_GROUP, 0), |
---|
| 923 | + 0, sizeof(cmd), &cmd); |
---|
| 924 | +} |
---|
| 925 | + |
---|
763 | 926 | int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
---|
764 | 927 | int duration, enum ieee80211_roc_type type) |
---|
765 | 928 | { |
---|
.. | .. |
---|
772 | 935 | IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n"); |
---|
773 | 936 | return -EBUSY; |
---|
774 | 937 | } |
---|
| 938 | + |
---|
| 939 | + if (fw_has_capa(&mvm->fw->ucode_capa, |
---|
| 940 | + IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) |
---|
| 941 | + return iwl_mvm_start_p2p_roc_session_protection(mvm, vif, |
---|
| 942 | + duration, |
---|
| 943 | + type); |
---|
775 | 944 | |
---|
776 | 945 | time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); |
---|
777 | 946 | time_cmd.id_and_color = |
---|
.. | .. |
---|
850 | 1019 | __iwl_mvm_remove_time_event(mvm, te_data, &uid); |
---|
851 | 1020 | } |
---|
852 | 1021 | |
---|
853 | | -void iwl_mvm_stop_roc(struct iwl_mvm *mvm) |
---|
| 1022 | +void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) |
---|
854 | 1023 | { |
---|
855 | 1024 | struct iwl_mvm_vif *mvmvif; |
---|
856 | 1025 | struct iwl_mvm_time_event_data *te_data; |
---|
| 1026 | + |
---|
| 1027 | + if (fw_has_capa(&mvm->fw->ucode_capa, |
---|
| 1028 | + IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { |
---|
| 1029 | + mvmvif = iwl_mvm_vif_from_mac80211(vif); |
---|
| 1030 | + |
---|
| 1031 | + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { |
---|
| 1032 | + iwl_mvm_cancel_session_protection(mvm, mvmvif); |
---|
| 1033 | + set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); |
---|
| 1034 | + } else { |
---|
| 1035 | + iwl_mvm_remove_aux_roc_te(mvm, mvmvif, |
---|
| 1036 | + &mvmvif->time_event_data); |
---|
| 1037 | + } |
---|
| 1038 | + |
---|
| 1039 | + iwl_mvm_roc_finished(mvm); |
---|
| 1040 | + |
---|
| 1041 | + return; |
---|
| 1042 | + } |
---|
857 | 1043 | |
---|
858 | 1044 | te_data = iwl_mvm_get_roc_te(mvm); |
---|
859 | 1045 | if (!te_data) { |
---|
.. | .. |
---|
871 | 1057 | } |
---|
872 | 1058 | |
---|
873 | 1059 | iwl_mvm_roc_finished(mvm); |
---|
| 1060 | +} |
---|
| 1061 | + |
---|
| 1062 | +void iwl_mvm_remove_csa_period(struct iwl_mvm *mvm, |
---|
| 1063 | + struct ieee80211_vif *vif) |
---|
| 1064 | +{ |
---|
| 1065 | + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
---|
| 1066 | + struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; |
---|
| 1067 | + u32 id; |
---|
| 1068 | + |
---|
| 1069 | + lockdep_assert_held(&mvm->mutex); |
---|
| 1070 | + |
---|
| 1071 | + spin_lock_bh(&mvm->time_event_lock); |
---|
| 1072 | + id = te_data->id; |
---|
| 1073 | + spin_unlock_bh(&mvm->time_event_lock); |
---|
| 1074 | + |
---|
| 1075 | + if (id != TE_CHANNEL_SWITCH_PERIOD) |
---|
| 1076 | + return; |
---|
| 1077 | + |
---|
| 1078 | + iwl_mvm_remove_time_event(mvm, mvmvif, te_data); |
---|
874 | 1079 | } |
---|
875 | 1080 | |
---|
876 | 1081 | int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, |
---|
.. | .. |
---|
919 | 1124 | |
---|
920 | 1125 | return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); |
---|
921 | 1126 | } |
---|
| 1127 | + |
---|
| 1128 | +static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait, |
---|
| 1129 | + struct iwl_rx_packet *pkt, void *data) |
---|
| 1130 | +{ |
---|
| 1131 | + struct iwl_mvm *mvm = |
---|
| 1132 | + container_of(notif_wait, struct iwl_mvm, notif_wait); |
---|
| 1133 | + struct iwl_mvm_session_prot_notif *resp; |
---|
| 1134 | + int resp_len = iwl_rx_packet_payload_len(pkt); |
---|
| 1135 | + |
---|
| 1136 | + if (WARN_ON(pkt->hdr.cmd != SESSION_PROTECTION_NOTIF || |
---|
| 1137 | + pkt->hdr.group_id != MAC_CONF_GROUP)) |
---|
| 1138 | + return true; |
---|
| 1139 | + |
---|
| 1140 | + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { |
---|
| 1141 | + IWL_ERR(mvm, "Invalid SESSION_PROTECTION_NOTIF response\n"); |
---|
| 1142 | + return true; |
---|
| 1143 | + } |
---|
| 1144 | + |
---|
| 1145 | + resp = (void *)pkt->data; |
---|
| 1146 | + |
---|
| 1147 | + if (!resp->status) |
---|
| 1148 | + IWL_ERR(mvm, |
---|
| 1149 | + "TIME_EVENT_NOTIFICATION received but not executed\n"); |
---|
| 1150 | + |
---|
| 1151 | + return true; |
---|
| 1152 | +} |
---|
| 1153 | + |
---|
| 1154 | +void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, |
---|
| 1155 | + struct ieee80211_vif *vif, |
---|
| 1156 | + u32 duration, u32 min_duration, |
---|
| 1157 | + bool wait_for_notif) |
---|
| 1158 | +{ |
---|
| 1159 | + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
---|
| 1160 | + struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; |
---|
| 1161 | + const u16 notif[] = { iwl_cmd_id(SESSION_PROTECTION_NOTIF, |
---|
| 1162 | + MAC_CONF_GROUP, 0) }; |
---|
| 1163 | + struct iwl_notification_wait wait_notif; |
---|
| 1164 | + struct iwl_mvm_session_prot_cmd cmd = { |
---|
| 1165 | + .id_and_color = |
---|
| 1166 | + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, |
---|
| 1167 | + mvmvif->color)), |
---|
| 1168 | + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), |
---|
| 1169 | + .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), |
---|
| 1170 | + .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), |
---|
| 1171 | + }; |
---|
| 1172 | + |
---|
| 1173 | + lockdep_assert_held(&mvm->mutex); |
---|
| 1174 | + |
---|
| 1175 | + spin_lock_bh(&mvm->time_event_lock); |
---|
| 1176 | + if (te_data->running && |
---|
| 1177 | + time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { |
---|
| 1178 | + IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", |
---|
| 1179 | + jiffies_to_msecs(te_data->end_jiffies - jiffies)); |
---|
| 1180 | + spin_unlock_bh(&mvm->time_event_lock); |
---|
| 1181 | + |
---|
| 1182 | + return; |
---|
| 1183 | + } |
---|
| 1184 | + |
---|
| 1185 | + iwl_mvm_te_clear_data(mvm, te_data); |
---|
| 1186 | + /* |
---|
| 1187 | + * The time_event_data.id field is reused to save session |
---|
| 1188 | + * protection's configuration. |
---|
| 1189 | + */ |
---|
| 1190 | + te_data->id = le32_to_cpu(cmd.conf_id); |
---|
| 1191 | + te_data->duration = le32_to_cpu(cmd.duration_tu); |
---|
| 1192 | + spin_unlock_bh(&mvm->time_event_lock); |
---|
| 1193 | + |
---|
| 1194 | + IWL_DEBUG_TE(mvm, "Add new session protection, duration %d TU\n", |
---|
| 1195 | + le32_to_cpu(cmd.duration_tu)); |
---|
| 1196 | + |
---|
| 1197 | + if (!wait_for_notif) { |
---|
| 1198 | + if (iwl_mvm_send_cmd_pdu(mvm, |
---|
| 1199 | + iwl_cmd_id(SESSION_PROTECTION_CMD, |
---|
| 1200 | + MAC_CONF_GROUP, 0), |
---|
| 1201 | + 0, sizeof(cmd), &cmd)) { |
---|
| 1202 | + IWL_ERR(mvm, |
---|
| 1203 | + "Couldn't send the SESSION_PROTECTION_CMD\n"); |
---|
| 1204 | + spin_lock_bh(&mvm->time_event_lock); |
---|
| 1205 | + iwl_mvm_te_clear_data(mvm, te_data); |
---|
| 1206 | + spin_unlock_bh(&mvm->time_event_lock); |
---|
| 1207 | + } |
---|
| 1208 | + |
---|
| 1209 | + return; |
---|
| 1210 | + } |
---|
| 1211 | + |
---|
| 1212 | + iwl_init_notification_wait(&mvm->notif_wait, &wait_notif, |
---|
| 1213 | + notif, ARRAY_SIZE(notif), |
---|
| 1214 | + iwl_mvm_session_prot_notif, NULL); |
---|
| 1215 | + |
---|
| 1216 | + if (iwl_mvm_send_cmd_pdu(mvm, |
---|
| 1217 | + iwl_cmd_id(SESSION_PROTECTION_CMD, |
---|
| 1218 | + MAC_CONF_GROUP, 0), |
---|
| 1219 | + 0, sizeof(cmd), &cmd)) { |
---|
| 1220 | + IWL_ERR(mvm, |
---|
| 1221 | + "Couldn't send the SESSION_PROTECTION_CMD\n"); |
---|
| 1222 | + iwl_remove_notification(&mvm->notif_wait, &wait_notif); |
---|
| 1223 | + } else if (iwl_wait_notification(&mvm->notif_wait, &wait_notif, |
---|
| 1224 | + TU_TO_JIFFIES(100))) { |
---|
| 1225 | + IWL_ERR(mvm, |
---|
| 1226 | + "Failed to protect session until session protection\n"); |
---|
| 1227 | + } |
---|
| 1228 | +} |
---|