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