root/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. iwl_mvm_te_clear_data
  2. iwl_mvm_roc_done_wk
  3. iwl_mvm_roc_finished
  4. iwl_mvm_csa_noa_start
  5. iwl_mvm_te_check_disconnect
  6. iwl_mvm_te_handle_notify_csa
  7. iwl_mvm_te_check_trigger
  8. iwl_mvm_te_handle_notif
  9. iwl_mvm_aux_roc_te_handle_notif
  10. iwl_mvm_rx_time_event_notif
  11. iwl_mvm_te_notif
  12. iwl_mvm_time_event_response
  13. iwl_mvm_time_event_send_add
  14. iwl_mvm_protect_session
  15. __iwl_mvm_remove_time_event
  16. iwl_mvm_remove_aux_roc_te
  17. iwl_mvm_remove_time_event
  18. iwl_mvm_stop_session_protection
  19. iwl_mvm_start_p2p_roc
  20. iwl_mvm_get_roc_te
  21. iwl_mvm_cleanup_roc_te
  22. iwl_mvm_stop_roc
  23. iwl_mvm_schedule_csa_period

   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 void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
 738                                      struct ieee80211_vif *vif)
 739 {
 740         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 741         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
 742         u32 id;
 743 
 744         lockdep_assert_held(&mvm->mutex);
 745 
 746         spin_lock_bh(&mvm->time_event_lock);
 747         id = te_data->id;
 748         spin_unlock_bh(&mvm->time_event_lock);
 749 
 750         if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) {
 751                 IWL_DEBUG_TE(mvm,
 752                              "don't remove TE with id=%u (not session protection)\n",
 753                              id);
 754                 return;
 755         }
 756 
 757         iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
 758 }
 759 
 760 int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 761                           int duration, enum ieee80211_roc_type type)
 762 {
 763         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 764         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
 765         struct iwl_time_event_cmd time_cmd = {};
 766 
 767         lockdep_assert_held(&mvm->mutex);
 768         if (te_data->running) {
 769                 IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
 770                 return -EBUSY;
 771         }
 772 
 773         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
 774         time_cmd.id_and_color =
 775                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
 776 
 777         switch (type) {
 778         case IEEE80211_ROC_TYPE_NORMAL:
 779                 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
 780                 break;
 781         case IEEE80211_ROC_TYPE_MGMT_TX:
 782                 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
 783                 break;
 784         default:
 785                 WARN_ONCE(1, "Got an invalid ROC type\n");
 786                 return -EINVAL;
 787         }
 788 
 789         time_cmd.apply_time = cpu_to_le32(0);
 790         time_cmd.interval = cpu_to_le32(1);
 791 
 792         /*
 793          * The P2P Device TEs can have lower priority than other events
 794          * that are being scheduled by the driver/fw, and thus it might not be
 795          * scheduled. To improve the chances of it being scheduled, allow them
 796          * to be fragmented, and in addition allow them to be delayed.
 797          */
 798         time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
 799         time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
 800         time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
 801         time_cmd.repeat = 1;
 802         time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
 803                                       TE_V2_NOTIF_HOST_EVENT_END |
 804                                       TE_V2_START_IMMEDIATELY);
 805 
 806         return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
 807 }
 808 
 809 static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm)
 810 {
 811         struct iwl_mvm_time_event_data *te_data;
 812 
 813         lockdep_assert_held(&mvm->mutex);
 814 
 815         spin_lock_bh(&mvm->time_event_lock);
 816 
 817         /*
 818          * Iterate over the list of time events and find the time event that is
 819          * associated with a P2P_DEVICE interface.
 820          * This assumes that a P2P_DEVICE interface can have only a single time
 821          * event at any given time and this time event coresponds to a ROC
 822          * request
 823          */
 824         list_for_each_entry(te_data, &mvm->time_event_list, list) {
 825                 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)
 826                         goto out;
 827         }
 828 
 829         /* There can only be at most one AUX ROC time event, we just use the
 830          * list to simplify/unify code. Remove it if it exists.
 831          */
 832         te_data = list_first_entry_or_null(&mvm->aux_roc_te_list,
 833                                            struct iwl_mvm_time_event_data,
 834                                            list);
 835 out:
 836         spin_unlock_bh(&mvm->time_event_lock);
 837         return te_data;
 838 }
 839 
 840 void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm)
 841 {
 842         struct iwl_mvm_time_event_data *te_data;
 843         u32 uid;
 844 
 845         te_data = iwl_mvm_get_roc_te(mvm);
 846         if (te_data)
 847                 __iwl_mvm_remove_time_event(mvm, te_data, &uid);
 848 }
 849 
 850 void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
 851 {
 852         struct iwl_mvm_vif *mvmvif;
 853         struct iwl_mvm_time_event_data *te_data;
 854 
 855         te_data = iwl_mvm_get_roc_te(mvm);
 856         if (!te_data) {
 857                 IWL_WARN(mvm, "No remain on channel event\n");
 858                 return;
 859         }
 860 
 861         mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
 862 
 863         if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
 864                 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
 865                 set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status);
 866         } else {
 867                 iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
 868         }
 869 
 870         iwl_mvm_roc_finished(mvm);
 871 }
 872 
 873 int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
 874                                 struct ieee80211_vif *vif,
 875                                 u32 duration, u32 apply_time)
 876 {
 877         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 878         struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
 879         struct iwl_time_event_cmd time_cmd = {};
 880 
 881         lockdep_assert_held(&mvm->mutex);
 882 
 883         if (te_data->running) {
 884                 u32 id;
 885 
 886                 spin_lock_bh(&mvm->time_event_lock);
 887                 id = te_data->id;
 888                 spin_unlock_bh(&mvm->time_event_lock);
 889 
 890                 if (id == TE_CHANNEL_SWITCH_PERIOD) {
 891                         IWL_DEBUG_TE(mvm, "CS period is already scheduled\n");
 892                         return -EBUSY;
 893                 }
 894 
 895                 /*
 896                  * Remove the session protection time event to allow the
 897                  * channel switch. If we got here, we just heard a beacon so
 898                  * the session protection is not needed anymore anyway.
 899                  */
 900                 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
 901         }
 902 
 903         time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
 904         time_cmd.id_and_color =
 905                 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
 906         time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD);
 907         time_cmd.apply_time = cpu_to_le32(apply_time);
 908         time_cmd.max_frags = TE_V2_FRAG_NONE;
 909         time_cmd.duration = cpu_to_le32(duration);
 910         time_cmd.repeat = 1;
 911         time_cmd.interval = cpu_to_le32(1);
 912         time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
 913                                       TE_V2_ABSENCE);
 914         if (!apply_time)
 915                 time_cmd.policy |= cpu_to_le16(TE_V2_START_IMMEDIATELY);
 916 
 917         return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
 918 }

/* [<][>][^][v][top][bottom][index][help] */