Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/net/mac80211/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 119 kB image not shown  

Quelle  util.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2002-2005, Instant802 Networks, Inc.
 * Copyright 2005-2006, Devicescape Software, Inc.
 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
 * Copyright 2013-2014  Intel Mobile Communications GmbH
 * Copyright (C) 2015-2017 Intel Deutschland GmbH
 * Copyright (C) 2018-2025 Intel Corporation
 *
 * utilities for mac80211
 */


#include <net/mac80211.h>
#include <linux/netdevice.h>
#include <linux/export.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/bitmap.h>
#include <linux/crc32.h>
#include <net/net_namespace.h>
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include <kunit/visibility.h>

#include "ieee80211_i.h"
#include "driver-ops.h"
#include "rate.h"
#include "mesh.h"
#include "wme.h"
#include "led.h"
#include "wep.h"

/* privid for wiphys to determine whether they belong to us or not */
const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid;

struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
{
 struct ieee80211_local *local;

 local = wiphy_priv(wiphy);
 return &local->hw;
}
EXPORT_SYMBOL(wiphy_to_ieee80211_hw);

const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited = {
 .mode = IEEE80211_CONN_MODE_EHT,
 .bw_limit = IEEE80211_CONN_BW_LIMIT_320,
};

u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
   enum nl80211_iftype type)
{
 __le16 fc = hdr->frame_control;

 if (ieee80211_is_data(fc)) {
  if (len < 24) /* drop incorrect hdr len (data) */
   return NULL;

  if (ieee80211_has_a4(fc))
   return NULL;
  if (ieee80211_has_tods(fc))
   return hdr->addr1;
  if (ieee80211_has_fromds(fc))
   return hdr->addr2;

  return hdr->addr3;
 }

 if (ieee80211_is_s1g_beacon(fc)) {
  struct ieee80211_ext *ext = (void *) hdr;

  return ext->u.s1g_beacon.sa;
 }

 if (ieee80211_is_mgmt(fc)) {
  if (len < 24) /* drop incorrect hdr len (mgmt) */
   return NULL;
  return hdr->addr3;
 }

 if (ieee80211_is_ctl(fc)) {
  if (ieee80211_is_pspoll(fc))
   return hdr->addr1;

  if (ieee80211_is_back_req(fc)) {
   switch (type) {
   case NL80211_IFTYPE_STATION:
    return hdr->addr2;
   case NL80211_IFTYPE_AP:
   case NL80211_IFTYPE_AP_VLAN:
    return hdr->addr1;
   default:
    break/* fall through to the return */
   }
  }
 }

 return NULL;
}
EXPORT_SYMBOL(ieee80211_get_bssid);

void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
{
 struct sk_buff *skb;
 struct ieee80211_hdr *hdr;

 skb_queue_walk(&tx->skbs, skb) {
  hdr = (struct ieee80211_hdr *) skb->data;
  hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
 }
}

int ieee80211_frame_duration(enum nl80211_band band, size_t len,
        int rate, int erp, int short_preamble)
{
 int dur;

 /* calculate duration (in microseconds, rounded up to next higher
 * integer if it includes a fractional microsecond) to send frame of
 * len bytes (does not include FCS) at the given rate. Duration will
 * also include SIFS.
 *
 * rate is in 100 kbps, so divident is multiplied by 10 in the
 * DIV_ROUND_UP() operations.
 */


 if (band == NL80211_BAND_5GHZ || erp) {
  /*
 * OFDM:
 *
 * N_DBPS = DATARATE x 4
 * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
 * (16 = SIGNAL time, 6 = tail bits)
 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
 *
 * T_SYM = 4 usec
 * 802.11a - 18.5.2: aSIFSTime = 16 usec
 * 802.11g - 19.8.4: aSIFSTime = 10 usec +
 * signal ext = 6 usec
 */

  dur = 16; /* SIFS + signal ext */
  dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
  dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */

  /* rates should already consider the channel bandwidth,
 * don't apply divisor again.
 */

  dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
     4 * rate); /* T_SYM x N_SYM */
 } else {
  /*
 * 802.11b or 802.11g with 802.11b compatibility:
 * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
 * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
 *
 * 802.11 (DS): 15.3.3, 802.11b: 18.3.4
 * aSIFSTime = 10 usec
 * aPreambleLength = 144 usec or 72 usec with short preamble
 * aPLCPHeaderLength = 48 usec or 24 usec with short preamble
 */

  dur = 10; /* aSIFSTime = 10 usec */
  dur += short_preamble ? (72 + 24) : (144 + 48);

  dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
 }

 return dur;
}

/* Exported duration function for driver use */
__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
     struct ieee80211_vif *vif,
     enum nl80211_band band,
     size_t frame_len,
     struct ieee80211_rate *rate)
{
 struct ieee80211_sub_if_data *sdata;
 u16 dur;
 int erp;
 bool short_preamble = false;

 erp = 0;
 if (vif) {
  sdata = vif_to_sdata(vif);
  short_preamble = sdata->vif.bss_conf.use_short_preamble;
  if (sdata->deflink.operating_11g_mode)
   erp = rate->flags & IEEE80211_RATE_ERP_G;
 }

 dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
           short_preamble);

 return cpu_to_le16(dur);
}
EXPORT_SYMBOL(ieee80211_generic_frame_duration);

__le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
         struct ieee80211_vif *vif, size_t frame_len,
         const struct ieee80211_tx_info *frame_txctl)
{
 struct ieee80211_local *local = hw_to_local(hw);
 struct ieee80211_rate *rate;
 struct ieee80211_sub_if_data *sdata;
 bool short_preamble;
 int erp, bitrate;
 u16 dur;
 struct ieee80211_supported_band *sband;

 sband = local->hw.wiphy->bands[frame_txctl->band];

 short_preamble = false;

 rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];

 erp = 0;
 if (vif) {
  sdata = vif_to_sdata(vif);
  short_preamble = sdata->vif.bss_conf.use_short_preamble;
  if (sdata->deflink.operating_11g_mode)
   erp = rate->flags & IEEE80211_RATE_ERP_G;
 }

 bitrate = rate->bitrate;

 /* CTS duration */
 dur = ieee80211_frame_duration(sband->band, 10, bitrate,
           erp, short_preamble);
 /* Data frame duration */
 dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
     erp, short_preamble);
 /* ACK duration */
 dur += ieee80211_frame_duration(sband->band, 10, bitrate,
     erp, short_preamble);

 return cpu_to_le16(dur);
}
EXPORT_SYMBOL(ieee80211_rts_duration);

__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
        struct ieee80211_vif *vif,
        size_t frame_len,
        const struct ieee80211_tx_info *frame_txctl)
{
 struct ieee80211_local *local = hw_to_local(hw);
 struct ieee80211_rate *rate;
 struct ieee80211_sub_if_data *sdata;
 bool short_preamble;
 int erp, bitrate;
 u16 dur;
 struct ieee80211_supported_band *sband;

 sband = local->hw.wiphy->bands[frame_txctl->band];

 short_preamble = false;

 rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
 erp = 0;
 if (vif) {
  sdata = vif_to_sdata(vif);
  short_preamble = sdata->vif.bss_conf.use_short_preamble;
  if (sdata->deflink.operating_11g_mode)
   erp = rate->flags & IEEE80211_RATE_ERP_G;
 }

 bitrate = rate->bitrate;

 /* Data frame duration */
 dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
           erp, short_preamble);
 if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
  /* ACK duration */
  dur += ieee80211_frame_duration(sband->band, 10, bitrate,
      erp, short_preamble);
 }

 return cpu_to_le16(dur);
}
EXPORT_SYMBOL(ieee80211_ctstoself_duration);

static void wake_tx_push_queue(struct ieee80211_local *local,
          struct ieee80211_sub_if_data *sdata,
          struct ieee80211_txq *queue)
{
 struct ieee80211_tx_control control = {
  .sta = queue->sta,
 };
 struct sk_buff *skb;

 while (1) {
  skb = ieee80211_tx_dequeue(&local->hw, queue);
  if (!skb)
   break;

  drv_tx(local, &control, skb);
 }
}

/* wake_tx_queue handler for driver not implementing a custom one*/
void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
        struct ieee80211_txq *txq)
{
 struct ieee80211_local *local = hw_to_local(hw);
 struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
 struct ieee80211_txq *queue;

 spin_lock(&local->handle_wake_tx_queue_lock);

 /* Use ieee80211_next_txq() for airtime fairness accounting */
 ieee80211_txq_schedule_start(hw, txq->ac);
 while ((queue = ieee80211_next_txq(hw, txq->ac))) {
  wake_tx_push_queue(local, sdata, queue);
  ieee80211_return_txq(hw, queue, false);
 }
 ieee80211_txq_schedule_end(hw, txq->ac);
 spin_unlock(&local->handle_wake_tx_queue_lock);
}
EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue);

