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