The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/dev/iwlwifi/mvm/time-event.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 3b8b4f1531184a26c22f0bc34a0a2eaa


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.