static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
{
 struct ieee80211_local *local = sdata->local;
 struct ieee80211_vif *vif = &sdata->vif;
 struct fq *fq = &local->fq;
 struct ps_data *ps = NULL;
 struct txq_info *txqi;
 struct sta_info *sta;
 int i;

 local_bh_disable();
 spin_lock(&fq->lock);

 if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
  goto out;

 if (sdata->vif.type == NL80211_IFTYPE_AP)
  ps = &sdata->bss->ps;

 list_for_each_entry_rcu(sta, &local->sta_list, list) {
  if (sdata != sta->sdata)
   continue;

  for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
   struct ieee80211_txq *txq = sta->sta.txq[i];

   if (!txq)
    continue;

   txqi = to_txq_info(txq);

   if (ac != txq->ac)
    continue;

   if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY,
      &txqi->flags))
    continue;

   spin_unlock(&fq->lock);
   drv_wake_tx_queue(local, txqi);
   spin_lock(&fq->lock);
  }
 }

 if (!vif->txq)
  goto out;

 txqi = to_txq_info(vif->txq);

 if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ||
     (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac)
  goto out;

 spin_unlock(&fq->lock);

 drv_wake_tx_queue(local, txqi);
 local_bh_enable();
 return;
out:
 spin_unlock(&fq->lock);
 local_bh_enable();
}

static void
__releases(&local->queue_stop_reason_lock)
__acquires(&local->queue_stop_reason_lock)
_ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags)
{
 struct ieee80211_sub_if_data *sdata;
 int n_acs = IEEE80211_NUM_ACS;
 int i;

 rcu_read_lock();

 if (local->hw.queues < IEEE80211_NUM_ACS)
  n_acs = 1;

 for (i = 0; i < local->hw.queues; i++) {
  if (local->queue_stop_reasons[i])
   continue;

  spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags);
  list_for_each_entry_rcu(sdata, &local->interfaces, list) {
   int ac;

   for (ac = 0; ac < n_acs; ac++) {
    int ac_queue = sdata->vif.hw_queue[ac];

    if (ac_queue == i ||
        sdata->vif.cab_queue == i)
     __ieee80211_wake_txqs(sdata, ac);
   }
  }
  spin_lock_irqsave(&local->queue_stop_reason_lock, *flags);
 }

 rcu_read_unlock();
}

void ieee80211_wake_txqs(struct tasklet_struct *t)
{
 struct ieee80211_local *local = from_tasklet(local, t,
           wake_txqs_tasklet);
 unsigned long flags;

 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 _ieee80211_wake_txqs(local, &flags);
 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}

static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
       enum queue_stop_reason reason,
       bool refcounted,
       unsigned long *flags)
{
 struct ieee80211_local *local = hw_to_local(hw);

 if (WARN_ON(queue >= hw->queues))
  return;

 if (!test_bit(reason, &local->queue_stop_reasons[queue]))
  return;

 if (!refcounted) {
  local->q_stop_reasons[queue][reason] = 0;
 } else {
  local->q_stop_reasons[queue][reason]--;
  if (WARN_ON(local->q_stop_reasons[queue][reason] < 0))
   local->q_stop_reasons[queue][reason] = 0;
 }

 if (local->q_stop_reasons[queue][reason] == 0)
  __clear_bit(reason, &local->queue_stop_reasons[queue]);

 trace_wake_queue(local, queue, reason,
    local->q_stop_reasons[queue][reason]);

 if (local->queue_stop_reasons[queue] != 0)
  /* someone still has this queue stopped */
  return;

 if (!skb_queue_empty(&local->pending[queue]))
  tasklet_schedule(&local->tx_pending_tasklet);

 /*
 * Calling _ieee80211_wake_txqs here can be a problem because it may
 * release queue_stop_reason_lock which has been taken by
 * __ieee80211_wake_queue's caller. It is certainly not very nice to
 * release someone's lock, but it is fine because all the callers of
 * __ieee80211_wake_queue call it right before releasing the lock.
 */

 if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
  tasklet_schedule(&local->wake_txqs_tasklet);
 else
  _ieee80211_wake_txqs(local, flags);
}

void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
        enum queue_stop_reason reason,
        bool refcounted)
{
 struct ieee80211_local *local = hw_to_local(hw);
 unsigned long flags;

 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 __ieee80211_wake_queue(hw, queue, reason, refcounted, &flags);
 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}

void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
{
 ieee80211_wake_queue_by_reason(hw, queue,
           IEEE80211_QUEUE_STOP_REASON_DRIVER,
           false);
}
EXPORT_SYMBOL(ieee80211_wake_queue);

static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
       enum queue_stop_reason reason,
       bool refcounted)
{
 struct ieee80211_local *local = hw_to_local(hw);

 if (WARN_ON(queue >= hw->queues))
  return;

 if (!refcounted)
  local->q_stop_reasons[queue][reason] = 1;
 else
  local->q_stop_reasons[queue][reason]++;

 trace_stop_queue(local, queue, reason,
    local->q_stop_reasons[queue][reason]);

 set_bit(reason, &local->queue_stop_reasons[queue]);
}

void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
        enum queue_stop_reason reason,
        bool refcounted)
{
 struct ieee80211_local *local = hw_to_local(hw);
 unsigned long flags;

 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 __ieee80211_stop_queue(hw, queue, reason, refcounted);
 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}

void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
{
 ieee80211_stop_queue_by_reason(hw, queue,
           IEEE80211_QUEUE_STOP_REASON_DRIVER,
           false);
}
EXPORT_SYMBOL(ieee80211_stop_queue);

void ieee80211_add_pending_skb(struct ieee80211_local *local,
          struct sk_buff *skb)
{
 struct ieee80211_hw *hw = &local->hw;
 unsigned long flags;
 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 int queue = info->hw_queue;

 if (WARN_ON(!info->control.vif)) {
  ieee80211_free_txskb(&local->hw, skb);
  return;
 }

 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
          false);
 __skb_queue_tail(&local->pending[queue], skb);
 __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
          false, &flags);
 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}

void ieee80211_add_pending_skbs(struct ieee80211_local *local,
    struct sk_buff_head *skbs)
{
 struct ieee80211_hw *hw = &local->hw;
 struct sk_buff *skb;
 unsigned long flags;
 int queue, i;

 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 while ((skb = skb_dequeue(skbs))) {
  struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);

  if (WARN_ON(!info->control.vif)) {
   ieee80211_free_txskb(&local->hw, skb);
   continue;
  }

  queue = info->hw_queue;

  __ieee80211_stop_queue(hw, queue,
    IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
    false);

  __skb_queue_tail(&local->pending[queue], skb);
 }

 for (i = 0; i < hw->queues; i++)
  __ieee80211_wake_queue(hw, i,
   IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
   false, &flags);
 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}

void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
         unsigned long queues,
         enum queue_stop_reason reason,
         bool refcounted)
{
 struct ieee80211_local *local = hw_to_local(hw);
 unsigned long flags;
 int i;

 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);

 for_each_set_bit(i, &queues, hw->queues)
  __ieee80211_stop_queue(hw, i, reason, refcounted);

 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}

void ieee80211_stop_queues(struct ieee80211_hw *hw)
{
 ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
     IEEE80211_QUEUE_STOP_REASON_DRIVER,
     false);
}
EXPORT_SYMBOL(ieee80211_stop_queues);

int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
{
 struct ieee80211_local *local = hw_to_local(hw);
 unsigned long flags;
 int ret;

 if (WARN_ON(queue >= hw->queues))
  return true;

 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER,
         &local->queue_stop_reasons[queue]);
 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 return ret;
}
EXPORT_SYMBOL(ieee80211_queue_stopped);

void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
         unsigned long queues,
         enum queue_stop_reason reason,
         bool refcounted)
{
 struct ieee80211_local *local = hw_to_local(hw);
 unsigned long flags;
 int i;

 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);

 for_each_set_bit(i, &queues, hw->queues)
  __ieee80211_wake_queue(hw, i, reason, refcounted, &flags);

 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}

void ieee80211_wake_queues(struct ieee80211_hw *hw)
{
 ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
     IEEE80211_QUEUE_STOP_REASON_DRIVER,
     false);
}
EXPORT_SYMBOL(ieee80211_wake_queues);

unsigned int
ieee80211_get_vif_queues(struct ieee80211_local *local,
    struct ieee80211_sub_if_data *sdata)
{
 unsigned int queues;

