objtool: Re-arrange validate_functions()
[linux-block.git] / drivers / net / wireless / intel / iwlwifi / mvm / time-event.c
1 /******************************************************************************
2  *
3  * This file is provided under a dual BSD/GPLv2 license.  When using or
4  * redistributing this file, you may do so under either license.
5  *
6  * GPL LICENSE SUMMARY
7  *
8  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
9  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
10  * Copyright(c) 2017 Intel Deutschland GmbH
11  * Copyright(c) 2018 - 2019 Intel Corporation
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of version 2 of the GNU General Public License as
15  * published by the Free Software Foundation.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  *
22  * The full GNU General Public License is included in this distribution
23  * in the file called COPYING.
24  *
25  * Contact Information:
26  *  Intel Linux Wireless <linuxwifi@intel.com>
27  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
28  *
29  * BSD LICENSE
30  *
31  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
32  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
33  * Copyright(c) 2017 Intel Deutschland GmbH
34  * Copyright(c) 2018 - 2019 Intel Corporation
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  *
41  *  * Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  *  * Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in
45  *    the documentation and/or other materials provided with the
46  *    distribution.
47  *  * Neither the name Intel Corporation nor the names of its
48  *    contributors may be used to endorse or promote products derived
49  *    from this software without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
52  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
53  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
54  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
55  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
56  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
57  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
58  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
59  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
60  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
61  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62  *
63  *****************************************************************************/
64
65 #include <linux/jiffies.h>
66 #include <net/mac80211.h>
67
68 #include "fw/notif-wait.h"
69 #include "iwl-trans.h"
70 #include "fw-api.h"
71 #include "time-event.h"
72 #include "mvm.h"
73 #include "iwl-io.h"
74 #include "iwl-prph.h"
75
76 /*
77  * For the high priority TE use a time event type that has similar priority to
78  * the FW's action scan priority.
79  */
80 #define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE
81 #define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC
82
83 void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
84                            struct iwl_mvm_time_event_data *te_data)
85 {
86         lockdep_assert_held(&mvm->time_event_lock);
87
88         if (!te_data || !te_data->vif)
89                 return;
90
91         list_del(&te_data->list);
92         te_data->running = false;
93         te_data->uid = 0;
94         te_data->id = TE_MAX;
95         te_data->vif = NULL;
96 }
97
98 void iwl_mvm_roc_done_wk(struct work_struct *wk)
99 {
100         struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
101
102         /*
103          * Clear the ROC_RUNNING /ROC_AUX_RUNNING status bit.
104          * This will cause the TX path to drop offchannel transmissions.
105          * That would also be done by mac80211, but it is racy, in particular
106          * in the case that the time event actually completed in the firmware
107          * (which is handled in iwl_mvm_te_handle_notif).
108          */
109         clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
110         clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
111
112         synchronize_net();
113
114         /*
115          * Flush the offchannel queue -- this is called when the time
116          * event finishes or is canceled, so that frames queued for it
117          * won't get stuck on the queue and be transmitted in the next
118          * time event.
119          * We have to send the command asynchronously since this cannot
120          * be under the mutex for locking reasons, but that's not an
121          * issue as it will have to complete before the next command is
122          * executed, and a new time event means a new command.
123          */
124         iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true, CMD_ASYNC);
125
126         /* Do the same for the P2P device queue (STA) */
127         if (test_and_clear_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) {
128                 struct iwl_mvm_vif *mvmvif;
129
130                 /*
131                  * NB: access to this pointer would be racy, but the flush bit
132                  * can only be set when we had a P2P-Device VIF, and we have a
133                  * flush of this work in iwl_mvm_prepare_mac_removal() so it's
134                  * not really racy.
135                  */
136
137                 if (!WARN_ON(!mvm->p2p_device_vif)) {
138                         mvmvif = iwl_mvm_vif_from_mac80211(mvm->p2p_device_vif);
139                         iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true,
140                                           CMD_ASYNC);
141                 }
142         }
143 }
144
145 static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
146 {
147         /*
148          * Of course, our status bit is just as racy as mac80211, so in
149          * addition, fire off the work struct which will drop all frames
150          * from the hardware queues that made it through the race. First
151          * it will of course synchronize the TX path to make sure that
152          * any *new* TX will be rejected.
153          */
154         schedule_work(&mvm->roc_done_wk);
155 }
156
157 static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
158 {
159         struct ieee80211_vif *csa_vif;
160
161         rcu_read_lock();
162
163         csa_vif = rcu_dereference(mvm->csa_vif);
164         if (!csa_vif || !csa_vif->csa_active)
165                 goto out_unlock;
166
167         IWL_DEBUG_TE(mvm, "CSA NOA started\n");
168
169         /*
170          * CSA NoA is started but we still have beacons to
171          * transmit on the current channel.
172          * So we just do nothing here and the switch
173          * will be performed on the last TBTT.
174          */
175         if (!ieee80211_csa_is_complete(csa_vif)) {
176                 IWL_WARN(mvm, "CSA NOA started too early\n");
177                 goto out_unlock;
178         }
179
180         ieee80211_csa_finish(csa_vif);
181
182         rcu_read_unlock();
183
184         RCU_INIT_POINTER(mvm->csa_vif, NULL);
185
186         return;
187
188 out_unlock:
189         rcu_read_unlock();
190 }
191
192 static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
193                                         struct ieee80211_vif *vif,
194                                         const char *errmsg)
195 {
196         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
197
198         if (vif->type != NL80211_IFTYPE_STATION)
199                 return false;
200
201         if (!mvmvif->csa_bcn_pending && vif->bss_conf.assoc &&
202             vif->bss_conf.dtim_period)
203                 return false;
204         if (errmsg)
205                 IWL_ERR(mvm, "%s\n", errmsg);
206
207         iwl_mvm_connection_loss(mvm, vif, errmsg);
208         return true;
209 }
210
211 static void
212 iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
213                              struct iwl_mvm_time_event_data *te_data,
214                              struct iwl_time_event_notif *notif)
215 {
216         struct ieee80211_vif *vif = te_data->vif;
217         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
218
219         if (!notif->status)
220                 IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
221
222         switch (te_data->vif->type) {
223         case NL80211_IFTYPE_AP:
224                 if (!notif->status)
225                         mvmvif->csa_failed = true;
226                 iwl_mvm_csa_noa_start(mvm);
227                 break;
228         case NL80211_IFTYPE_STATION:
229                 if (!notif->status) {
230                         iwl_mvm_connection_loss(mvm, vif,
231                                                 "CSA TE failed to start");
232                         break;
233                 }
234                 iwl_mvm_csa_client_absent(mvm, te_data->vif);
235                 cancel_delayed_work(&mvmvif->csa_work);
236                 ieee80211_chswitch_done(te_data->vif, true);
237                 break;
238         default:
239                 /* should never happen */
240                 WARN_ON_ONCE(1);
241                 break;
242         }
243
244         /* we don't need it anymore */
245         iwl_mvm_te_clear_data(mvm, te_data);
246 }
247
248 static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm,
249                                      struct iwl_time_event_notif *notif,
250                                      struct iwl_mvm_time_event_data *te_data)
251 {
252         struct iwl_fw_dbg_trigger_tlv *trig;
253         struct iwl_fw_dbg_trigger_time_event *te_trig;
254         int i;
255
256         trig = iwl_fw_dbg_trigger_on(&mvm->fwrt,
257                                      ieee80211_vif_to_wdev(te_data->vif),
258                                      FW_DBG_TRIGGER_TIME_EVENT);
259         if (!trig)
260                 return;
261
262         te_trig = (void *)trig->data;
263
264         for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) {
265                 u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id);
266                 u32 trig_action_bitmap =
267                         le32_to_cpu(te_trig->time_events[i].action_bitmap);
268                 u32 trig_status_bitmap =
269                         le32_to_cpu(te_trig->time_events[i].status_bitmap);
270
271                 if (trig_te_id != te_data->id ||
272                     !(trig_action_bitmap & le32_to_cpu(notif->action)) ||
273                     !(trig_status_bitmap & BIT(le32_to_cpu(notif->status))))
274                         continue;
275
276                 iwl_fw_dbg_collect_trig(&mvm->fwrt, trig,
277                                         "Time event %d Action 0x%x received status: %d",
278                                         te_data->id,
279                                         le32_to_cpu(notif->action),
280                                         le32_to_cpu(notif->status));
281                 break;
282         }
283 }
284
285 /*
286  * Handles a FW notification for an event that is known to the driver.
287  *
288  * @mvm: the mvm component
289  * @te_data: the time event data
290  * @notif: the notification data corresponding the time event data.
291  */
292 static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
293                                     struct iwl_mvm_time_event_data *te_data,
294                                     struct iwl_time_event_notif *notif)
295 {
296         lockdep_assert_held(&mvm->time_event_lock);
297
298         IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
299                      le32_to_cpu(notif->unique_id),
300                      le32_to_cpu(notif->action));
301
302         iwl_mvm_te_check_trigger(mvm, notif, te_data);
303
304         /*
305          * The FW sends the start/end time event notifications even for events
306          * that it fails to schedule. This is indicated in the status field of
307          * the notification. This happens in cases that the scheduler cannot
308          * find a schedule that can handle the event (for example requesting a
309          * P2P Device discoveribility, while there are other higher priority
310          * events in the system).
311          */
312         if (!le32_to_cpu(notif->status)) {
313                 const char *msg;
314
315                 if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START))
316                         msg = "Time Event start notification failure";
317                 else
318                         msg = "Time Event end notification failure";
319
320                 IWL_DEBUG_TE(mvm, "%s\n", msg);
321
322                 if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) {
323                         iwl_mvm_te_clear_data(mvm, te_data);
324                         return;
325                 }
326         }
327
328         if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) {
329                 IWL_DEBUG_TE(mvm,
330                              "TE ended - current time %lu, estimated end %lu\n",
331                              jiffies, te_data->end_jiffies);
332
333                 switch (te_data->vif->type) {
334                 case NL80211_IFTYPE_P2P_DEVICE:
335                         ieee80211_remain_on_channel_expired(mvm->hw);
336                         set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status);
337                         iwl_mvm_roc_finished(mvm);
338                         break;
339                 case NL80211_IFTYPE_STATION:
340                         /*
341                          * By now, we should have finished association
342                          * and know the dtim period.
343                          */
344                         iwl_mvm_te_check_disconnect(mvm, te_data->vif,
345                                 "No beacon heard and the time event is over already...");
346                         break;
347                 default:
348                         break;
349                 }
350
351                 iwl_mvm_te_clear_data(mvm, te_data);
352         } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
353                 te_data->running = true;
354                 te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
355
356                 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
357                         set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
358                         ieee80211_ready_on_channel(mvm->hw);
359                 } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
360                         iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
361                 }
362         } else {
363                 IWL_WARN(mvm, "Got TE with unknown action\n");
364         }
365 }
366
367 /*
368  * Handle A Aux ROC time event
369  */
370 static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
371                                            struct iwl_time_event_notif *notif)
372 {
373         struct iwl_mvm_time_event_data *te_data, *tmp;
374         bool aux_roc_te = false;
375
376         list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) {
377                 if (le32_to_cpu(notif->unique_id) == te_data->uid) {
378                         aux_roc_te = true;
379                         break;
380                 }
381         }
382         if (!aux_roc_te) /* Not a Aux ROC time event */
383                 return -EINVAL;
384
385         iwl_mvm_te_check_trigger(mvm, notif, te_data);
386
387         IWL_DEBUG_TE(mvm,
388                      "Aux ROC time event notification  - UID = 0x%x action %d (error = %d)\n",
389                      le32_to_cpu(notif->unique_id),
390                      le32_to_cpu(notif->action), le32_to_cpu(notif->status));
391
392         if (!le32_to_cpu(notif->status) ||
393             le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
394                 /* End TE, notify mac80211 */
395                 ieee80211_remain_on_channel_expired(mvm->hw);
396                 iwl_mvm_roc_finished(mvm); /* flush aux queue */
397                 list_del(&te_data->list); /* remove from list */
398                 te_data->running = false;
399                 te_data->vif = NULL;
400                 te_data->uid = 0;
401                 te_data->id = TE_MAX;
402         } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
403                 set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
404                 te_data->running = true;
405                 ieee80211_ready_on_channel(mvm->hw); /* Start TE */
406         } else {
407                 IWL_DEBUG_TE(mvm,
408                              "ERROR: Unknown Aux ROC Time Event (action = %d)\n",
409                              le32_to_cpu(notif->action));
410                 return -EINVAL;
411         }
412
413         return 0;
414 }
415
416 /*
417  * The Rx handler for time event notifications
418  */
419 void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
420                                  struct iwl_rx_cmd_buffer *rxb)
421 {
422         struct iwl_rx_packet *pkt = rxb_addr(rxb);
423         struct iwl_time_event_notif *notif = (void *)pkt->data;
424         struct iwl_mvm_time_event_data *te_data, *tmp;
425
426         IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
427                      le32_to_cpu(notif->unique_id),
428                      le32_to_cpu(notif->action));
429
430         spin_lock_bh(&mvm->time_event_lock);
431         /* This time event is triggered for Aux ROC request */
432         if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif))
433                 goto unlock;
434
435         list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
436                 if (le32_to_cpu(notif->unique_id) == te_data->uid)
437                         iwl_mvm_te_handle_notif(mvm, te_data, notif);
438         }
439 unlock:
440         spin_unlock_bh(&mvm->time_event_lock);
441 }
442
443 static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait,
444                              struct iwl_rx_packet *pkt, void *data)
445 {
446         struct iwl_mvm *mvm =
447                 container_of(notif_wait, struct iwl_mvm, notif_wait);
448         struct iwl_mvm_time_event_data *te_data = data;
449         struct iwl_time_event_notif *resp;
450         int resp_len = iwl_rx_packet_payload_len(pkt);
451
452         if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION))
453                 return true;
454
455         if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
456                 IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n");
457                 return true;
458         }
459
460         resp = (void *)pkt->data;
461
462         /* te_data->uid is already set in the TIME_EVENT_CMD response */
463         if (le32_to_cpu(resp->unique_id) != te_data->uid)
464                 return false;
465
466         IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n",
467                      te_data->uid);
468         if (!resp->status)
469                 IWL_ERR(mvm,
470                         "TIME_EVENT_NOTIFICATION received but not executed\n");
471
472         return true;
473 }
474
475 static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
476                                         struct iwl_rx_packet *pkt, void *data)
477 {
478         struct iwl_mvm *mvm =
479                 container_of(notif_wait, struct iwl_mvm, notif_wait);
480         struct iwl_mvm_time_event_data *te_data = data;
481         struct iwl_time_event_resp *resp;
482         int resp_len = iwl_rx_packet_payload_len(pkt);
483
484         if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD))
485                 return true;
486
487         if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
488                 IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n");
489                 return true;
490         }
491
492         resp = (void *)pkt->data;
493
494         /* we should never get a response to another TIME_EVENT_CMD here */
495         if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id))
496                 return false;
497
498         te_data->uid = le32_to_cpu(resp->unique_id);
499         IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
500                      te_data->uid);
501         return true;
502 }
503
504 static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
505                                        struct ieee80211_vif *vif,
506                                        struct iwl_mvm_time_event_data *te_data,
507                                        struct iwl_time_event_cmd *te_cmd)
508 {
509         static const u16 time_event_response[] = { TIME_EVENT_CMD };
510         struct iwl_notification_wait wait_time_event;
511         int ret;
512
513         lockdep_assert_held(&mvm->mutex);
514
515         IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n",
516                      le32_to_cpu(te_cmd->duration));
517
518         spin_lock_bh(&mvm->time_event_lock);
519         if (WARN_ON(te_data->id != TE_MAX)) {
520                 spin_unlock_bh(&mvm->time_event_lock);
521                 return -EIO;
522         }
523         te_data->vif = vif;
524         te_data->duration = le32_to_cpu(te_cmd->duration);
525         te_data->id = le32_to_cpu(te_cmd->id);
526         list_add_tail(&te_data->list, &mvm->time_event_list);
527         spin_unlock_bh(&mvm->time_event_lock);
528
529         /*
530          * Use a notification wait, which really just processes the
531          * command response and doesn't wait for anything, in order
532          * to be able to process the response and get the UID inside
533          * the RX path. Using CMD_WANT_SKB doesn't work because it
534          * stores the buffer and then wakes up this thread, by which
535          * time another notification (that the time event started)
536          * might already be processed unsuccessfully.
537          */
538         iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
539                                    time_event_response,
540                                    ARRAY_SIZE(time_event_response),
541                                    iwl_mvm_time_event_response, te_data);
542
543         ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
544                                             sizeof(*te_cmd), te_cmd);
545         if (ret) {
546                 IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
547                 iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
548                 goto out_clear_te;
549         }
550
551         /* No need to wait for anything, so just pass 1 (0 isn't valid) */
552         ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
553         /* should never fail */
554         WARN_ON_ONCE(ret);
555
556         if (ret) {
557  out_clear_te:
558                 spin_lock_bh(&mvm->time_event_lock);
559                 iwl_mvm_te_clear_data(mvm, te_data);
560                 spin_unlock_bh(&mvm->time_event_lock);
561         }
562         return ret;
563 }
564
565 void iwl_mvm_protect_session(struct iwl_mvm *mvm,
566                              struct ieee80211_vif *vif,
567                              u32 duration, u32 min_duration,
568                              u32 max_delay, bool wait_for_notif)
569 {
570         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
571         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
572         const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION };
573         struct iwl_notification_wait wait_te_notif;
574         struct iwl_time_event_cmd time_cmd = {};
575
576         lockdep_assert_held(&mvm->mutex);
577
578         if (te_data->running &&
579             time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
580                 IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
581                              jiffies_to_msecs(te_data->end_jiffies - jiffies));
582                 return;
583         }
584
585         if (te_data->running) {
586                 IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
587                              te_data->uid,
588                              jiffies_to_msecs(te_data->end_jiffies - jiffies));
589                 /*
590                  * we don't have enough time
591                  * cancel the current TE and issue a new one
592                  * Of course it would be better to remove the old one only
593                  * when the new one is added, but we don't care if we are off
594                  * channel for a bit. All we need to do, is not to return
595                  * before we actually begin to be on the channel.
596                  */
597                 iwl_mvm_stop_session_protection(mvm, vif);
598         }
599
600         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
601         time_cmd.id_and_color =
602                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
603         time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
604
605         time_cmd.apply_time = cpu_to_le32(0);
606
607         time_cmd.max_frags = TE_V2_FRAG_NONE;
608         time_cmd.max_delay = cpu_to_le32(max_delay);
609         /* TODO: why do we need to interval = bi if it is not periodic? */
610         time_cmd.interval = cpu_to_le32(1);
611         time_cmd.duration = cpu_to_le32(duration);
612         time_cmd.repeat = 1;
613         time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
614                                       TE_V2_NOTIF_HOST_EVENT_END |
615                                       TE_V2_START_IMMEDIATELY);
616
617         if (!wait_for_notif) {
618                 iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
619                 return;
620         }
621
622         /*
623          * Create notification_wait for the TIME_EVENT_NOTIFICATION to use
624          * right after we send the time event
625          */
626         iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif,
627                                    te_notif_response,
628                                    ARRAY_SIZE(te_notif_response),
629                                    iwl_mvm_te_notif, te_data);
630
631         /* If TE was sent OK - wait for the notification that started */
632         if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) {
633                 IWL_ERR(mvm, "Failed to add TE to protect session\n");
634                 iwl_remove_notification(&mvm->notif_wait, &wait_te_notif);
635         } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif,
636                                          TU_TO_JIFFIES(max_delay))) {
637                 IWL_ERR(mvm, "Failed to protect session until TE\n");
638         }
639 }
640
641 static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
642                                         struct iwl_mvm_time_event_data *te_data,
643                                         u32 *uid)
644 {
645         u32 id;
646
647         /*
648          * It is possible that by the time we got to this point the time
649          * event was already removed.
650          */
651         spin_lock_bh(&mvm->time_event_lock);
652
653         /* Save time event uid before clearing its data */
654         *uid = te_data->uid;
655         id = te_data->id;
656
657         /*
658          * The clear_data function handles time events that were already removed
659          */
660         iwl_mvm_te_clear_data(mvm, te_data);
661         spin_unlock_bh(&mvm->time_event_lock);
662
663         /*
664          * It is possible that by the time we try to remove it, the time event
665          * has already ended and removed. In such a case there is no need to
666          * send a removal command.
667          */
668         if (id == TE_MAX) {
669                 IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);
670                 return false;
671         }
672
673         return true;
674 }
675
676 /*
677  * Explicit request to remove a aux roc time event. The removal of a time
678  * event needs to be synchronized with the flow of a time event's end
679  * notification, which also removes the time event from the op mode
680  * data structures.
681  */
682 static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
683                                       struct iwl_mvm_vif *mvmvif,
684                                       struct iwl_mvm_time_event_data *te_data)
685 {
686         struct iwl_hs20_roc_req aux_cmd = {};
687         u16 len = sizeof(aux_cmd) - iwl_mvm_chan_info_padding(mvm);
688
689         u32 uid;
690         int ret;
691
692         if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
693                 return;
694
695         aux_cmd.event_unique_id = cpu_to_le32(uid);
696         aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
697         aux_cmd.id_and_color =
698                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
699         IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
700                      le32_to_cpu(aux_cmd.event_unique_id));
701         ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
702                                    len, &aux_cmd);
703
704         if (WARN_ON(ret))
705                 return;
706 }
707
708 /*
709  * Explicit request to remove a time event. The removal of a time event needs to
710  * be synchronized with the flow of a time event's end notification, which also
711  * removes the time event from the op mode data structures.
712  */
713 void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
714                                struct iwl_mvm_vif *mvmvif,
715                                struct iwl_mvm_time_event_data *te_data)
716 {
717         struct iwl_time_event_cmd time_cmd = {};
718         u32 uid;
719         int ret;
720
721         if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
722                 return;
723
724         /* When we remove a TE, the UID is to be set in the id field */
725         time_cmd.id = cpu_to_le32(uid);
726         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
727         time_cmd.id_and_color =
728                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
729
730         IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
731         ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
732                                    sizeof(time_cmd), &time_cmd);
733         if (WARN_ON(ret))
734                 return;
735 }
736
737 /*
738  * When the firmware supports the session protection API,
739  * this is not needed since it'll automatically remove the
740  * session protection after association + beacon reception.
741  */
742 void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
743                                      struct ieee80211_vif *vif)
744 {
745         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
746         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
747         u32 id;
748
749         lockdep_assert_held(&mvm->mutex);
750
751         spin_lock_bh(&mvm->time_event_lock);
752         id = te_data->id;
753         spin_unlock_bh(&mvm->time_event_lock);
754
755         if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) {
756                 IWL_DEBUG_TE(mvm,
757                              "don't remove TE with id=%u (not session protection)\n",
758                              id);
759                 return;
760         }
761
762         iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
763 }
764
765 void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,
766                                       struct iwl_rx_cmd_buffer *rxb)
767 {
768         struct iwl_rx_packet *pkt = rxb_addr(rxb);
769         struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data;
770         struct ieee80211_vif *vif;
771
772         rcu_read_lock();
773         vif = iwl_mvm_rcu_dereference_vif_id(mvm, le32_to_cpu(notif->mac_id),
774                                              true);
775
776         if (!vif)
777                 goto out_unlock;
778
779         /* The vif is not a P2P_DEVICE, maintain its time_event_data */
780         if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
781                 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
782                 struct iwl_mvm_time_event_data *te_data =
783                         &mvmvif->time_event_data;
784
785                 if (!le32_to_cpu(notif->status)) {
786                         iwl_mvm_te_check_disconnect(mvm, vif,
787                                                     "Session protection failure");
788                         iwl_mvm_te_clear_data(mvm, te_data);
789                 }
790
791                 if (le32_to_cpu(notif->start)) {
792                         spin_lock_bh(&mvm->time_event_lock);
793                         te_data->running = le32_to_cpu(notif->start);
794                         te_data->end_jiffies =
795                                 TU_TO_EXP_TIME(te_data->duration);
796                         spin_unlock_bh(&mvm->time_event_lock);
797                 } else {
798                         /*
799                          * By now, we should have finished association
800                          * and know the dtim period.
801                          */
802                         iwl_mvm_te_check_disconnect(mvm, vif,
803                                                     "No beacon heard and the session protection is over already...");
804                         iwl_mvm_te_clear_data(mvm, te_data);
805                 }
806
807                 goto out_unlock;
808         }
809
810         if (!le32_to_cpu(notif->status) || !le32_to_cpu(notif->start)) {
811                 /* End TE, notify mac80211 */
812                 ieee80211_remain_on_channel_expired(mvm->hw);
813                 set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status);
814                 iwl_mvm_roc_finished(mvm);
815         } else if (le32_to_cpu(notif->start)) {
816                 set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
817                 ieee80211_ready_on_channel(mvm->hw); /* Start TE */
818         }
819
820  out_unlock:
821         rcu_read_unlock();
822 }
823
824 static int
825 iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm,
826                                          struct ieee80211_vif *vif,
827                                          int duration,
828                                          enum ieee80211_roc_type type)
829 {
830         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
831         struct iwl_mvm_session_prot_cmd cmd = {
832                 .id_and_color =
833                         cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
834                                                         mvmvif->color)),
835                 .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
836                 .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
837         };
838
839         lockdep_assert_held(&mvm->mutex);
840
841         switch (type) {
842         case IEEE80211_ROC_TYPE_NORMAL:
843                 cmd.conf_id =
844                         cpu_to_le32(SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV);
845                 break;
846         case IEEE80211_ROC_TYPE_MGMT_TX:
847                 cmd.conf_id =
848                         cpu_to_le32(SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION);
849                 break;
850         default:
851                 WARN_ONCE(1, "Got an invalid ROC type\n");
852                 return -EINVAL;
853         }
854
855         return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD,
856                                                     MAC_CONF_GROUP, 0),
857                                     0, sizeof(cmd), &cmd);
858 }
859
860 int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
861                           int duration, enum ieee80211_roc_type type)
862 {
863         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
864         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
865         struct iwl_time_event_cmd time_cmd = {};
866
867         lockdep_assert_held(&mvm->mutex);
868         if (te_data->running) {
869                 IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
870                 return -EBUSY;
871         }
872
873         if (fw_has_capa(&mvm->fw->ucode_capa,
874                         IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD))
875                 return iwl_mvm_start_p2p_roc_session_protection(mvm, vif,
876                                                                 duration,
877                                                                 type);
878
879         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
880         time_cmd.id_and_color =
881                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
882
883         switch (type) {
884         case IEEE80211_ROC_TYPE_NORMAL:
885                 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
886                 break;
887         case IEEE80211_ROC_TYPE_MGMT_TX:
888                 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
889                 break;
890         default:
891                 WARN_ONCE(1, "Got an invalid ROC type\n");
892                 return -EINVAL;
893         }
894
895         time_cmd.apply_time = cpu_to_le32(0);
896         time_cmd.interval = cpu_to_le32(1);
897
898         /*
899          * The P2P Device TEs can have lower priority than other events
900          * that are being scheduled by the driver/fw, and thus it might not be
901          * scheduled. To improve the chances of it being scheduled, allow them
902          * to be fragmented, and in addition allow them to be delayed.
903          */
904         time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
905         time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
906         time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
907         time_cmd.repeat = 1;
908         time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
909                                       TE_V2_NOTIF_HOST_EVENT_END |
910                                       TE_V2_START_IMMEDIATELY);
911
912         return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
913 }
914
915 static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm)
916 {
917         struct iwl_mvm_time_event_data *te_data;
918
919         lockdep_assert_held(&mvm->mutex);
920
921         spin_lock_bh(&mvm->time_event_lock);
922
923         /*
924          * Iterate over the list of time events and find the time event that is
925          * associated with a P2P_DEVICE interface.
926          * This assumes that a P2P_DEVICE interface can have only a single time
927          * event at any given time and this time event coresponds to a ROC
928          * request
929          */
930         list_for_each_entry(te_data, &mvm->time_event_list, list) {
931                 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)
932                         goto out;
933         }
934
935         /* There can only be at most one AUX ROC time event, we just use the
936          * list to simplify/unify code. Remove it if it exists.
937          */
938         te_data = list_first_entry_or_null(&mvm->aux_roc_te_list,
939                                            struct iwl_mvm_time_event_data,
940                                            list);
941 out:
942         spin_unlock_bh(&mvm->time_event_lock);
943         return te_data;
944 }
945
946 void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm)
947 {
948         struct iwl_mvm_time_event_data *te_data;
949         u32 uid;
950
951         te_data = iwl_mvm_get_roc_te(mvm);
952         if (te_data)
953                 __iwl_mvm_remove_time_event(mvm, te_data, &uid);
954 }
955
956 static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm,
957                                               struct iwl_mvm_vif *mvmvif)
958 {
959         struct iwl_mvm_session_prot_cmd cmd = {
960                 .id_and_color =
961                         cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
962                                                         mvmvif->color)),
963                 .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
964         };
965         int ret;
966
967         ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD,
968                                                    MAC_CONF_GROUP, 0),
969                                    0, sizeof(cmd), &cmd);
970         if (ret)
971                 IWL_ERR(mvm,
972                         "Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret);
973 }
974
975 void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
976 {
977         struct iwl_mvm_vif *mvmvif;
978         struct iwl_mvm_time_event_data *te_data;
979
980         if (fw_has_capa(&mvm->fw->ucode_capa,
981                         IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) {
982                 mvmvif = iwl_mvm_vif_from_mac80211(vif);
983
984                 iwl_mvm_cancel_session_protection(mvm, mvmvif);
985
986                 if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
987                         set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status);
988
989                 iwl_mvm_roc_finished(mvm);
990
991                 return;
992         }
993
994         te_data = iwl_mvm_get_roc_te(mvm);
995         if (!te_data) {
996                 IWL_WARN(mvm, "No remain on channel event\n");
997                 return;
998         }
999
1000         mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
1001
1002         if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
1003                 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
1004                 set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status);
1005         } else {
1006                 iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
1007         }
1008
1009         iwl_mvm_roc_finished(mvm);
1010 }
1011
1012 int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
1013                                 struct ieee80211_vif *vif,
1014                                 u32 duration, u32 apply_time)
1015 {
1016         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1017         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
1018         struct iwl_time_event_cmd time_cmd = {};
1019
1020         lockdep_assert_held(&mvm->mutex);
1021
1022         if (te_data->running) {
1023                 u32 id;
1024
1025                 spin_lock_bh(&mvm->time_event_lock);
1026                 id = te_data->id;
1027                 spin_unlock_bh(&mvm->time_event_lock);
1028
1029                 if (id == TE_CHANNEL_SWITCH_PERIOD) {
1030                         IWL_DEBUG_TE(mvm, "CS period is already scheduled\n");
1031                         return -EBUSY;
1032                 }
1033
1034                 /*
1035                  * Remove the session protection time event to allow the
1036                  * channel switch. If we got here, we just heard a beacon so
1037                  * the session protection is not needed anymore anyway.
1038                  */
1039                 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
1040         }
1041
1042         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
1043         time_cmd.id_and_color =
1044                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
1045         time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD);
1046         time_cmd.apply_time = cpu_to_le32(apply_time);
1047         time_cmd.max_frags = TE_V2_FRAG_NONE;
1048         time_cmd.duration = cpu_to_le32(duration);
1049         time_cmd.repeat = 1;
1050         time_cmd.interval = cpu_to_le32(1);
1051         time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
1052                                       TE_V2_ABSENCE);
1053         if (!apply_time)
1054                 time_cmd.policy |= cpu_to_le16(TE_V2_START_IMMEDIATELY);
1055
1056         return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
1057 }
1058
1059 static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait,
1060                                        struct iwl_rx_packet *pkt, void *data)
1061 {
1062         struct iwl_mvm *mvm =
1063                 container_of(notif_wait, struct iwl_mvm, notif_wait);
1064         struct iwl_mvm_session_prot_notif *resp;
1065         int resp_len = iwl_rx_packet_payload_len(pkt);
1066
1067         if (WARN_ON(pkt->hdr.cmd != SESSION_PROTECTION_NOTIF ||
1068                     pkt->hdr.group_id != MAC_CONF_GROUP))
1069                 return true;
1070
1071         if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
1072                 IWL_ERR(mvm, "Invalid SESSION_PROTECTION_NOTIF response\n");
1073                 return true;
1074         }
1075
1076         resp = (void *)pkt->data;
1077
1078         if (!resp->status)
1079                 IWL_ERR(mvm,
1080                         "TIME_EVENT_NOTIFICATION received but not executed\n");
1081
1082         return true;
1083 }
1084
1085 void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm,
1086                                          struct ieee80211_vif *vif,
1087                                          u32 duration, u32 min_duration,
1088                                          bool wait_for_notif)
1089 {
1090         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1091         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
1092         const u16 notif[] = { iwl_cmd_id(SESSION_PROTECTION_NOTIF,
1093                                          MAC_CONF_GROUP, 0) };
1094         struct iwl_notification_wait wait_notif;
1095         struct iwl_mvm_session_prot_cmd cmd = {
1096                 .id_and_color =
1097                         cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
1098                                                         mvmvif->color)),
1099                 .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
1100                 .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
1101                 .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
1102         };
1103
1104         lockdep_assert_held(&mvm->mutex);
1105
1106         spin_lock_bh(&mvm->time_event_lock);
1107         if (te_data->running &&
1108             time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
1109                 IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
1110                              jiffies_to_msecs(te_data->end_jiffies - jiffies));
1111                 spin_unlock_bh(&mvm->time_event_lock);
1112
1113                 return;
1114         }
1115
1116         iwl_mvm_te_clear_data(mvm, te_data);
1117         te_data->duration = le32_to_cpu(cmd.duration_tu);
1118         spin_unlock_bh(&mvm->time_event_lock);
1119
1120         IWL_DEBUG_TE(mvm, "Add new session protection, duration %d TU\n",
1121                      le32_to_cpu(cmd.duration_tu));
1122
1123         if (!wait_for_notif) {
1124                 if (iwl_mvm_send_cmd_pdu(mvm,
1125                                          iwl_cmd_id(SESSION_PROTECTION_CMD,
1126                                                     MAC_CONF_GROUP, 0),
1127                                          0, sizeof(cmd), &cmd)) {
1128                         IWL_ERR(mvm,
1129                                 "Couldn't send the SESSION_PROTECTION_CMD\n");
1130                         spin_lock_bh(&mvm->time_event_lock);
1131                         iwl_mvm_te_clear_data(mvm, te_data);
1132                         spin_unlock_bh(&mvm->time_event_lock);
1133                 }
1134
1135                 return;
1136         }
1137
1138         iwl_init_notification_wait(&mvm->notif_wait, &wait_notif,
1139                                    notif, ARRAY_SIZE(notif),
1140                                    iwl_mvm_session_prot_notif, NULL);
1141
1142         if (iwl_mvm_send_cmd_pdu(mvm,
1143                                  iwl_cmd_id(SESSION_PROTECTION_CMD,
1144                                             MAC_CONF_GROUP, 0),
1145                                  0, sizeof(cmd), &cmd)) {
1146                 IWL_ERR(mvm,
1147                         "Couldn't send the SESSION_PROTECTION_CMD\n");
1148                 iwl_remove_notification(&mvm->notif_wait, &wait_notif);
1149         } else if (iwl_wait_notification(&mvm->notif_wait, &wait_notif,
1150                                          TU_TO_JIFFIES(100))) {
1151                 IWL_ERR(mvm,
1152                         "Failed to protect session until session protection\n");
1153         }
1154 }