Linux-libre 3.17.4-gnu
[librecmc/linux-libre.git] / drivers / net / wireless / 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  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of version 2 of the GNU General Public License as
12  * published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22  * USA
23  *
24  * The full GNU General Public License is included in this distribution
25  * in the file called COPYING.
26  *
27  * Contact Information:
28  *  Intel Linux Wireless <ilw@linux.intel.com>
29  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30  *
31  * BSD LICENSE
32  *
33  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  *
40  *  * Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  *  * Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in
44  *    the documentation and/or other materials provided with the
45  *    distribution.
46  *  * Neither the name Intel Corporation nor the names of its
47  *    contributors may be used to endorse or promote products derived
48  *    from this software without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61  *
62  *****************************************************************************/
63
64 #include <linux/jiffies.h>
65 #include <net/mac80211.h>
66
67 #include "iwl-notif-wait.h"
68 #include "iwl-trans.h"
69 #include "fw-api.h"
70 #include "time-event.h"
71 #include "mvm.h"
72 #include "iwl-io.h"
73 #include "iwl-prph.h"
74
75 /*
76  * For the high priority TE use a time event type that has similar priority to
77  * the FW's action scan priority.
78  */
79 #define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE
80 #define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC
81
82 void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
83                            struct iwl_mvm_time_event_data *te_data)
84 {
85         lockdep_assert_held(&mvm->time_event_lock);
86
87         if (te_data->id == TE_MAX)
88                 return;
89
90         list_del(&te_data->list);
91         te_data->running = false;
92         te_data->uid = 0;
93         te_data->id = TE_MAX;
94         te_data->vif = NULL;
95 }
96
97 void iwl_mvm_roc_done_wk(struct work_struct *wk)
98 {
99         struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
100         u32 queues = 0;
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         if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
110                 queues |= BIT(IWL_MVM_OFFCHANNEL_QUEUE);
111         if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
112                 queues |= BIT(mvm->aux_queue);
113
114         iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
115
116         synchronize_net();
117
118         /*
119          * Flush the offchannel queue -- this is called when the time
120          * event finishes or is cancelled, so that frames queued for it
121          * won't get stuck on the queue and be transmitted in the next
122          * time event.
123          * We have to send the command asynchronously since this cannot
124          * be under the mutex for locking reasons, but that's not an
125          * issue as it will have to complete before the next command is
126          * executed, and a new time event means a new command.
127          */
128         iwl_mvm_flush_tx_path(mvm, queues, false);
129 }
130
131 static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
132 {
133         /*
134          * Of course, our status bit is just as racy as mac80211, so in
135          * addition, fire off the work struct which will drop all frames
136          * from the hardware queues that made it through the race. First
137          * it will of course synchronize the TX path to make sure that
138          * any *new* TX will be rejected.
139          */
140         schedule_work(&mvm->roc_done_wk);
141 }
142
143 static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
144 {
145         struct ieee80211_vif *csa_vif;
146
147         rcu_read_lock();
148
149         csa_vif = rcu_dereference(mvm->csa_vif);
150         if (!csa_vif || !csa_vif->csa_active)
151                 goto out_unlock;
152
153         IWL_DEBUG_TE(mvm, "CSA NOA started\n");
154
155         /*
156          * CSA NoA is started but we still have beacons to
157          * transmit on the current channel.
158          * So we just do nothing here and the switch
159          * will be performed on the last TBTT.
160          */
161         if (!ieee80211_csa_is_complete(csa_vif)) {
162                 IWL_WARN(mvm, "CSA NOA started too early\n");
163                 goto out_unlock;
164         }
165
166         ieee80211_csa_finish(csa_vif);
167
168         rcu_read_unlock();
169
170         RCU_INIT_POINTER(mvm->csa_vif, NULL);
171
172         return;
173
174 out_unlock:
175         rcu_read_unlock();
176 }
177
178 static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
179                                         struct ieee80211_vif *vif,
180                                         const char *errmsg)
181 {
182         if (vif->type != NL80211_IFTYPE_STATION)
183                 return false;
184         if (vif->bss_conf.assoc && vif->bss_conf.dtim_period)
185                 return false;
186         if (errmsg)
187                 IWL_ERR(mvm, "%s\n", errmsg);
188         ieee80211_connection_loss(vif);
189         return true;
190 }
191
192 /*
193  * Handles a FW notification for an event that is known to the driver.
194  *
195  * @mvm: the mvm component
196  * @te_data: the time event data
197  * @notif: the notification data corresponding the time event data.
198  */
199 static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
200                                     struct iwl_mvm_time_event_data *te_data,
201                                     struct iwl_time_event_notif *notif)
202 {
203         lockdep_assert_held(&mvm->time_event_lock);
204
205         IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
206                      le32_to_cpu(notif->unique_id),
207                      le32_to_cpu(notif->action));
208
209         /*
210          * The FW sends the start/end time event notifications even for events
211          * that it fails to schedule. This is indicated in the status field of
212          * the notification. This happens in cases that the scheduler cannot
213          * find a schedule that can handle the event (for example requesting a
214          * P2P Device discoveribility, while there are other higher priority
215          * events in the system).
216          */
217         if (!le32_to_cpu(notif->status)) {
218                 bool start = le32_to_cpu(notif->action) &
219                                 TE_V2_NOTIF_HOST_EVENT_START;
220                 IWL_WARN(mvm, "Time Event %s notification failure\n",
221                          start ? "start" : "end");
222                 if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) {
223                         iwl_mvm_te_clear_data(mvm, te_data);
224                         return;
225                 }
226         }
227
228         if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) {
229                 IWL_DEBUG_TE(mvm,
230                              "TE ended - current time %lu, estimated end %lu\n",
231                              jiffies, te_data->end_jiffies);
232
233                 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
234                         ieee80211_remain_on_channel_expired(mvm->hw);
235                         iwl_mvm_roc_finished(mvm);
236                 }
237
238                 /*
239                  * By now, we should have finished association
240                  * and know the dtim period.
241                  */
242                 iwl_mvm_te_check_disconnect(mvm, te_data->vif,
243                         "No association and the time event is over already...");
244                 iwl_mvm_te_clear_data(mvm, te_data);
245         } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
246                 te_data->running = true;
247                 te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
248
249                 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
250                         set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
251                         iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
252                         ieee80211_ready_on_channel(mvm->hw);
253                 } else if (te_data->vif->type == NL80211_IFTYPE_AP) {
254                         if (le32_to_cpu(notif->status))
255                                 iwl_mvm_csa_noa_start(mvm);
256                         else
257                                 IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n");
258
259                         /* we don't need it anymore */
260                         iwl_mvm_te_clear_data(mvm, te_data);
261                 }
262         } else {
263                 IWL_WARN(mvm, "Got TE with unknown action\n");
264         }
265 }
266
267 /*
268  * Handle A Aux ROC time event
269  */
270 static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
271                                            struct iwl_time_event_notif *notif)
272 {
273         struct iwl_mvm_time_event_data *te_data, *tmp;
274         bool aux_roc_te = false;
275
276         list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) {
277                 if (le32_to_cpu(notif->unique_id) == te_data->uid) {
278                         aux_roc_te = true;
279                         break;
280                 }
281         }
282         if (!aux_roc_te) /* Not a Aux ROC time event */
283                 return -EINVAL;
284
285         if (!le32_to_cpu(notif->status)) {
286                 IWL_DEBUG_TE(mvm,
287                              "ERROR: Aux ROC Time Event %s notification failure\n",
288                              (le32_to_cpu(notif->action) &
289                               TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end");
290                 return -EINVAL;
291         }
292
293         IWL_DEBUG_TE(mvm,
294                      "Aux ROC time event notification  - UID = 0x%x action %d\n",
295                      le32_to_cpu(notif->unique_id),
296                      le32_to_cpu(notif->action));
297
298         if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
299                 /* End TE, notify mac80211 */
300                 ieee80211_remain_on_channel_expired(mvm->hw);
301                 iwl_mvm_roc_finished(mvm); /* flush aux queue */
302                 list_del(&te_data->list); /* remove from list */
303                 te_data->running = false;
304                 te_data->vif = NULL;
305                 te_data->uid = 0;
306         } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
307                 set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
308                 set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
309                 te_data->running = true;
310                 ieee80211_ready_on_channel(mvm->hw); /* Start TE */
311         } else {
312                 IWL_DEBUG_TE(mvm,
313                              "ERROR: Unknown Aux ROC Time Event (action = %d)\n",
314                              le32_to_cpu(notif->action));
315                 return -EINVAL;
316         }
317
318         return 0;
319 }
320
321 /*
322  * The Rx handler for time event notifications
323  */
324 int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
325                                 struct iwl_rx_cmd_buffer *rxb,
326                                 struct iwl_device_cmd *cmd)
327 {
328         struct iwl_rx_packet *pkt = rxb_addr(rxb);
329         struct iwl_time_event_notif *notif = (void *)pkt->data;
330         struct iwl_mvm_time_event_data *te_data, *tmp;
331
332         IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
333                      le32_to_cpu(notif->unique_id),
334                      le32_to_cpu(notif->action));
335
336         spin_lock_bh(&mvm->time_event_lock);
337         /* This time event is triggered for Aux ROC request */
338         if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif))
339                 goto unlock;
340
341         list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
342                 if (le32_to_cpu(notif->unique_id) == te_data->uid)
343                         iwl_mvm_te_handle_notif(mvm, te_data, notif);
344         }
345 unlock:
346         spin_unlock_bh(&mvm->time_event_lock);
347
348         return 0;
349 }
350
351 static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
352                                         struct iwl_rx_packet *pkt, void *data)
353 {
354         struct iwl_mvm *mvm =
355                 container_of(notif_wait, struct iwl_mvm, notif_wait);
356         struct iwl_mvm_time_event_data *te_data = data;
357         struct iwl_time_event_resp *resp;
358         int resp_len = iwl_rx_packet_payload_len(pkt);
359
360         if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD))
361                 return true;
362
363         if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
364                 IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n");
365                 return true;
366         }
367
368         resp = (void *)pkt->data;
369
370         /* we should never get a response to another TIME_EVENT_CMD here */
371         if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id))
372                 return false;
373
374         te_data->uid = le32_to_cpu(resp->unique_id);
375         IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
376                      te_data->uid);
377         return true;
378 }
379
380 static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
381                                        struct ieee80211_vif *vif,
382                                        struct iwl_mvm_time_event_data *te_data,
383                                        struct iwl_time_event_cmd *te_cmd)
384 {
385         static const u8 time_event_response[] = { TIME_EVENT_CMD };
386         struct iwl_notification_wait wait_time_event;
387         int ret;
388
389         lockdep_assert_held(&mvm->mutex);
390
391         IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n",
392                      le32_to_cpu(te_cmd->duration));
393
394         spin_lock_bh(&mvm->time_event_lock);
395         if (WARN_ON(te_data->id != TE_MAX)) {
396                 spin_unlock_bh(&mvm->time_event_lock);
397                 return -EIO;
398         }
399         te_data->vif = vif;
400         te_data->duration = le32_to_cpu(te_cmd->duration);
401         te_data->id = le32_to_cpu(te_cmd->id);
402         list_add_tail(&te_data->list, &mvm->time_event_list);
403         spin_unlock_bh(&mvm->time_event_lock);
404
405         /*
406          * Use a notification wait, which really just processes the
407          * command response and doesn't wait for anything, in order
408          * to be able to process the response and get the UID inside
409          * the RX path. Using CMD_WANT_SKB doesn't work because it
410          * stores the buffer and then wakes up this thread, by which
411          * time another notification (that the time event started)
412          * might already be processed unsuccessfully.
413          */
414         iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
415                                    time_event_response,
416                                    ARRAY_SIZE(time_event_response),
417                                    iwl_mvm_time_event_response, te_data);
418
419         ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
420                                             sizeof(*te_cmd), te_cmd);
421         if (ret) {
422                 IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
423                 iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
424                 goto out_clear_te;
425         }
426
427         /* No need to wait for anything, so just pass 1 (0 isn't valid) */
428         ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
429         /* should never fail */
430         WARN_ON_ONCE(ret);
431
432         if (ret) {
433  out_clear_te:
434                 spin_lock_bh(&mvm->time_event_lock);
435                 iwl_mvm_te_clear_data(mvm, te_data);
436                 spin_unlock_bh(&mvm->time_event_lock);
437         }
438         return ret;
439 }
440
441 void iwl_mvm_protect_session(struct iwl_mvm *mvm,
442                              struct ieee80211_vif *vif,
443                              u32 duration, u32 min_duration,
444                              u32 max_delay)
445 {
446         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
447         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
448         struct iwl_time_event_cmd time_cmd = {};
449
450         lockdep_assert_held(&mvm->mutex);
451
452         if (te_data->running &&
453             time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
454                 IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
455                              jiffies_to_msecs(te_data->end_jiffies - jiffies));
456                 return;
457         }
458
459         if (te_data->running) {
460                 IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
461                              te_data->uid,
462                              jiffies_to_msecs(te_data->end_jiffies - jiffies));
463                 /*
464                  * we don't have enough time
465                  * cancel the current TE and issue a new one
466                  * Of course it would be better to remove the old one only
467                  * when the new one is added, but we don't care if we are off
468                  * channel for a bit. All we need to do, is not to return
469                  * before we actually begin to be on the channel.
470                  */
471                 iwl_mvm_stop_session_protection(mvm, vif);
472         }
473
474         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
475         time_cmd.id_and_color =
476                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
477         time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
478
479         time_cmd.apply_time =
480                 cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG));
481
482         time_cmd.max_frags = TE_V2_FRAG_NONE;
483         time_cmd.max_delay = cpu_to_le32(max_delay);
484         /* TODO: why do we need to interval = bi if it is not periodic? */
485         time_cmd.interval = cpu_to_le32(1);
486         time_cmd.duration = cpu_to_le32(duration);
487         time_cmd.repeat = 1;
488         time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
489                                       TE_V2_NOTIF_HOST_EVENT_END |
490                                       T2_V2_START_IMMEDIATELY);
491
492         iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
493 }
494
495 /*
496  * Explicit request to remove a time event. The removal of a time event needs to
497  * be synchronized with the flow of a time event's end notification, which also
498  * removes the time event from the op mode data structures.
499  */
500 void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
501                                struct iwl_mvm_vif *mvmvif,
502                                struct iwl_mvm_time_event_data *te_data)
503 {
504         struct iwl_time_event_cmd time_cmd = {};
505         u32 id, uid;
506         int ret;
507
508         /*
509          * It is possible that by the time we got to this point the time
510          * event was already removed.
511          */
512         spin_lock_bh(&mvm->time_event_lock);
513
514         /* Save time event uid before clearing its data */
515         uid = te_data->uid;
516         id = te_data->id;
517
518         /*
519          * The clear_data function handles time events that were already removed
520          */
521         iwl_mvm_te_clear_data(mvm, te_data);
522         spin_unlock_bh(&mvm->time_event_lock);
523
524         /*
525          * It is possible that by the time we try to remove it, the time event
526          * has already ended and removed. In such a case there is no need to
527          * send a removal command.
528          */
529         if (id == TE_MAX) {
530                 IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", uid);
531                 return;
532         }
533
534         /* When we remove a TE, the UID is to be set in the id field */
535         time_cmd.id = cpu_to_le32(uid);
536         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
537         time_cmd.id_and_color =
538                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
539
540         IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
541         ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
542                                    sizeof(time_cmd), &time_cmd);
543         if (WARN_ON(ret))
544                 return;
545 }
546
547 void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
548                                      struct ieee80211_vif *vif)
549 {
550         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
551         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
552
553         lockdep_assert_held(&mvm->mutex);
554         iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
555 }
556
557 int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
558                           int duration, enum ieee80211_roc_type type)
559 {
560         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
561         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
562         struct iwl_time_event_cmd time_cmd = {};
563
564         lockdep_assert_held(&mvm->mutex);
565         if (te_data->running) {
566                 IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
567                 return -EBUSY;
568         }
569
570         /*
571          * Flush the done work, just in case it's still pending, so that
572          * the work it does can complete and we can accept new frames.
573          */
574         flush_work(&mvm->roc_done_wk);
575
576         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
577         time_cmd.id_and_color =
578                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
579
580         switch (type) {
581         case IEEE80211_ROC_TYPE_NORMAL:
582                 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
583                 break;
584         case IEEE80211_ROC_TYPE_MGMT_TX:
585                 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
586                 break;
587         default:
588                 WARN_ONCE(1, "Got an invalid ROC type\n");
589                 return -EINVAL;
590         }
591
592         time_cmd.apply_time = cpu_to_le32(0);
593         time_cmd.interval = cpu_to_le32(1);
594
595         /*
596          * The P2P Device TEs can have lower priority than other events
597          * that are being scheduled by the driver/fw, and thus it might not be
598          * scheduled. To improve the chances of it being scheduled, allow them
599          * to be fragmented, and in addition allow them to be delayed.
600          */
601         time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
602         time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
603         time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
604         time_cmd.repeat = 1;
605         time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
606                                       TE_V2_NOTIF_HOST_EVENT_END |
607                                       T2_V2_START_IMMEDIATELY);
608
609         return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
610 }
611
612 void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
613 {
614         struct iwl_mvm_vif *mvmvif;
615         struct iwl_mvm_time_event_data *te_data;
616
617         lockdep_assert_held(&mvm->mutex);
618
619         /*
620          * Iterate over the list of time events and find the time event that is
621          * associated with a P2P_DEVICE interface.
622          * This assumes that a P2P_DEVICE interface can have only a single time
623          * event at any given time and this time event coresponds to a ROC
624          * request
625          */
626         mvmvif = NULL;
627         spin_lock_bh(&mvm->time_event_lock);
628         list_for_each_entry(te_data, &mvm->time_event_list, list) {
629                 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
630                         mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
631                         break;
632                 }
633         }
634         spin_unlock_bh(&mvm->time_event_lock);
635
636         if (!mvmvif) {
637                 IWL_WARN(mvm, "P2P_DEVICE no remain on channel event\n");
638                 return;
639         }
640
641         iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
642
643         iwl_mvm_roc_finished(mvm);
644 }
645
646 int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm,
647                               struct ieee80211_vif *vif,
648                               u32 duration, u32 apply_time)
649 {
650         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
651         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
652         struct iwl_time_event_cmd time_cmd = {};
653
654         lockdep_assert_held(&mvm->mutex);
655
656         if (te_data->running) {
657                 IWL_DEBUG_TE(mvm, "CS NOA is already scheduled\n");
658                 return -EBUSY;
659         }
660
661         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
662         time_cmd.id_and_color =
663                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
664         time_cmd.id = cpu_to_le32(TE_P2P_GO_CSA_NOA);
665         time_cmd.apply_time = cpu_to_le32(apply_time);
666         time_cmd.max_frags = TE_V2_FRAG_NONE;
667         time_cmd.duration = cpu_to_le32(duration);
668         time_cmd.repeat = 1;
669         time_cmd.interval = cpu_to_le32(1);
670         time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
671                                       TE_V2_ABSENCE);
672
673         return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
674 }