 if (sdata && ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
  int ac;

  queues = 0;

  for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
   if (sdata->vif.hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
    queues |= BIT(sdata->vif.hw_queue[ac]);
  if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE)
   queues |= BIT(sdata->vif.cab_queue);
 } else {
  /* all queues */
  queues = BIT(local->hw.queues) - 1;
 }

 return queues;
}

void __ieee80211_flush_queues(struct ieee80211_local *local,
         struct ieee80211_sub_if_data *sdata,
         unsigned int queues, bool drop)
{
 if (!local->ops->flush && !drop)
  return;

 /*
 * If no queue was set, or if the HW doesn't support
 * IEEE80211_HW_QUEUE_CONTROL - flush all queues
 */

 if (!queues || !ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
  queues = ieee80211_get_vif_queues(local, sdata);

 ieee80211_stop_queues_by_reason(&local->hw, queues,
     IEEE80211_QUEUE_STOP_REASON_FLUSH,
     false);

 if (drop) {
  struct sta_info *sta;

  /* Purge the queues, so the frames on them won't be
 * sent during __ieee80211_wake_queue()
 */

  list_for_each_entry(sta, &local->sta_list, list) {
   if (sdata != sta->sdata)
    continue;
   ieee80211_purge_sta_txqs(sta);
  }
 }

 if (local->ops->flush)
  drv_flush(local, sdata, queues, drop);

 ieee80211_wake_queues_by_reason(&local->hw, queues,
     IEEE80211_QUEUE_STOP_REASON_FLUSH,
     false);
}

void ieee80211_flush_queues(struct ieee80211_local *local,
       struct ieee80211_sub_if_data *sdata, bool drop)
{
 __ieee80211_flush_queues(local, sdata, 0, drop);
}

static void __iterate_interfaces(struct ieee80211_local *local,
     u32 iter_flags,
     void (*iterator)(void *data, u8 *mac,
        struct ieee80211_vif *vif),
     void *data)
{
 struct ieee80211_sub_if_data *sdata;
 bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;

 list_for_each_entry_rcu(sdata, &local->interfaces, list,
    lockdep_is_held(&local->iflist_mtx) ||
    lockdep_is_held(&local->hw.wiphy->mtx)) {
  switch (sdata->vif.type) {
  case NL80211_IFTYPE_MONITOR:
   if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
       !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
    continue;
   break;
  case NL80211_IFTYPE_AP_VLAN:
   continue;
  default:
   break;
  }
  if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
      active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
   continue;
  if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) &&
      !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
   continue;
  if (ieee80211_sdata_running(sdata) || !active_only)
   iterator(data, sdata->vif.addr,
     &sdata->vif);
 }

 sdata = rcu_dereference_check(local->monitor_sdata,
          lockdep_is_held(&local->iflist_mtx) ||
          lockdep_is_held(&local->hw.wiphy->mtx));
 if (sdata && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
     (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
      sdata->flags & IEEE80211_SDATA_IN_DRIVER))
  iterator(data, sdata->vif.addr, &sdata->vif);
}

void ieee80211_iterate_interfaces(
 struct ieee80211_hw *hw, u32 iter_flags,
 void (*iterator)(void *data, u8 *mac,
    struct ieee80211_vif *vif),
 void *data)
{
 struct ieee80211_local *local = hw_to_local(hw);

 mutex_lock(&local->iflist_mtx);
 __iterate_interfaces(local, iter_flags, iterator, data);
 mutex_unlock(&local->iflist_mtx);
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces);

void ieee80211_iterate_active_interfaces_atomic(
 struct ieee80211_hw *hw, u32 iter_flags,
 void (*iterator)(void *data, u8 *mac,
    struct ieee80211_vif *vif),
 void *data)
{
 struct ieee80211_local *local = hw_to_local(hw);

 rcu_read_lock();
 __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
        iterator, data);
 rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);

void ieee80211_iterate_active_interfaces_mtx(
 struct ieee80211_hw *hw, u32 iter_flags,
 void (*iterator)(void *data, u8 *mac,
    struct ieee80211_vif *vif),
 void *data)
{
 struct ieee80211_local *local = hw_to_local(hw);

 lockdep_assert_wiphy(hw->wiphy);

 __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
        iterator, data);
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_mtx);

static void __iterate_stations(struct ieee80211_local *local,
          void (*iterator)(void *data,
      struct ieee80211_sta *sta),
          void *data)
{
 struct sta_info *sta;

 list_for_each_entry_rcu(sta, &local->sta_list, list,
    lockdep_is_held(&local->hw.wiphy->mtx)) {
  if (!sta->uploaded)
   continue;

  iterator(data, &sta->sta);
 }
}

void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
   void (*iterator)(void *data,
      struct ieee80211_sta *sta),
   void *data)
{
 struct ieee80211_local *local = hw_to_local(hw);

 rcu_read_lock();
 __iterate_stations(local, iterator, data);
 rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic);

void ieee80211_iterate_stations_mtx(struct ieee80211_hw *hw,
        void (*iterator)(void *data,
           struct ieee80211_sta *sta),
        void *data)
{
 struct ieee80211_local *local = hw_to_local(hw);

 lockdep_assert_wiphy(local->hw.wiphy);

 __iterate_stations(local, iterator, data);
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_mtx);

struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
{
 struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);

 if (!ieee80211_sdata_running(sdata) ||
     !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
  return NULL;
 return &sdata->vif;
}
EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);

struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
{
 if (!vif)
  return NULL;

 return &vif_to_sdata(vif)->wdev;
}
EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);

/*
 * Nothing should have been stuffed into the workqueue during
 * the suspend->resume cycle. Since we can't check each caller
 * of this function if we are already quiescing / suspended,
 * check here and don't WARN since this can actually happen when
 * the rx path (for example) is racing against __ieee80211_suspend
 * and suspending / quiescing was set after the rx path checked
 * them.
 */

static bool ieee80211_can_queue_work(struct ieee80211_local *local)
{
 if (local->quiescing || (local->suspended && !local->resuming)) {
  pr_warn("queueing ieee80211 work while going to suspend\n");
  return false;
 }

 return true;
}

void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work)
{
 struct ieee80211_local *local = hw_to_local(hw);

 if (!ieee80211_can_queue_work(local))
  return;

 queue_work(local->workqueue, work);
}
EXPORT_SYMBOL(ieee80211_queue_work);

void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
      struct delayed_work *dwork,
      unsigned long delay)
{
 struct ieee80211_local *local = hw_to_local(hw);

 if (!ieee80211_can_queue_work(local))
  return;

 queue_delayed_work(local->workqueue, dwork, delay);
}
EXPORT_SYMBOL(ieee80211_queue_delayed_work);

void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_tx_queue_params
        *qparam, int ac)
{
 struct ieee80211_chanctx_conf *chanctx_conf;
 const struct ieee80211_reg_rule *rrule;
 const struct ieee80211_wmm_ac *wmm_ac;
 u16 center_freq = 0;

 if (sdata->vif.type != NL80211_IFTYPE_AP &&
     sdata->vif.type != NL80211_IFTYPE_STATION)
  return;

 rcu_read_lock();
 chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
 if (chanctx_conf)
  center_freq = chanctx_conf->def.chan->center_freq;

 if (!center_freq) {
  rcu_read_unlock();
  return;
 }

 rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq));

 if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) {
  rcu_read_unlock();
  return;
 }

 if (sdata->vif.type == NL80211_IFTYPE_AP)
  wmm_ac = &rrule->wmm_rule.ap[ac];
 else
  wmm_ac = &rrule->wmm_rule.client[ac];
 qparam->cw_min = max_t(u16, qparam->cw_min, wmm_ac->cw_min);
 qparam->cw_max = max_t(u16, qparam->cw_max, wmm_ac->cw_max);
 qparam->aifs = max_t(u8, qparam->aifs, wmm_ac->aifsn);
 qparam->txop = min_t(u16, qparam->txop, wmm_ac->cot / 32);
 rcu_read_unlock();
}

void ieee80211_set_wmm_default(struct ieee80211_link_data *link,
          bool bss_notify, bool enable_qos)
{
 struct ieee80211_sub_if_data *sdata = link->sdata;
 struct ieee80211_local *local = sdata->local;
 struct ieee80211_tx_queue_params qparam;
 struct ieee80211_chanctx_conf *chanctx_conf;
 int ac;
 bool use_11b;
 bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
 int aCWmin, aCWmax;

 if (!local->ops->conf_tx)
  return;

 if (local->hw.queues < IEEE80211_NUM_ACS)
  return;

 memset(&qparam, 0, sizeof(qparam));

 rcu_read_lock();
 chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
 use_11b = (chanctx_conf &&
     chanctx_conf->def.chan->band == NL80211_BAND_2GHZ) &&
   !link->operating_11g_mode;
 rcu_read_unlock();

 is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);

 /* Set defaults according to 802.11-2007 Table 7-37 */
 aCWmax = 1023;
 if (use_11b)
  aCWmin = 31;
 else
  aCWmin = 15;

 /* Configure old 802.11b/g medium access rules. */
 qparam.cw_max = aCWmax;
 qparam.cw_min = aCWmin;
 qparam.txop = 0;
 qparam.aifs = 2;

 for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
  /* Update if QoS is enabled. */
  if (enable_qos) {
   switch (ac) {
   case IEEE80211_AC_BK:
    qparam.cw_max = aCWmax;
    qparam.cw_min = aCWmin;
    qparam.txop = 0;
    if (is_ocb)
     qparam.aifs = 9;
    else
     qparam.aifs = 7;
    break;
   /* never happens but let's not leave undefined */
   default:
   case IEEE80211_AC_BE:
    qparam.cw_max = aCWmax;
    qparam.cw_min = aCWmin;
    qparam.txop = 0;
    if (is_ocb)
     qparam.aifs = 6;
    else
     qparam.aifs = 3;
    break;
   case IEEE80211_AC_VI:
    qparam.cw_max = aCWmin;
    qparam.cw_min = (aCWmin + 1) / 2 - 1;
    if (is_ocb)
     qparam.txop = 0;
    else if (use_11b)
     qparam.txop = 6016/32;
    else
     qparam.txop = 3008/32;

    if (is_ocb)
     qparam.aifs = 3;
    else
     qparam.aifs = 2;
    break;
   case IEEE80211_AC_VO:
    qparam.cw_max = (aCWmin + 1) / 2 - 1;
    qparam.cw_min = (aCWmin + 1) / 4 - 1;
    if (is_ocb)
     qparam.txop = 0;
    else if (use_11b)
     qparam.txop = 3264/32;
    else
     qparam.txop = 1504/32;
    qparam.aifs = 2;
    break;
   }
  }
  ieee80211_regulatory_limit_wmm_params(sdata, &qparam, ac);

  qparam.uapsd = false;

  link->tx_conf[ac] = qparam;
  drv_conf_tx(local, link, ac, &qparam);
 }

 if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
     sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
     sdata->vif.type != NL80211_IFTYPE_NAN) {
  link->conf->qos = enable_qos;
  if (bss_notify)
   ieee80211_link_info_change_notify(sdata, link,
         BSS_CHANGED_QOS);
 }
}

void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
    u16 transaction, u16 auth_alg, u16 status,
    const u8 *extra, size_t extra_len, const u8 *da,
    const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
    u32 tx_flags)
{
 struct ieee80211_local *local = sdata->local;
 struct sk_buff *skb;
 struct ieee80211_mgmt *mgmt;
 bool multi_link = ieee80211_vif_is_mld(&sdata->vif);
 struct {
  u8 id;
  u8 len;
  u8 ext_id;
  struct ieee80211_multi_link_elem ml;
  struct ieee80211_mle_basic_common_info basic;
 } __packed mle = {
  .id = WLAN_EID_EXTENSION,
  .len = sizeof(mle) - 2,
  .ext_id = WLAN_EID_EXT_EHT_MULTI_LINK,
  .ml.control = cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC),
  .basic.len = sizeof(mle.basic),
 };
 int err;

 memcpy(mle.basic.mld_mac_addr, sdata->vif.addr, ETH_ALEN);

 /* 24 + 6 = header + auth_algo + auth_transaction + status_code */
 skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN +
       24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN +
       multi_link * sizeof(mle));
 if (!skb)
  return;

 skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN);

 mgmt = skb_put_zero(skb, 24 + 6);
 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
       IEEE80211_STYPE_AUTH);
 memcpy(mgmt->da, da, ETH_ALEN);
 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
 memcpy(mgmt->bssid, bssid, ETH_ALEN);
 mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
 mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
 mgmt->u.auth.status_code = cpu_to_le16(status);
 if (extra)
  skb_put_data(skb, extra, extra_len);
 if (multi_link)
  skb_put_data(skb, &mle, sizeof(mle));

 if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
  mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
  err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
  if (WARN_ON(err)) {
   kfree_skb(skb);
   return;
  }
 }

 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
     tx_flags;
 ieee80211_tx_skb(sdata, skb);
}

void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
        const u8 *da, const u8 *bssid,
        u16 stype, u16 reason,
        bool send_frame, u8 *frame_buf)
{
 struct ieee80211_local *local = sdata->local;
 struct sk_buff *skb;
 struct ieee80211_mgmt *mgmt = (void *)frame_buf;

 /* build frame */
 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
 mgmt->duration = 0; /* initialize only */
 mgmt->seq_ctrl = 0; /* initialize only */
 memcpy(mgmt->da, da, ETH_ALEN);
 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
 memcpy(mgmt->bssid, bssid, ETH_ALEN);
 /* u.deauth.reason_code == u.disassoc.reason_code */
 mgmt->u.deauth.reason_code = cpu_to_le16(reason);

 if (send_frame) {
  skb = dev_alloc_skb(local->hw.extra_tx_headroom +
        IEEE80211_DEAUTH_FRAME_LEN);
  if (!skb)
   return;

  skb_reserve(skb, local->hw.extra_tx_headroom);

  /* copy in frame */
  skb_put_data(skb, mgmt, IEEE80211_DEAUTH_FRAME_LEN);

  if (sdata->vif.type != NL80211_IFTYPE_STATION ||
      !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
   IEEE80211_SKB_CB(skb)->flags |=
    IEEE80211_TX_INTFL_DONT_ENCRYPT;

  ieee80211_tx_skb(sdata, skb);
 }
}

static int ieee80211_put_s1g_cap(struct sk_buff *skb,
     struct ieee80211_sta_s1g_cap *s1g_cap)
{
 if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_s1g_cap))
  return -ENOBUFS;

 skb_put_u8(skb, WLAN_EID_S1G_CAPABILITIES);
 skb_put_u8(skb, sizeof(struct ieee80211_s1g_cap));

 skb_put_data(skb, &s1g_cap->cap, sizeof(s1g_cap->cap));
 skb_put_data(skb, &s1g_cap->nss_mcs, sizeof(s1g_cap->nss_mcs));

 return 0;
}

static int ieee80211_put_preq_ies_band(struct sk_buff *skb,
           struct ieee80211_sub_if_data *sdata,
           const u8 *ie, size_t ie_len,
           size_t *offset,
           enum nl80211_band band,
           u32 rate_mask,
           struct cfg80211_chan_def *chandef,
           u32 flags)
{
 struct ieee80211_local *local = sdata->local;
 struct ieee80211_supported_band *sband;
 int i, err;
 size_t noffset;
 bool have_80mhz = false;

 *offset = 0;

 sband = local->hw.wiphy->bands[band];
 if (WARN_ON_ONCE(!sband))
  return 0;

 /* For direct scan add S1G IE and consider its override bits */
 if (band == NL80211_BAND_S1GHZ)
  return ieee80211_put_s1g_cap(skb, &sband->s1g_cap);

 err = ieee80211_put_srates_elem(skb, sband, 0,
     ~rate_mask, WLAN_EID_SUPP_RATES);
 if (err)
  return err;

 /* insert "request information" if in custom IEs */
 if (ie && ie_len) {
  static const u8 before_extrates[] = {
   WLAN_EID_SSID,
   WLAN_EID_SUPP_RATES,
   WLAN_EID_REQUEST,
  };
  noffset = ieee80211_ie_split(ie, ie_len,
          before_extrates,
          ARRAY_SIZE(before_extrates),
          *offset);
  if (skb_tailroom(skb) < noffset - *offset)
   return -ENOBUFS;
  skb_put_data(skb, ie + *offset, noffset - *offset);
  *offset = noffset;
 }

 err = ieee80211_put_srates_elem(skb, sband, 0,
     ~rate_mask, WLAN_EID_EXT_SUPP_RATES);
 if (err)
  return err;

 if (chandef->chan && sband->band == NL80211_BAND_2GHZ) {
  if (skb_tailroom(skb) < 3)
   return -ENOBUFS;
  skb_put_u8(skb, WLAN_EID_DS_PARAMS);
  skb_put_u8(skb, 1);
  skb_put_u8(skb,
      ieee80211_frequency_to_channel(chandef->chan->center_freq));
 }

 if (flags & IEEE80211_PROBE_FLAG_MIN_CONTENT)
  return 0;

 /* insert custom IEs that go before HT */
 if (ie && ie_len) {
  static const u8 before_ht[] = {
   /*
 * no need to list the ones split off already
 * (or generated here)
 */

   WLAN_EID_DS_PARAMS,
   WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
  };
  noffset = ieee80211_ie_split(ie, ie_len,
          before_ht, ARRAY_SIZE(before_ht),
          *offset);
  if (skb_tailroom(skb) < noffset - *offset)
   return -ENOBUFS;
  skb_put_data(skb, ie + *offset, noffset - *offset);
  *offset = noffset;
 }

 if (sband->ht_cap.ht_supported) {
  u8 *pos;

  if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
   return -ENOBUFS;

  pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap));
  ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
       sband->ht_cap.cap);
 }

 /* insert custom IEs that go before VHT */
 if (ie && ie_len) {
  static const u8 before_vht[] = {
   /*
 * no need to list the ones split off already
 * (or generated here)
 */

   WLAN_EID_BSS_COEX_2040,
   WLAN_EID_EXT_CAPABILITY,
   WLAN_EID_SSID_LIST,
   WLAN_EID_CHANNEL_USAGE,
   WLAN_EID_INTERWORKING,
   WLAN_EID_MESH_ID,
   /* 60 GHz (Multi-band, DMG, MMS) can't happen */
  };
  noffset = ieee80211_ie_split(ie, ie_len,
          before_vht, ARRAY_SIZE(before_vht),
          *offset);
  if (skb_tailroom(skb) < noffset - *offset)
   return -ENOBUFS;
  skb_put_data(skb, ie + *offset, noffset - *offset);
  *offset = noffset;
 }

 /* Check if any channel in this sband supports at least 80 MHz */
 for (i = 0; i < sband->n_channels; i++) {
  if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
      IEEE80211_CHAN_NO_80MHZ))
   continue;

  have_80mhz = true;
  break;
 }

 if (sband->vht_cap.vht_supported && have_80mhz) {
  u8 *pos;

  if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_cap))
   return -ENOBUFS;

  pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_cap));
  ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
        sband->vht_cap.cap);
 }

 /* insert custom IEs that go before HE */
 if (ie && ie_len) {
  static const u8 before_he[] = {
   /*
 * no need to list the ones split off before VHT
 * or generated here
 */

   WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS,
   WLAN_EID_AP_CSN,
   /* TODO: add 11ah/11aj/11ak elements */
  };
  noffset = ieee80211_ie_split(ie, ie_len,
          before_he, ARRAY_SIZE(before_he),
          *offset);
  if (skb_tailroom(skb) < noffset - *offset)
   return -ENOBUFS;
  skb_put_data(skb, ie + *offset, noffset - *offset);
  *offset = noffset;
 }

 if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
      IEEE80211_CHAN_NO_HE)) {
  err = ieee80211_put_he_cap(skb, sdata, sband, NULL);
  if (err)
   return err;
 }

 if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
      IEEE80211_CHAN_NO_HE |
      IEEE80211_CHAN_NO_EHT)) {
  err = ieee80211_put_eht_cap(skb, sdata, sband, NULL);
  if (err)
   return err;
 }

 err = ieee80211_put_he_6ghz_cap(skb, sdata, IEEE80211_SMPS_OFF);
 if (err)
  return err;

 /*
 * If adding more here, adjust code in main.c
 * that calculates local->scan_ies_len.
 */


 return 0;
}

static int ieee80211_put_preq_ies(struct sk_buff *skb,
      struct ieee80211_sub_if_data *sdata,
      struct ieee80211_scan_ies *ie_desc,
      const u8 *ie, size_t ie_len,
      u8 bands_used, u32 *rate_masks,
      struct cfg80211_chan_def *chandef,
      u32 flags)
{
 size_t custom_ie_offset = 0;
 int i, err;

 memset(ie_desc, 0, sizeof(*ie_desc));

 for (i = 0; i < NUM_NL80211_BANDS; i++) {
  if (bands_used & BIT(i)) {
   ie_desc->ies[i] = skb_tail_pointer(skb);
   err = ieee80211_put_preq_ies_band(skb, sdata,
         ie, ie_len,
         &custom_ie_offset,
         i, rate_masks[i],
         chandef, flags);
   if (err)
    return err;
   ie_desc->len[i] = skb_tail_pointer(skb) -
       ie_desc->ies[i];
  }
 }

 /* add any remaining custom IEs */
 if (ie && ie_len) {
  if (WARN_ONCE(skb_tailroom(skb) < ie_len - custom_ie_offset,
         "not enough space for preq custom IEs\n"))
   return -ENOBUFS;
  ie_desc->common_ies = skb_tail_pointer(skb);
  skb_put_data(skb, ie + custom_ie_offset,
        ie_len - custom_ie_offset);
  ie_desc->common_ie_len = skb_tail_pointer(skb) -
      ie_desc->common_ies;
 }

 return 0;
};

int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer,
        size_t buffer_len,
        struct ieee80211_scan_ies *ie_desc,
        const u8 *ie, size_t ie_len,
        u8 bands_used, u32 *rate_masks,
        struct cfg80211_chan_def *chandef,
        u32 flags)
{
 struct sk_buff *skb = alloc_skb(buffer_len, GFP_KERNEL);
 uintptr_t offs;
 int ret, i;
 u8 *start;

 if (!skb)
  return -ENOMEM;

 start = skb_tail_pointer(skb);
 memset(start, 0, skb_tailroom(skb));
 ret = ieee80211_put_preq_ies(skb, sdata, ie_desc, ie, ie_len,
         bands_used, rate_masks, chandef,
         flags);
 if (ret < 0) {
  goto out;
 }

 if (skb->len > buffer_len) {
  ret = -ENOBUFS;
  goto out;
 }

 memcpy(buffer, start, skb->len);

 /* adjust ie_desc for copy */
 for (i = 0; i < NUM_NL80211_BANDS; i++) {
  offs = ie_desc->ies[i] - start;
  ie_desc->ies[i] = buffer + offs;
 }
 offs = ie_desc->common_ies - start;
 ie_desc->common_ies = buffer + offs;

 ret = skb->len;
out:
 consume_skb(skb);
 return ret;
}

struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
       const u8 *src, const u8 *dst,
       u32 ratemask,
       struct ieee80211_channel *chan,
       const u8 *ssid, size_t ssid_len,
       const u8 *ie, size_t ie_len,
       u32 flags)
{
 struct ieee80211_local *local = sdata->local;
 struct cfg80211_chan_def chandef;
 struct sk_buff *skb;
 struct ieee80211_mgmt *mgmt;
 u32 rate_masks[NUM_NL80211_BANDS] = {};
 struct ieee80211_scan_ies dummy_ie_desc;

 /*
 * Do not send DS Channel parameter for directed probe requests
 * in order to maximize the chance that we get a response.  Some
 * badly-behaved APs don't respond when this parameter is included.
 */

 chandef.width = sdata->vif.bss_conf.chanreq.oper.width;
 if (flags & IEEE80211_PROBE_FLAG_DIRECTED)
  chandef.chan = NULL;
 else
  chandef.chan = chan;

 skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len,
         local->scan_ies_len + ie_len);
 if (!skb)
  return NULL;

 rate_masks[chan->band] = ratemask;
 ieee80211_put_preq_ies(skb, sdata, &dummy_ie_desc,
          ie, ie_len, BIT(chan->band),
          rate_masks, &chandef, flags);

 if (dst) {
  mgmt = (struct ieee80211_mgmt *) skb->data;
  memcpy(mgmt->da, dst, ETH_ALEN);
  memcpy(mgmt->bssid, dst, ETH_ALEN);
 }

 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;

 return skb;
}

u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
       struct ieee802_11_elems *elems,
       enum nl80211_band band, u32 *basic_rates)
{
 struct ieee80211_supported_band *sband;
 size_t num_rates;
 u32 supp_rates;
 int i, j;

 sband = sdata->local->hw.wiphy->bands[band];
 if (WARN_ON(!sband))
  return 1;

 num_rates = sband->n_bitrates;
 supp_rates = 0;
 for (i = 0; i < elems->supp_rates_len +
       elems->ext_supp_rates_len; i++) {
  u8 rate = 0;
  int own_rate;
  bool is_basic;
  if (i < elems->supp_rates_len)
   rate = elems->supp_rates[i];
  else if (elems->ext_supp_rates)
   rate = elems->ext_supp_rates
    [i - elems->supp_rates_len];
  own_rate = 5 * (rate & 0x7f);
  is_basic = !!(rate & 0x80);

  if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
   continue;

  for (j = 0; j < num_rates; j++) {
   int brate = sband->bitrates[j].bitrate;

   if (brate == own_rate) {
    supp_rates |= BIT(j);
    if (basic_rates && is_basic)
     *basic_rates |= BIT(j);
   }
  }
 }
 return supp_rates;
}

void ieee80211_stop_device(struct ieee80211_local *local, bool suspend)
{
 local_bh_disable();
 ieee80211_handle_queued_frames(local);
 local_bh_enable();

 ieee80211_led_radio(local, false);
 ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO);

 wiphy_work_cancel(local->hw.wiphy, &local->reconfig_filter);

 flush_workqueue(local->workqueue);
 wiphy_work_flush(local->hw.wiphy, NULL);
 drv_stop(local, suspend);
}

static void ieee80211_flush_completed_scan(struct ieee80211_local *local,
        bool aborted)
{
 /* It's possible that we don't handle the scan completion in
 * time during suspend, so if it's still marked as completed
 * here, queue the work and flush it to clean things up.
 * Instead of calling the worker function directly here, we
 * really queue it to avoid potential races with other flows
 * scheduling the same work.
 */

 if (test_bit(SCAN_COMPLETED, &local->scanning)) {
  /* If coming from reconfiguration failure, abort the scan so
 * we don't attempt to continue a partial HW scan - which is
 * possible otherwise if (e.g.) the 2.4 GHz portion was the
 * completed scan, and a 5 GHz portion is still pending.
 */

  if (aborted)
   set_bit(SCAN_ABORTED, &local->scanning);
  wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0);
  wiphy_delayed_work_flush(local->hw.wiphy, &local->scan_work);
 }
}

static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
{
 struct ieee80211_sub_if_data *sdata;
 struct ieee80211_chanctx *ctx;

 lockdep_assert_wiphy(local->hw.wiphy);

 /*
 * We get here if during resume the device can't be restarted properly.
 * We might also get here if this happens during HW reset, which is a
 * slightly different situation and we need to drop all connections in
 * the latter case.
 *
 * Ask cfg80211 to turn off all interfaces, this will result in more
 * warnings but at least we'll then get into a clean stopped state.
 */


 local->resuming = false;
 local->suspended = false;
 local->in_reconfig = false;
 local->reconfig_failure = true;

 ieee80211_flush_completed_scan(local, true);

 /* scheduled scan clearly can't be running any more, but tell
 * cfg80211 and clear local state
 */

 ieee80211_sched_scan_end(local);

 list_for_each_entry(sdata, &local->interfaces, list)
  sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;

 /* Mark channel contexts as not being in the driver any more to avoid
 * removing them from the driver during the shutdown process...
 */

 list_for_each_entry(ctx, &local->chanctx_list, list)
  ctx->driver_present = false;
}

static void ieee80211_assign_chanctx(struct ieee80211_local *local,
         struct ieee80211_sub_if_data *sdata,
         struct ieee80211_link_data *link)
{
 struct ieee80211_chanctx_conf *conf;
 struct ieee80211_chanctx *ctx;

 lockdep_assert_wiphy(local->hw.wiphy);

 conf = rcu_dereference_protected(link->conf->chanctx_conf,
      lockdep_is_held(&local->hw.wiphy->mtx));
 if (conf) {
  ctx = container_of(conf, struct ieee80211_chanctx, conf);
  drv_assign_vif_chanctx(local, sdata, link->conf, ctx);
 }
}

static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata)
{
 struct ieee80211_local *local = sdata->local;
 struct sta_info *sta;

 lockdep_assert_wiphy(local->hw.wiphy);

 /* add STAs back */
 list_for_each_entry(sta, &local->sta_list, list) {
  enum ieee80211_sta_state state;

  if (!sta->uploaded || sta->sdata != sdata)
   continue;

  for (state = IEEE80211_STA_NOTEXIST;
       state < sta->sta_state; state++)
   WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
           state + 1));
 }
}

static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata)
{
 struct cfg80211_nan_func *func, **funcs;
 int res, id, i = 0;

 res = drv_start_nan(sdata->local, sdata,
       &sdata->u.nan.conf);
 if (WARN_ON(res))
  return res;

 funcs = kcalloc(sdata->local->hw.max_nan_de_entries + 1,
   sizeof(*funcs),
   GFP_KERNEL);
 if (!funcs)
  return -ENOMEM;

 /* Add all the functions:
 * This is a little bit ugly. We need to call a potentially sleeping
 * callback for each NAN function, so we can't hold the spinlock.
 */

 spin_lock_bh(&sdata->u.nan.func_lock);

 idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id)
  funcs[i++] = func;

 spin_unlock_bh(&sdata->u.nan.func_lock);

 for (i = 0; funcs[i]; i++) {
  res = drv_add_nan_func(sdata->local, sdata, funcs[i]);
  if (WARN_ON(res))
   ieee80211_nan_func_terminated(&sdata->vif,
            funcs[i]->instance_id,
            NL80211_NAN_FUNC_TERM_REASON_ERROR,
            GFP_KERNEL);
 }

 kfree(funcs);

 return 0;
}

static void ieee80211_reconfig_ap_links(struct ieee80211_local *local,
     struct ieee80211_sub_if_data *sdata,
     u64 changed)
{
 int link_id;

 for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
  struct ieee80211_link_data *link;

  if (!(sdata->vif.active_links & BIT(link_id)))
   continue;

  link = sdata_dereference(sdata->link[link_id], sdata);
  if (!link)
   continue;

  if (rcu_access_pointer(link->u.ap.beacon))
   drv_start_ap(local, sdata, link->conf);

  if (!link->conf->enable_beacon)
   continue;

  changed |= BSS_CHANGED_BEACON |
      BSS_CHANGED_BEACON_ENABLED;

  ieee80211_link_info_change_notify(sdata, link, changed);
 }
}

int ieee80211_reconfig(struct ieee80211_local *local)
{
 struct ieee80211_hw *hw = &local->hw;
 struct ieee80211_sub_if_data *sdata;
 struct ieee80211_chanctx *ctx;
 struct sta_info *sta;
 int res, i;
 bool reconfig_due_to_wowlan = false;
 struct ieee80211_sub_if_data *sched_scan_sdata;
 struct cfg80211_sched_scan_request *sched_scan_req;
 bool sched_scan_stopped = false;
 bool suspended = local->suspended;
 bool in_reconfig = false;
 u32 rts_threshold;

 lockdep_assert_wiphy(local->hw.wiphy);

 /* nothing to do if HW shouldn't run */
 if (!local->open_count)
  goto wake_up;

#ifdef CONFIG_PM
 if (suspended)
  local->resuming = true;

 if (local->wowlan) {
  /*
 * In the wowlan case, both mac80211 and the device
 * are functional when the resume op is called, so
 * clear local->suspended so the device could operate
 * normally (e.g. pass rx frames).
 */

  local->suspended = false;
  res = drv_resume(local);
  local->wowlan = false;
  if (res < 0) {
   local->resuming = false;
   return res;
  }
  if (res == 0)
   goto wake_up;
  WARN_ON(res > 1);
  /*
 * res is 1, which means the driver requested
 * to go through a regular reset on wakeup.
 * restore local->suspended in this case.
 */

  reconfig_due_to_wowlan = true;
  local->suspended = true;
 }
#endif

 /*
 * In case of hw_restart during suspend (without wowlan),
 * cancel restart work, as we are reconfiguring the device
 * anyway.
 * Note that restart_work is scheduled on a frozen workqueue,
 * so we can't deadlock in this case.
 */

 if (suspended && local->in_reconfig && !reconfig_due_to_wowlan)
  cancel_work_sync(&local->restart_work);

 local->started = false;

 /*
 * Upon resume hardware can sometimes be goofy due to
 * various platform / driver / bus issues, so restarting
 * the device may at times not work immediately. Propagate
 * the error.
 */

 res = drv_start(local);
 if (res) {
  if (suspended)
   WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
  else
   WARN(1, "Hardware became unavailable during restart.\n");
  ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
      IEEE80211_QUEUE_STOP_REASON_SUSPEND,
      false);
  ieee80211_handle_reconfig_failure(local);
  return res;
 }

 /* setup fragmentation threshold */
 drv_set_frag_threshold(local, -1, hw->wiphy->frag_threshold);

 /* setup RTS threshold */
 if (hw->wiphy->n_radio > 0) {
  for (i = 0; i < hw->wiphy->n_radio; i++) {
   rts_threshold = hw->wiphy->radio_cfg[i].rts_threshold;
   drv_set_rts_threshold(local, i, rts_threshold);
  }
 } else {
  drv_set_rts_threshold(local, -1, hw->wiphy->rts_threshold);
 }

 /* reset coverage class */
 drv_set_coverage_class(local, -1, hw->wiphy->coverage_class);

 ieee80211_led_radio(local, true);
 ieee80211_mod_tpt_led_trig(local,
       IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);

 /* add interfaces */
 sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
 if (sdata && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) {
  /* in HW restart it exists already */
  WARN_ON(local->resuming);
  res = drv_add_interface(local, sdata);
  if (WARN_ON(res)) {
   RCU_INIT_POINTER(local->monitor_sdata, NULL);
   synchronize_net();
   kfree(sdata);
  }
 }

 list_for_each_entry(sdata, &local->interfaces, list) {
  if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
      !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
   continue;
  if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
      ieee80211_sdata_running(sdata)) {
   res = drv_add_interface(local, sdata);
   if (WARN_ON(res))
    break;
  }
 }

 /* If adding any of the interfaces failed above, roll back and
 * report failure.
 */

 if (res) {
  list_for_each_entry_continue_reverse(sdata, &local->interfaces,
           list) {
   if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
       !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
    continue;
   if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
       ieee80211_sdata_running(sdata))
    drv_remove_interface(local, sdata);
  }
  ieee80211_handle_reconfig_failure(local);
  return res;
 }

 /* add channel contexts */
 list_for_each_entry(ctx, &local->chanctx_list, list)
  if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
   WARN_ON(drv_add_chanctx(local, ctx));

 sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
 if (sdata && ieee80211_sdata_running(sdata))
  ieee80211_assign_chanctx(local, sdata, &sdata->deflink);

 /* reconfigure hardware */
 ieee80211_hw_config(local, -1, IEEE80211_CONF_CHANGE_LISTEN_INTERVAL |
           IEEE80211_CONF_CHANGE_MONITOR |
           IEEE80211_CONF_CHANGE_PS |
           IEEE80211_CONF_CHANGE_RETRY_LIMITS |
           IEEE80211_CONF_CHANGE_IDLE);

 ieee80211_configure_filter(local);

 /* Finally also reconfigure all the BSS information */
 list_for_each_entry(sdata, &local->interfaces, list) {
  /* common change flags for all interface types - link only */
  u64 changed = BSS_CHANGED_ERP_CTS_PROT |
         BSS_CHANGED_ERP_PREAMBLE |
         BSS_CHANGED_ERP_SLOT |
         BSS_CHANGED_HT |
         BSS_CHANGED_BASIC_RATES |
         BSS_CHANGED_BEACON_INT |
         BSS_CHANGED_BSSID |
         BSS_CHANGED_CQM |
         BSS_CHANGED_QOS |
         BSS_CHANGED_TXPOWER |
         BSS_CHANGED_MCAST_RATE;
  struct ieee80211_link_data *link = NULL;
  unsigned int link_id;
  u32 active_links = 0;

  if (!ieee80211_sdata_running(sdata))
   continue;

  if (ieee80211_vif_is_mld(&sdata->vif)) {
   struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS] = {
    [0] = &sdata->vif.bss_conf,
   };

   if (sdata->vif.type == NL80211_IFTYPE_STATION) {
    /* start with a single active link */
    active_links = sdata->vif.active_links;
    link_id = ffs(active_links) - 1;
    sdata->vif.active_links = BIT(link_id);
   }

   drv_change_vif_links(local, sdata, 0,
          sdata->vif.active_links,
          old);
  }

  sdata->restart_active_links = active_links;

  for (link_id = 0;
       link_id < ARRAY_SIZE(sdata->vif.link_conf);
       link_id++) {
   if (!ieee80211_vif_link_active(&sdata->vif, link_id))
    continue;

   link = sdata_dereference(sdata->link[link_id], sdata);
   if (!link)
    continue;

   ieee80211_assign_chanctx(local, sdata, link);
  }

  switch (sdata->vif.type) {
  case NL80211_IFTYPE_AP_VLAN:
  case NL80211_IFTYPE_MONITOR:
   break;
  case NL80211_IFTYPE_ADHOC:
   if (sdata->vif.cfg.ibss_joined)
    WARN_ON(drv_join_ibss(local, sdata));
   fallthrough;
  default:
   ieee80211_reconfig_stations(sdata);
   fallthrough;
  case NL80211_IFTYPE_AP: /* AP stations are handled later */
   for (i = 0; i < IEEE80211_NUM_ACS; i++)
    drv_conf_tx(local, &sdata->deflink, i,
         &sdata->deflink.tx_conf[i]);
   break;
  }

  if (sdata->vif.bss_conf.mu_mimo_owner)
   changed |= BSS_CHANGED_MU_GROUPS;

  if (!ieee80211_vif_is_mld(&sdata->vif))
   changed |= BSS_CHANGED_IDLE;

  switch (sdata->vif.type) {
  case NL80211_IFTYPE_STATION:
   if (!ieee80211_vif_is_mld(&sdata->vif)) {
    changed |= BSS_CHANGED_ASSOC |
        BSS_CHANGED_ARP_FILTER |
        BSS_CHANGED_PS;

    /* Re-send beacon info report to the driver */
    if (sdata->deflink.u.mgd.have_beacon)
     changed |= BSS_CHANGED_BEACON_INFO;

    if (sdata->vif.bss_conf.max_idle_period ||
        sdata->vif.bss_conf.protected_keep_alive)
     changed |= BSS_CHANGED_KEEP_ALIVE;

    ieee80211_bss_info_change_notify(sdata,
         changed);
   } else if (!WARN_ON(!link)) {
    ieee80211_link_info_change_notify(sdata, link,
          changed);
    changed = BSS_CHANGED_ASSOC |
       BSS_CHANGED_IDLE |
       BSS_CHANGED_PS |
       BSS_CHANGED_ARP_FILTER;
    ieee80211_vif_cfg_change_notify(sdata, changed);
   }
   break;
  case NL80211_IFTYPE_OCB:
   changed |= BSS_CHANGED_OCB;
   ieee80211_bss_info_change_notify(sdata, changed);
   break;
  case NL80211_IFTYPE_ADHOC:
   changed |= BSS_CHANGED_IBSS;
   fallthrough;
  case NL80211_IFTYPE_AP:
   changed |= BSS_CHANGED_P2P_PS;

   if (ieee80211_vif_is_mld(&sdata->vif))
    ieee80211_vif_cfg_change_notify(sdata,
        BSS_CHANGED_SSID);
   else
    changed |= BSS_CHANGED_SSID;

   if (sdata->vif.bss_conf.ftm_responder == 1 &&
       wiphy_ext_feature_isset(sdata->local->hw.wiphy,
     NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
    changed |= BSS_CHANGED_FTM_RESPONDER;

   if (sdata->vif.type == NL80211_IFTYPE_AP) {
    changed |= BSS_CHANGED_AP_PROBE_RESP;

    if (ieee80211_vif_is_mld(&sdata->vif)) {
     ieee80211_reconfig_ap_links(local,
            sdata,
            changed);
     break;
    }

    if (rcu_access_pointer(sdata->deflink.u.ap.beacon))
     drv_start_ap(local, sdata,
           sdata->deflink.conf);
   }
   fallthrough;
  case NL80211_IFTYPE_MESH_POINT:
   if (sdata->vif.bss_conf.enable_beacon) {
    changed |= BSS_CHANGED_BEACON |
        BSS_CHANGED_BEACON_ENABLED;
    ieee80211_bss_info_change_notify(sdata, changed);
   }
   break;
  case NL80211_IFTYPE_NAN:
   res = ieee80211_reconfig_nan(sdata);
   if (res < 0) {
    ieee80211_handle_reconfig_failure(local);
    return res;
   }
   break;
  case NL80211_IFTYPE_AP_VLAN:
  case NL80211_IFTYPE_MONITOR:
  case NL80211_IFTYPE_P2P_DEVICE:
   /* nothing to do */
   break;
  case NL80211_IFTYPE_UNSPECIFIED:
  case NUM_NL80211_IFTYPES:
  case NL80211_IFTYPE_P2P_CLIENT:
  case NL80211_IFTYPE_P2P_GO:
  case NL80211_IFTYPE_WDS:
   WARN_ON(1);
   break;
  }
 }

 ieee80211_recalc_ps(local);

 /*
 * The sta might be in psm against the ap (e.g. because
 * this was the state before a hw restart), so we
 * explicitly send a null packet in order to make sure
 * it'll sync against the ap (and get out of psm).
 */

 if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) {
  list_for_each_entry(sdata, &local->interfaces, list) {
   if (sdata->vif.type != NL80211_IFTYPE_STATION)
    continue;
   if (!sdata->u.mgd.associated)
    continue;

   ieee80211_send_nullfunc(local, sdata, false);
  }
 }

 /* APs are now beaconing, add back stations */
 list_for_each_entry(sdata, &local->interfaces, list) {
  if (!ieee80211_sdata_running(sdata))
   continue;

  switch (sdata->vif.type) {
  case NL80211_IFTYPE_AP_VLAN:
  case NL80211_IFTYPE_AP:
   ieee80211_reconfig_stations(sdata);
   break;
  default:
   break;
  }
 }

 /* add back keys */
 list_for_each_entry(sdata, &local->interfaces, list)
  ieee80211_reenable_keys(sdata);

 /* re-enable multi-link for client interfaces */
 list_for_each_entry(sdata, &local->interfaces, list) {
  if (sdata->restart_active_links)
   ieee80211_set_active_links(&sdata->vif,
         sdata->restart_active_links);
  /*
 * If a link switch was scheduled before the restart, and ran
 * before reconfig, it will do nothing, so re-schedule.
 */

  if (sdata->desired_active_links)
   wiphy_work_queue(sdata->local->hw.wiphy,
      &sdata->activate_links_work);
 }

 /* Reconfigure sched scan if it was interrupted by FW restart */
 sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
      lockdep_is_held(&local->hw.wiphy->mtx));
 sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
      lockdep_is_held(&local->hw.wiphy->mtx));
 if (sched_scan_sdata && sched_scan_req)
  /*
 * Sched scan stopped, but we don't want to report it. Instead,
 * we're trying to reschedule. However, if more than one scan
 * plan was set, we cannot reschedule since we don't know which
 * scan plan was currently running (and some scan plans may have
 * already finished).
 */

  if (sched_scan_req->n_scan_plans > 1 ||
      __ieee80211_request_sched_scan_start(sched_scan_sdata,
        sched_scan_req)) {
   RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
   RCU_INIT_POINTER(local->sched_scan_req, NULL);
   sched_scan_stopped = true;
  }

 if (sched_scan_stopped)
  cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0);

 wake_up:
 /*
 * Clear the WLAN_STA_BLOCK_BA flag so new aggregation
 * sessions can be established after a resume.
 *
 * Also tear down aggregation sessions since reconfiguring
 * them in a hardware restart scenario is not easily done
 * right now, and the hardware will have lost information
 * about the sessions, but we and the AP still think they
 * are active. This is really a workaround though.
 */

 if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) {
  list_for_each_entry(sta, &local->sta_list, list) {
   if (!local->resuming)
    ieee80211_sta_tear_down_BA_sessions(
      sta, AGG_STOP_LOCAL_REQUEST);
   clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
  }
 }

 /*
 * If this is for hw restart things are still running.
 * We may want to change that later, however.
 */

 if (local->open_count && (!suspended || reconfig_due_to_wowlan))
  drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);

 if (local->in_reconfig) {
  in_reconfig = local->in_reconfig;
  local->in_reconfig = false;
  barrier();

  ieee80211_reconfig_roc(local);

  /* Requeue all works */
  list_for_each_entry(sdata, &local->interfaces, list) {
   if (ieee80211_sdata_running(sdata))
    wiphy_work_queue(local->hw.wiphy, &sdata->work);
  }
 }

 ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
     IEEE80211_QUEUE_STOP_REASON_SUSPEND,
     false);

 if (in_reconfig) {
  list_for_each_entry(sdata, &local->interfaces, list) {
   if (!ieee80211_sdata_running(sdata))
    continue;
   if (sdata->vif.type == NL80211_IFTYPE_STATION)
    ieee80211_sta_restart(sdata);
  }
 }

 if (local->virt_monitors > 0 &&
     local->virt_monitors == local->open_count)
  ieee80211_add_virtual_monitor(local);

 if (!suspended)
  return 0;

#ifdef CONFIG_PM
 /* first set suspended false, then resuming */
 local->suspended = false;
 mb();
 local->resuming = false;

 ieee80211_flush_completed_scan(local, false);

 if (local->open_count && !reconfig_due_to_wowlan)
  drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);

 list_for_each_entry(sdata, &local->interfaces, list) {
  if (!ieee80211_sdata_running(sdata))
   continue;
  if (sdata->vif.type == NL80211_IFTYPE_STATION)
   ieee80211_sta_restart(sdata);
 }

 mod_timer(&local->sta_cleanup, jiffies + 1);
#else
 WARN_ON(1);
#endif

 return 0;
}

static void ieee80211_reconfig_disconnect(struct ieee80211_vif *vif, u8 flag)
{
 struct ieee80211_sub_if_data *sdata;
 struct ieee80211_local *local;
 struct ieee80211_key *key;

 if (WARN_ON(!vif))
  return;

 sdata = vif_to_sdata(vif);
 local = sdata->local;

 lockdep_assert_wiphy(local->hw.wiphy);

 if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_RESUME &&
      !local->resuming))
  return;

 if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_HW_RESTART &&
      !local->in_reconfig))
  return;

 if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
  return;

 sdata->flags |= flag;

 list_for_each_entry(key, &sdata->key_list, list)
  key->flags |= KEY_FLAG_TAINTED;
}

void ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif)
{
 ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_HW_RESTART);
}
EXPORT_SYMBOL_GPL(ieee80211_hw_restart_disconnect);

void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
{
 ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_RESUME);
}
EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);

void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata,
      struct ieee80211_link_data *link)
{
 struct ieee80211_local *local = sdata->local;
 struct ieee80211_chanctx_conf *chanctx_conf;
 struct ieee80211_chanctx *chanctx;

 lockdep_assert_wiphy(local->hw.wiphy);

 chanctx_conf = rcu_dereference_protected(link->conf->chanctx_conf,
       lockdep_is_held(&local->hw.wiphy->mtx));

 /*
 * This function can be called from a work, thus it may be possible
 * that the chanctx_conf is removed (due to a disconnection, for
 * example).
 * So nothing should be done in such case.
 */

 if (!chanctx_conf)
  return;

 chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
 ieee80211_recalc_smps_chanctx(local, chanctx);
}

void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata,
      int link_id)
{
 struct ieee80211_local *local = sdata->local;
 struct ieee80211_chanctx_conf *chanctx_conf;
 struct ieee80211_chanctx *chanctx;
 int i;

 lockdep_assert_wiphy(local->hw.wiphy);

 for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) {
  struct ieee80211_bss_conf *bss_conf;

  if (link_id >= 0 && link_id != i)
   continue;

  rcu_read_lock();
  bss_conf = rcu_dereference(sdata->vif.link_conf[i]);
  if (!bss_conf) {
   rcu_read_unlock();
   continue;
  }

  chanctx_conf = rcu_dereference_protected(bss_conf->chanctx_conf,
        lockdep_is_held(&local->hw.wiphy->mtx));
  /*
 * Since we hold the wiphy mutex (checked above)
 * we can take the chanctx_conf pointer out of the
 * RCU critical section, it cannot go away without
 * the mutex. Just the way we reached it could - in
 * theory - go away, but we don't really care and
 * it really shouldn't happen anyway.
 */

  rcu_read_unlock();

  if (!chanctx_conf)
   return;

  chanctx = container_of(chanctx_conf, struct ieee80211_chanctx,
           conf);
  ieee80211_recalc_chanctx_min_def(local, chanctx, NULL, false);
 }
}

size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
{
 size_t pos = offset;

 while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC)
  pos += 2 + ies[pos + 1];

 return pos;
}

u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
         u16 cap)
{
 __le16 tmp;

 *pos++ = WLAN_EID_HT_CAPABILITY;
 *pos++ = sizeof(struct ieee80211_ht_cap);
 memset(pos, 0, sizeof(struct ieee80211_ht_cap));

 /* capability flags */
 tmp = cpu_to_le16(cap);
 memcpy(pos, &tmp, sizeof(u16));
 pos += sizeof(u16);

 /* AMPDU parameters */
 *pos++ = ht_cap->ampdu_factor |
   (ht_cap->ampdu_density <<
   IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);

 /* MCS set */
 memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
 pos += sizeof(ht_cap->mcs);

 /* extended capabilities */
 pos += sizeof(__le16);

 /* BF capabilities */
 pos += sizeof(__le32);

 /* antenna selection */
 pos += sizeof(u8);

 return pos;
}

u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
          u32 cap)
{
 __le32 tmp;

 *pos++ = WLAN_EID_VHT_CAPABILITY;
 *pos++ = sizeof(struct ieee80211_vht_cap);
 memset(pos, 0, sizeof(struct ieee80211_vht_cap));

 /* capability flags */
 tmp = cpu_to_le32(cap);
 memcpy(pos, &tmp, sizeof(u32));
 pos += sizeof(u32);

 /* VHT MCS set */
 memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
 pos += sizeof(vht_cap->vht_mcs);

 return pos;
}

/* this may return more than ieee80211_put_he_6ghz_cap() will need */
u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata)
{
 const struct ieee80211_sta_he_cap *he_cap;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=94 G=92

¤ Dauer der Verarbeitung: 0.53 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.