| 1 | |
| 2 | |
| 3 | * Copyright 2002-2005, Instant802 Networks, Inc.
|
| 4 | * Copyright 2005-2006, Devicescape Software, Inc.
|
| 5 | * Copyright 2006-2007 Jiri Benc <[email protected]>
|
| 6 | * Copyright 2007 Johannes Berg <[email protected]>
|
| 7 | * Copyright 2013-2014 Intel Mobile Communications GmbH
|
| 8 | * Copyright (C) 2015-2017 Intel Deutschland GmbH
|
| 9 | * Copyright (C) 2018-2026 Intel Corporation
|
| 10 | *
|
| 11 | * utilities for mac80211
|
| 12 |
|
| 13 |
|
| 14 | #include <net/mac80211.h>
|
| 15 | #include <linux/netdevice.h>
|
| 16 | #include <linux/export.h>
|
| 17 | #include <linux/types.h>
|
| 18 | #include <linux/slab.h>
|
| 19 | #include <linux/skbuff.h>
|
| 20 | #include <linux/etherdevice.h>
|
| 21 | #include <linux/if_arp.h>
|
| 22 | #include <linux/bitmap.h>
|
| 23 | #include <linux/crc32.h>
|
| 24 | #include <net/net_namespace.h>
|
| 25 | #include <net/cfg80211.h>
|
| 26 | #include <net/rtnetlink.h>
|
| 27 | #include <kunit/visibility.h>
|
| 28 |
|
| 29 | #include "ieee80211_i.h"
|
| 30 | #include "driver-ops.h"
|
| 31 | #include "rate.h"
|
| 32 | #include "mesh.h"
|
| 33 | #include "wme.h"
|
| 34 | #include "led.h"
|
| 35 | #include "wep.h"
|
| 36 |
|
| 37 |
|
| 38 | const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid;
|
| 39 |
|
| 40 | struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
|
| 41 | {
|
| 42 | struct ieee80211_local *local;
|
| 43 |
|
| 44 | local = wiphy_priv(wiphy);
|
| 45 | return &local->hw;
|
| 46 | }
|
| 47 | EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
|
| 48 |
|
| 49 | const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited = {
|
| 50 | .mode = IEEE80211_CONN_MODE_EHT,
|
| 51 | .bw_limit = IEEE80211_CONN_BW_LIMIT_320,
|
| 52 | };
|
| 53 |
|
| 54 | u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
|
| 55 | enum nl80211_iftype type)
|
| 56 | {
|
| 57 | __le16 fc = hdr->frame_control;
|
| 58 |
|
| 59 | if (ieee80211_is_data(fc)) {
|
| 60 | if (len < 24)
|
| 61 | return NULL;
|
| 62 |
|
| 63 | if (ieee80211_has_a4(fc))
|
| 64 | return NULL;
|
| 65 | if (ieee80211_has_tods(fc))
|
| 66 | return hdr->addr1;
|
| 67 | if (ieee80211_has_fromds(fc))
|
| 68 | return hdr->addr2;
|
| 69 |
|
| 70 | return hdr->addr3;
|
| 71 | }
|
| 72 |
|
| 73 | if (ieee80211_is_s1g_beacon(fc)) {
|
| 74 | struct ieee80211_ext *ext = (void *) hdr;
|
| 75 |
|
| 76 | if (len < offsetofend(struct ieee80211_ext, u.s1g_beacon.sa))
|
| 77 | return NULL;
|
| 78 |
|
| 79 | return ext->u.s1g_beacon.sa;
|
| 80 | }
|
| 81 |
|
| 82 | if (ieee80211_is_mgmt(fc)) {
|
| 83 | if (len < 24)
|
| 84 | return NULL;
|
| 85 | return hdr->addr3;
|
| 86 | }
|
| 87 |
|
| 88 | if (ieee80211_is_ctl(fc)) {
|
| 89 | if (ieee80211_is_pspoll(fc))
|
| 90 | return hdr->addr1;
|
| 91 |
|
| 92 | if (ieee80211_is_back_req(fc)) {
|
| 93 | switch (type) {
|
| 94 | case NL80211_IFTYPE_STATION:
|
| 95 | return hdr->addr2;
|
| 96 | case NL80211_IFTYPE_AP:
|
| 97 | case NL80211_IFTYPE_AP_VLAN:
|
| 98 | return hdr->addr1;
|
| 99 | default:
|
| 100 | break;
|
| 101 | }
|
| 102 | }
|
| 103 | }
|
| 104 |
|
| 105 | return NULL;
|
| 106 | }
|
| 107 |
|
| 108 | void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
|
| 109 | {
|
| 110 | struct sk_buff *skb;
|
| 111 | struct ieee80211_hdr *hdr;
|
| 112 |
|
| 113 | skb_queue_walk(&tx->skbs, skb) {
|
| 114 | hdr = (struct ieee80211_hdr *) skb->data;
|
| 115 | hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
| 116 | }
|
| 117 | }
|
| 118 |
|
| 119 | int ieee80211_frame_duration(enum nl80211_band band, size_t len,
|
| 120 | int rate, int erp, int short_preamble)
|
| 121 | {
|
| 122 | int dur;
|
| 123 |
|
| 124 | |
| 125 | * integer if it includes a fractional microsecond) to send frame of
|
| 126 | * len bytes (does not include FCS) at the given rate. Duration will
|
| 127 | * also include SIFS.
|
| 128 | *
|
| 129 | * rate is in 100 kbps, so divident is multiplied by 10 in the
|
| 130 | * DIV_ROUND_UP() operations.
|
| 131 |
|
| 132 |
|
| 133 | if (band == NL80211_BAND_5GHZ || erp) {
|
| 134 | |
| 135 | * OFDM:
|
| 136 | *
|
| 137 | * N_DBPS = DATARATE x 4
|
| 138 | * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
|
| 139 | * (16 = SIGNAL time, 6 = tail bits)
|
| 140 | * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
|
| 141 | *
|
| 142 | * T_SYM = 4 usec
|
| 143 | * 802.11a - 18.5.2: aSIFSTime = 16 usec
|
| 144 | * 802.11g - 19.8.4: aSIFSTime = 10 usec +
|
| 145 | * signal ext = 6 usec
|
| 146 |
|
| 147 | dur = 16;
|
| 148 | dur += 16;
|
| 149 | dur += 4;
|
| 150 |
|
| 151 | |
| 152 | * don't apply divisor again.
|
| 153 |
|
| 154 | dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
|
| 155 | 4 * rate);
|
| 156 | } else {
|
| 157 | |
| 158 | * 802.11b or 802.11g with 802.11b compatibility:
|
| 159 | * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
|
| 160 | * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
|
| 161 | *
|
| 162 | * 802.11 (DS): 15.3.3, 802.11b: 18.3.4
|
| 163 | * aSIFSTime = 10 usec
|
| 164 | * aPreambleLength = 144 usec or 72 usec with short preamble
|
| 165 | * aPLCPHeaderLength = 48 usec or 24 usec with short preamble
|
| 166 |
|
| 167 | dur = 10;
|
| 168 | dur += short_preamble ? (72 + 24) : (144 + 48);
|
| 169 |
|
| 170 | dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
|
| 171 | }
|
| 172 |
|
| 173 | return dur;
|
| 174 | }
|
| 175 |
|
| 176 |
|
| 177 | __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
|
| 178 | struct ieee80211_vif *vif,
|
| 179 | enum nl80211_band band,
|
| 180 | size_t frame_len,
|
| 181 | struct ieee80211_rate *rate)
|
| 182 | {
|
| 183 | struct ieee80211_sub_if_data *sdata;
|
| 184 | u16 dur;
|
| 185 | int erp;
|
| 186 | bool short_preamble = false;
|
| 187 |
|
| 188 | erp = 0;
|
| 189 | if (vif) {
|
| 190 | sdata = vif_to_sdata(vif);
|
| 191 | short_preamble = sdata->vif.bss_conf.use_short_preamble;
|
| 192 | if (sdata->deflink.operating_11g_mode)
|
| 193 | erp = rate->flags & IEEE80211_RATE_ERP_G;
|
| 194 | }
|
| 195 |
|
| 196 | dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
|
| 197 | short_preamble);
|
| 198 |
|
| 199 | return cpu_to_le16(dur);
|
| 200 | }
|
| 201 | EXPORT_SYMBOL(ieee80211_generic_frame_duration);
|
| 202 |
|
| 203 | __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
|
| 204 | struct ieee80211_vif *vif, size_t frame_len,
|
| 205 | const struct ieee80211_tx_info *frame_txctl)
|
| 206 | {
|
| 207 | struct ieee80211_local *local = hw_to_local(hw);
|
| 208 | struct ieee80211_rate *rate;
|
| 209 | struct ieee80211_sub_if_data *sdata;
|
| 210 | bool short_preamble;
|
| 211 | int erp, bitrate;
|
| 212 | u16 dur;
|
| 213 | struct ieee80211_supported_band *sband;
|
| 214 |
|
| 215 | sband = local->hw.wiphy->bands[frame_txctl->band];
|
| 216 |
|
| 217 | short_preamble = false;
|
| 218 |
|
| 219 | rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
|
| 220 |
|
| 221 | erp = 0;
|
| 222 | if (vif) {
|
| 223 | sdata = vif_to_sdata(vif);
|
| 224 | short_preamble = sdata->vif.bss_conf.use_short_preamble;
|
| 225 | if (sdata->deflink.operating_11g_mode)
|
| 226 | erp = rate->flags & IEEE80211_RATE_ERP_G;
|
| 227 | }
|
| 228 |
|
| 229 | bitrate = rate->bitrate;
|
| 230 |
|
| 231 |
|
| 232 | dur = ieee80211_frame_duration(sband->band, 10, bitrate,
|
| 233 | erp, short_preamble);
|
| 234 |
|
| 235 | dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
|
| 236 | erp, short_preamble);
|
| 237 |
|
| 238 | dur += ieee80211_frame_duration(sband->band, 10, bitrate,
|
| 239 | erp, short_preamble);
|
| 240 |
|
| 241 | return cpu_to_le16(dur);
|
| 242 | }
|
| 243 | EXPORT_SYMBOL(ieee80211_rts_duration);
|
| 244 |
|
| 245 | __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
|
| 246 | struct ieee80211_vif *vif,
|
| 247 | size_t frame_len,
|
| 248 | const struct ieee80211_tx_info *frame_txctl)
|
| 249 | {
|
| 250 | struct ieee80211_local *local = hw_to_local(hw);
|
| 251 | struct ieee80211_rate *rate;
|
| 252 | struct ieee80211_sub_if_data *sdata;
|
| 253 | bool short_preamble;
|
| 254 | int erp, bitrate;
|
| 255 | u16 dur;
|
| 256 | struct ieee80211_supported_band *sband;
|
| 257 |
|
| 258 | sband = local->hw.wiphy->bands[frame_txctl->band];
|
| 259 |
|
| 260 | short_preamble = false;
|
| 261 |
|
| 262 | rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
|
| 263 | erp = 0;
|
| 264 | if (vif) {
|
| 265 | sdata = vif_to_sdata(vif);
|
| 266 | short_preamble = sdata->vif.bss_conf.use_short_preamble;
|
| 267 | if (sdata->deflink.operating_11g_mode)
|
| 268 | erp = rate->flags & IEEE80211_RATE_ERP_G;
|
| 269 | }
|
| 270 |
|
| 271 | bitrate = rate->bitrate;
|
| 272 |
|
| 273 |
|
| 274 | dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
|
| 275 | erp, short_preamble);
|
| 276 | if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
|
| 277 |
|
| 278 | dur += ieee80211_frame_duration(sband->band, 10, bitrate,
|
| 279 | erp, short_preamble);
|
| 280 | }
|
| 281 |
|
| 282 | return cpu_to_le16(dur);
|
| 283 | }
|
| 284 | EXPORT_SYMBOL(ieee80211_ctstoself_duration);
|
| 285 |
|
| 286 | static void wake_tx_push_queue(struct ieee80211_local *local,
|
| 287 | struct ieee80211_sub_if_data *sdata,
|
| 288 | struct ieee80211_txq *queue)
|
| 289 | {
|
| 290 | struct ieee80211_tx_control control = {
|
| 291 | .sta = queue->sta,
|
| 292 | };
|
| 293 | struct sk_buff *skb;
|
| 294 |
|
| 295 | while (1) {
|
| 296 | skb = ieee80211_tx_dequeue(&local->hw, queue);
|
| 297 | if (!skb)
|
| 298 | break;
|
| 299 |
|
| 300 | drv_tx(local, &control, skb);
|
| 301 | }
|
| 302 | }
|
| 303 |
|
| 304 |
|
| 305 | void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
|
| 306 | struct ieee80211_txq *txq)
|
| 307 | {
|
| 308 | struct ieee80211_local *local = hw_to_local(hw);
|
| 309 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
|
| 310 | struct ieee80211_txq *queue;
|
| 311 |
|
| 312 | spin_lock(&local->handle_wake_tx_queue_lock);
|
| 313 |
|
| 314 |
|
| 315 | ieee80211_txq_schedule_start(hw, txq->ac);
|
| 316 | while ((queue = ieee80211_next_txq(hw, txq->ac))) {
|
| 317 | wake_tx_push_queue(local, sdata, queue);
|
| 318 | ieee80211_return_txq(hw, queue, false);
|
| 319 | }
|
| 320 | ieee80211_txq_schedule_end(hw, txq->ac);
|
| 321 | spin_unlock(&local->handle_wake_tx_queue_lock);
|
| 322 | }
|
| 323 | EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue);
|
| 324 |
|
| 325 | static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
|
| 326 | {
|
| 327 | struct ieee80211_local *local = sdata->local;
|
| 328 | struct ieee80211_vif *vif = &sdata->vif;
|
| 329 | struct fq *fq = &local->fq;
|
| 330 | struct ps_data *ps = NULL;
|
| 331 | struct txq_info *txqi = NULL;
|
| 332 | struct sta_info *sta;
|
| 333 | int i;
|
| 334 |
|
| 335 | local_bh_disable();
|
| 336 | spin_lock(&fq->lock);
|
| 337 |
|
| 338 | if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
|
| 339 | goto out;
|
| 340 |
|
| 341 | if (sdata->vif.type == NL80211_IFTYPE_AP)
|
| 342 | ps = &sdata->bss->ps;
|
| 343 |
|
| 344 | list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
| 345 | if (sdata != sta->sdata)
|
| 346 | continue;
|
| 347 |
|
| 348 | for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
|
| 349 | struct ieee80211_txq *txq = sta->sta.txq[i];
|
| 350 | struct txq_info *sta_txqi;
|
| 351 |
|
| 352 | if (!txq)
|
| 353 | continue;
|
| 354 |
|
| 355 | sta_txqi = to_txq_info(txq);
|
| 356 |
|
| 357 | if (ac != txq->ac)
|
| 358 | continue;
|
| 359 |
|
| 360 | if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY,
|
| 361 | &sta_txqi->flags))
|
| 362 | continue;
|
| 363 |
|
| 364 | spin_unlock(&fq->lock);
|
| 365 | drv_wake_tx_queue(local, sta_txqi);
|
| 366 | spin_lock(&fq->lock);
|
| 367 | }
|
| 368 | }
|
| 369 |
|
| 370 | if (vif->txq) {
|
| 371 | txqi = to_txq_info(vif->txq);
|
| 372 |
|
| 373 |
|
| 374 | WARN_ON_ONCE(vif->txq_mgmt);
|
| 375 |
|
| 376 | if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ||
|
| 377 | (ps && atomic_read(&ps->num_sta_ps)) ||
|
| 378 | ac != vif->txq->ac)
|
| 379 | txqi = NULL;
|
| 380 | } else if (vif->txq_mgmt) {
|
| 381 | txqi = to_txq_info(vif->txq_mgmt);
|
| 382 |
|
| 383 | if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ||
|
| 384 | ac != vif->txq_mgmt->ac)
|
| 385 | txqi = NULL;
|
| 386 | }
|
| 387 |
|
| 388 | spin_unlock(&fq->lock);
|
| 389 |
|
| 390 | if (txqi)
|
| 391 | drv_wake_tx_queue(local, txqi);
|
| 392 |
|
| 393 | local_bh_enable();
|
| 394 | return;
|
| 395 | out:
|
| 396 | spin_unlock(&fq->lock);
|
| 397 | local_bh_enable();
|
| 398 | }
|
| 399 |
|
| 400 | static void
|
| 401 | __releases(&local->queue_stop_reason_lock)
|
| 402 | __acquires(&local->queue_stop_reason_lock)
|
| 403 | _ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags)
|
| 404 | {
|
| 405 | struct ieee80211_sub_if_data *sdata;
|
| 406 | int n_acs = IEEE80211_NUM_ACS;
|
| 407 | int i;
|
| 408 |
|
| 409 | rcu_read_lock();
|
| 410 |
|
| 411 | if (local->hw.queues < IEEE80211_NUM_ACS)
|
| 412 | n_acs = 1;
|
| 413 |
|
| 414 | for (i = 0; i < local->hw.queues; i++) {
|
| 415 | if (local->queue_stop_reasons[i])
|
| 416 | continue;
|
| 417 |
|
| 418 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags);
|
| 419 | list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
| 420 | int ac;
|
| 421 |
|
| 422 | for (ac = 0; ac < n_acs; ac++) {
|
| 423 | int ac_queue = sdata->vif.hw_queue[ac];
|
| 424 |
|
| 425 | if (ac_queue == i ||
|
| 426 | sdata->vif.cab_queue == i)
|
| 427 | __ieee80211_wake_txqs(sdata, ac);
|
| 428 | }
|
| 429 | }
|
| 430 | spin_lock_irqsave(&local->queue_stop_reason_lock, *flags);
|
| 431 | }
|
| 432 |
|
| 433 | rcu_read_unlock();
|
| 434 | }
|
| 435 |
|
| 436 | void ieee80211_wake_txqs(struct tasklet_struct *t)
|
| 437 | {
|
| 438 | struct ieee80211_local *local = from_tasklet(local, t,
|
| 439 | wake_txqs_tasklet);
|
| 440 | unsigned long flags;
|
| 441 |
|
| 442 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
| 443 | _ieee80211_wake_txqs(local, &flags);
|
| 444 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
| 445 | }
|
| 446 |
|
| 447 | static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
|
| 448 | enum queue_stop_reason reason,
|
| 449 | bool refcounted,
|
| 450 | unsigned long *flags)
|
| 451 | {
|
| 452 | struct ieee80211_local *local = hw_to_local(hw);
|
| 453 |
|
| 454 | if (WARN_ON(queue >= hw->queues))
|
| 455 | return;
|
| 456 |
|
| 457 | if (!test_bit(reason, &local->queue_stop_reasons[queue]))
|
| 458 | return;
|
| 459 |
|
| 460 | if (!refcounted) {
|
| 461 | local->q_stop_reasons[queue][reason] = 0;
|
| 462 | } else {
|
| 463 | local->q_stop_reasons[queue][reason]--;
|
| 464 | if (WARN_ON(local->q_stop_reasons[queue][reason] < 0))
|
| 465 | local->q_stop_reasons[queue][reason] = 0;
|
| 466 | }
|
| 467 |
|
| 468 | if (local->q_stop_reasons[queue][reason] == 0)
|
| 469 | __clear_bit(reason, &local->queue_stop_reasons[queue]);
|
| 470 |
|
| 471 | trace_wake_queue(local, queue, reason,
|
| 472 | local->q_stop_reasons[queue][reason]);
|
| 473 |
|
| 474 | if (local->queue_stop_reasons[queue] != 0)
|
| 475 |
|
| 476 | return;
|
| 477 |
|
| 478 | if (!skb_queue_empty(&local->pending[queue]))
|
| 479 | tasklet_schedule(&local->tx_pending_tasklet);
|
| 480 |
|
| 481 | |
| 482 | * Calling _ieee80211_wake_txqs here can be a problem because it may
|
| 483 | * release queue_stop_reason_lock which has been taken by
|
| 484 | * __ieee80211_wake_queue's caller. It is certainly not very nice to
|
| 485 | * release someone's lock, but it is fine because all the callers of
|
| 486 | * __ieee80211_wake_queue call it right before releasing the lock.
|
| 487 |
|
| 488 | if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
|
| 489 | tasklet_schedule(&local->wake_txqs_tasklet);
|
| 490 | else
|
| 491 | _ieee80211_wake_txqs(local, flags);
|
| 492 | }
|
| 493 |
|
| 494 | void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
|
| 495 | enum queue_stop_reason reason,
|
| 496 | bool refcounted)
|
| 497 | {
|
| 498 | struct ieee80211_local *local = hw_to_local(hw);
|
| 499 | unsigned long flags;
|
| 500 |
|
| 501 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
| 502 | __ieee80211_wake_queue(hw, queue, reason, refcounted, &flags);
|
| 503 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
| 504 | }
|
| 505 |
|
| 506 | void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
|
| 507 | {
|
| 508 | ieee80211_wake_queue_by_reason(hw, queue,
|
| 509 | IEEE80211_QUEUE_STOP_REASON_DRIVER,
|
| 510 | false);
|
| 511 | }
|
| 512 | EXPORT_SYMBOL(ieee80211_wake_queue);
|
| 513 |
|
| 514 | static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
|
| 515 | enum queue_stop_reason reason,
|
| 516 | bool refcounted)
|
| 517 | {
|
| 518 | struct ieee80211_local *local = hw_to_local(hw);
|
| 519 |
|
| 520 | if (WARN_ON(queue >= hw->queues))
|
| 521 | return;
|
| 522 |
|
| 523 | if (!refcounted)
|
| 524 | local->q_stop_reasons[queue][reason] = 1;
|
| 525 | else
|
| 526 | local->q_stop_reasons[queue][reason]++;
|
| 527 |
|
| 528 | trace_stop_queue(local, queue, reason,
|
| 529 | local->q_stop_reasons[queue][reason]);
|
| 530 |
|
| 531 | set_bit(reason, &local->queue_stop_reasons[queue]);
|
| 532 | }
|
| 533 |
|
| 534 | void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
|
| 535 | enum queue_stop_reason reason,
|
| 536 | bool refcounted)
|
| 537 | {
|
| 538 | struct ieee80211_local *local = hw_to_local(hw);
|
| 539 | unsigned long flags;
|
| 540 |
|
| 541 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
| 542 | __ieee80211_stop_queue(hw, queue, reason, refcounted);
|
| 543 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
| 544 | }
|
| 545 |
|
| 546 | void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
|
| 547 | {
|
| 548 | ieee80211_stop_queue_by_reason(hw, queue,
|
| 549 | IEEE80211_QUEUE_STOP_REASON_DRIVER,
|
| 550 | false);
|
| 551 | }
|
| 552 | EXPORT_SYMBOL(ieee80211_stop_queue);
|
| 553 |
|
| 554 | void ieee80211_add_pending_skb(struct ieee80211_local *local,
|
| 555 | struct sk_buff *skb)
|
| 556 | {
|
| 557 | struct ieee80211_hw *hw = &local->hw;
|
| 558 | unsigned long flags;
|
| 559 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
| 560 | int queue = info->hw_queue;
|
| 561 |
|
| 562 | if (WARN_ON(!info->control.vif)) {
|
| 563 | ieee80211_free_txskb(&local->hw, skb);
|
| 564 | return;
|
| 565 | }
|
| 566 |
|
| 567 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
| 568 | __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
| 569 | false);
|
| 570 | __skb_queue_tail(&local->pending[queue], skb);
|
| 571 | __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
| 572 | false, &flags);
|
| 573 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
| 574 | }
|
| 575 |
|
| 576 | void ieee80211_add_pending_skbs(struct ieee80211_local *local,
|
| 577 | struct sk_buff_head *skbs)
|
| 578 | {
|
| 579 | struct ieee80211_hw *hw = &local->hw;
|
| 580 | struct sk_buff *skb;
|
| 581 | unsigned long flags;
|
| 582 | int queue, i;
|
| 583 |
|
| 584 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
| 585 | while ((skb = skb_dequeue(skbs))) {
|
| 586 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
| 587 |
|
| 588 | if (WARN_ON(!info->control.vif)) {
|
| 589 | ieee80211_free_txskb(&local->hw, skb);
|
| 590 | continue;
|
| 591 | }
|
| 592 |
|
| 593 | queue = info->hw_queue;
|
| 594 |
|
| 595 | __ieee80211_stop_queue(hw, queue,
|
| 596 | IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
| 597 | false);
|
| 598 |
|
| 599 | __skb_queue_tail(&local->pending[queue], skb);
|
| 600 | }
|
| 601 |
|
| 602 | for (i = 0; i < hw->queues; i++)
|
| 603 | __ieee80211_wake_queue(hw, i,
|
| 604 | IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
| 605 | false, &flags);
|
| 606 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
| 607 | }
|
| 608 |
|
| 609 | void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
|
| 610 | unsigned long queues,
|
| 611 | enum queue_stop_reason reason,
|
| 612 | bool refcounted)
|
| 613 | {
|
| 614 | struct ieee80211_local *local = hw_to_local(hw);
|
| 615 | unsigned long flags;
|
| 616 | int i;
|
| 617 |
|
| 618 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
| 619 |
|
| 620 | for_each_set_bit(i, &queues, hw->queues)
|
| 621 | __ieee80211_stop_queue(hw, i, reason, refcounted);
|
| 622 |
|
| 623 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
| 624 | }
|
| 625 |
|
| 626 | void ieee80211_stop_queues(struct ieee80211_hw *hw)
|
| 627 | {
|
| 628 | ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
|
| 629 | IEEE80211_QUEUE_STOP_REASON_DRIVER,
|
| 630 | false);
|
| 631 | }
|
| 632 | EXPORT_SYMBOL(ieee80211_stop_queues);
|
| 633 |
|
| 634 | int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
|
| 635 | {
|
| 636 | struct ieee80211_local *local = hw_to_local(hw);
|
| 637 | unsigned long flags;
|
| 638 | int ret;
|
| 639 |
|
| 640 | if (WARN_ON(queue >= hw->queues))
|
| 641 | return true;
|
| 642 |
|
| 643 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
| 644 | ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER,
|
| 645 | &local->queue_stop_reasons[queue]);
|
| 646 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
| 647 | return ret;
|
| 648 | }
|
| 649 | EXPORT_SYMBOL(ieee80211_queue_stopped);
|
| 650 |
|
| 651 | void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
| 652 | unsigned long queues,
|
| 653 | enum queue_stop_reason reason,
|
| 654 | bool refcounted)
|
| 655 | {
|
| 656 | struct ieee80211_local *local = hw_to_local(hw);
|
| 657 | unsigned long flags;
|
| 658 | int i;
|
| 659 |
|
| 660 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
| 661 |
|
| 662 | for_each_set_bit(i, &queues, hw->queues)
|
| 663 | __ieee80211_wake_queue(hw, i, reason, refcounted, &flags);
|
| 664 |
|
| 665 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
| 666 | }
|
| 667 |
|
| 668 | void ieee80211_wake_queues(struct ieee80211_hw *hw)
|
| 669 | {
|
| 670 | ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
|
| 671 | IEEE80211_QUEUE_STOP_REASON_DRIVER,
|
| 672 | false);
|
| 673 | }
|
| 674 | EXPORT_SYMBOL(ieee80211_wake_queues);
|
| 675 |
|
| 676 | unsigned int
|
| 677 | ieee80211_get_vif_queues(struct ieee80211_local *local,
|
| 678 | struct ieee80211_sub_if_data *sdata)
|
| 679 | {
|
| 680 | unsigned int queues;
|
| 681 |
|
| 682 | if (sdata && ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
|
| 683 | int ac;
|
| 684 |
|
| 685 | queues = 0;
|
| 686 |
|
| 687 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
| 688 | if (sdata->vif.hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
|
| 689 | queues |= BIT(sdata->vif.hw_queue[ac]);
|
| 690 | if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE)
|
| 691 | queues |= BIT(sdata->vif.cab_queue);
|
| 692 | } else {
|
| 693 |
|
| 694 | queues = BIT(local->hw.queues) - 1;
|
| 695 | }
|
| 696 |
|
| 697 | return queues;
|
| 698 | }
|
| 699 |
|
| 700 | void __ieee80211_flush_queues(struct ieee80211_local *local,
|
| 701 | struct ieee80211_sub_if_data *sdata,
|
| 702 | unsigned int queues, bool drop)
|
| 703 | {
|
| 704 | if (!local->ops->flush && !drop)
|
| 705 | return;
|
| 706 |
|
| 707 | |
| 708 | * If no queue was set, or if the HW doesn't support
|
| 709 | * IEEE80211_HW_QUEUE_CONTROL - flush all queues
|
| 710 |
|
| 711 | if (!queues || !ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
|
| 712 | queues = ieee80211_get_vif_queues(local, sdata);
|
| 713 |
|
| 714 | ieee80211_stop_queues_by_reason(&local->hw, queues,
|
| 715 | IEEE80211_QUEUE_STOP_REASON_FLUSH,
|
| 716 | false);
|
| 717 |
|
| 718 | if (drop) {
|
| 719 | struct sta_info *sta;
|
| 720 |
|
| 721 | |
| 722 | * sent during __ieee80211_wake_queue()
|
| 723 |
|
| 724 | list_for_each_entry(sta, &local->sta_list, list) {
|
| 725 | if (sdata != sta->sdata)
|
| 726 | continue;
|
| 727 | ieee80211_purge_sta_txqs(sta);
|
| 728 | }
|
| 729 | }
|
| 730 |
|
| 731 | if (local->ops->flush)
|
| 732 | drv_flush(local, sdata, queues, drop);
|
| 733 |
|
| 734 | ieee80211_wake_queues_by_reason(&local->hw, queues,
|
| 735 | IEEE80211_QUEUE_STOP_REASON_FLUSH,
|
| 736 | false);
|
| 737 | }
|
| 738 |
|
| 739 | void ieee80211_flush_queues(struct ieee80211_local *local,
|
| 740 | struct ieee80211_sub_if_data *sdata, bool drop)
|
| 741 | {
|
| 742 | __ieee80211_flush_queues(local, sdata, 0, drop);
|
| 743 | }
|
| 744 |
|
| 745 | static void __iterate_interfaces(struct ieee80211_local *local,
|
| 746 | u32 iter_flags,
|
| 747 | void (*iterator)(void *data, u8 *mac,
|
| 748 | struct ieee80211_vif *vif),
|
| 749 | void *data)
|
| 750 | {
|
| 751 | struct ieee80211_sub_if_data *sdata;
|
| 752 | bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;
|
| 753 |
|
| 754 | list_for_each_entry_rcu(sdata, &local->interfaces, list,
|
| 755 | lockdep_is_held(&local->iflist_mtx) ||
|
| 756 | lockdep_is_held(&local->hw.wiphy->mtx)) {
|
| 757 | switch (sdata->vif.type) {
|
| 758 | case NL80211_IFTYPE_MONITOR:
|
| 759 | if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
|
| 760 | !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
|
| 761 | continue;
|
| 762 | break;
|
| 763 | case NL80211_IFTYPE_AP_VLAN:
|
| 764 | continue;
|
| 765 | default:
|
| 766 | break;
|
| 767 | }
|
| 768 | if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
|
| 769 | active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
| 770 | continue;
|
| 771 | if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) &&
|
| 772 | !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
| 773 | continue;
|
| 774 | if (ieee80211_sdata_running(sdata) || !active_only)
|
| 775 | iterator(data, sdata->vif.addr,
|
| 776 | &sdata->vif);
|
| 777 | }
|
| 778 |
|
| 779 | sdata = rcu_dereference_check(local->monitor_sdata,
|
| 780 | lockdep_is_held(&local->iflist_mtx) ||
|
| 781 | lockdep_is_held(&local->hw.wiphy->mtx));
|
| 782 | if (sdata && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
|
| 783 | (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
|
| 784 | sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
| 785 | iterator(data, sdata->vif.addr, &sdata->vif);
|
| 786 | }
|
| 787 |
|
| 788 | void ieee80211_iterate_interfaces(
|
| 789 | struct ieee80211_hw *hw, u32 iter_flags,
|
| 790 | void (*iterator)(void *data, u8 *mac,
|
| 791 | struct ieee80211_vif *vif),
|
| 792 | void *data)
|
| 793 | {
|
| 794 | struct ieee80211_local *local = hw_to_local(hw);
|
| 795 |
|
| 796 | mutex_lock(&local->iflist_mtx);
|
| 797 | __iterate_interfaces(local, iter_flags, iterator, data);
|
| 798 | mutex_unlock(&local->iflist_mtx);
|
| 799 | }
|
| 800 | EXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces);
|
| 801 |
|
| 802 | void ieee80211_iterate_active_interfaces_atomic(
|
| 803 | struct ieee80211_hw *hw, u32 iter_flags,
|
| 804 | void (*iterator)(void *data, u8 *mac,
|
| 805 | struct ieee80211_vif *vif),
|
| 806 | void *data)
|
| 807 | {
|
| 808 | struct ieee80211_local *local = hw_to_local(hw);
|
| 809 |
|
| 810 | rcu_read_lock();
|
| 811 | __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
|
| 812 | iterator, data);
|
| 813 | rcu_read_unlock();
|
| 814 | }
|
| 815 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
|
| 816 |
|
| 817 | struct ieee80211_vif *
|
| 818 | __ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
|
| 819 | struct ieee80211_vif *prev,
|
| 820 | u32 iter_flags)
|
| 821 | {
|
| 822 | bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;
|
| 823 | struct ieee80211_sub_if_data *sdata = NULL, *monitor;
|
| 824 | struct ieee80211_local *local = hw_to_local(hw);
|
| 825 |
|
| 826 | lockdep_assert_wiphy(hw->wiphy);
|
| 827 |
|
| 828 | if (prev)
|
| 829 | sdata = vif_to_sdata(prev);
|
| 830 |
|
| 831 | monitor = rcu_dereference_check(local->monitor_sdata,
|
| 832 | lockdep_is_held(&hw->wiphy->mtx));
|
| 833 | if (monitor && monitor == sdata)
|
| 834 | return NULL;
|
| 835 |
|
| 836 | sdata = list_prepare_entry(sdata, &local->interfaces, list);
|
| 837 | list_for_each_entry_continue(sdata, &local->interfaces, list) {
|
| 838 | switch (sdata->vif.type) {
|
| 839 | case NL80211_IFTYPE_MONITOR:
|
| 840 | if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
|
| 841 | !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
|
| 842 | continue;
|
| 843 | break;
|
| 844 | case NL80211_IFTYPE_AP_VLAN:
|
| 845 | continue;
|
| 846 | default:
|
| 847 | break;
|
| 848 | }
|
| 849 | if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
|
| 850 | active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
| 851 | continue;
|
| 852 | if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) &&
|
| 853 | !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
| 854 | continue;
|
| 855 | if (ieee80211_sdata_running(sdata) || !active_only)
|
| 856 | return &sdata->vif;
|
| 857 | }
|
| 858 |
|
| 859 | if (monitor && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
|
| 860 | (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
|
| 861 | monitor->flags & IEEE80211_SDATA_IN_DRIVER))
|
| 862 | return &monitor->vif;
|
| 863 |
|
| 864 | return NULL;
|
| 865 | }
|
| 866 | EXPORT_SYMBOL_GPL(__ieee80211_iterate_interfaces);
|
| 867 |
|
| 868 | static void __iterate_stations(struct ieee80211_local *local,
|
| 869 | void (*iterator)(void *data,
|
| 870 | struct ieee80211_sta *sta),
|
| 871 | void *data)
|
| 872 | {
|
| 873 | struct sta_info *sta;
|
| 874 |
|
| 875 | list_for_each_entry_rcu(sta, &local->sta_list, list,
|
| 876 | lockdep_is_held(&local->hw.wiphy->mtx)) {
|
| 877 | if (!sta->uploaded)
|
| 878 | continue;
|
| 879 |
|
| 880 | iterator(data, &sta->sta);
|
| 881 | }
|
| 882 | }
|
| 883 |
|
| 884 | void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
|
| 885 | void (*iterator)(void *data,
|
| 886 | struct ieee80211_sta *sta),
|
| 887 | void *data)
|
| 888 | {
|
| 889 | struct ieee80211_local *local = hw_to_local(hw);
|
| 890 |
|
| 891 | rcu_read_lock();
|
| 892 | __iterate_stations(local, iterator, data);
|
| 893 | rcu_read_unlock();
|
| 894 | }
|
| 895 | EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic);
|
| 896 |
|
| 897 | struct ieee80211_sta *
|
| 898 | __ieee80211_iterate_stations(struct ieee80211_hw *hw,
|
| 899 | struct ieee80211_sta *prev)
|
| 900 | {
|
| 901 | struct ieee80211_local *local = hw_to_local(hw);
|
| 902 | struct sta_info *sta = NULL;
|
| 903 |
|
| 904 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 905 |
|
| 906 | if (prev)
|
| 907 | sta = container_of(prev, struct sta_info, sta);
|
| 908 |
|
| 909 | sta = list_prepare_entry(sta, &local->sta_list, list);
|
| 910 | list_for_each_entry_continue(sta, &local->sta_list, list) {
|
| 911 | if (!sta->uploaded)
|
| 912 | continue;
|
| 913 |
|
| 914 | return &sta->sta;
|
| 915 | }
|
| 916 |
|
| 917 | return NULL;
|
| 918 | }
|
| 919 | EXPORT_SYMBOL_GPL(__ieee80211_iterate_stations);
|
| 920 |
|
| 921 | struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
|
| 922 | {
|
| 923 | struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
| 924 |
|
| 925 | if (!ieee80211_sdata_running(sdata) ||
|
| 926 | !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
| 927 | return NULL;
|
| 928 | return &sdata->vif;
|
| 929 | }
|
| 930 | EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
|
| 931 |
|
| 932 | struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
|
| 933 | {
|
| 934 | if (!vif)
|
| 935 | return NULL;
|
| 936 |
|
| 937 | return &vif_to_sdata(vif)->wdev;
|
| 938 | }
|
| 939 | EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);
|
| 940 |
|
| 941 | |
| 942 | * Nothing should have been stuffed into the workqueue during
|
| 943 | * the suspend->resume cycle. Since we can't check each caller
|
| 944 | * of this function if we are already quiescing / suspended,
|
| 945 | * check here and don't WARN since this can actually happen when
|
| 946 | * the rx path (for example) is racing against __ieee80211_suspend
|
| 947 | * and suspending / quiescing was set after the rx path checked
|
| 948 | * them.
|
| 949 |
|
| 950 | static bool ieee80211_can_queue_work(struct ieee80211_local *local)
|
| 951 | {
|
| 952 | if (local->quiescing || (local->suspended && !local->resuming)) {
|
| 953 | pr_warn("queueing ieee80211 work while going to suspend\n");
|
| 954 | return false;
|
| 955 | }
|
| 956 |
|
| 957 | return true;
|
| 958 | }
|
| 959 |
|
| 960 | void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work)
|
| 961 | {
|
| 962 | struct ieee80211_local *local = hw_to_local(hw);
|
| 963 |
|
| 964 | if (!ieee80211_can_queue_work(local))
|
| 965 | return;
|
| 966 |
|
| 967 | queue_work(local->workqueue, work);
|
| 968 | }
|
| 969 | EXPORT_SYMBOL(ieee80211_queue_work);
|
| 970 |
|
| 971 | void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
|
| 972 | struct delayed_work *dwork,
|
| 973 | unsigned long delay)
|
| 974 | {
|
| 975 | struct ieee80211_local *local = hw_to_local(hw);
|
| 976 |
|
| 977 | if (!ieee80211_can_queue_work(local))
|
| 978 | return;
|
| 979 |
|
| 980 | queue_delayed_work(local->workqueue, dwork, delay);
|
| 981 | }
|
| 982 | EXPORT_SYMBOL(ieee80211_queue_delayed_work);
|
| 983 |
|
| 984 | void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
|
| 985 | struct ieee80211_tx_queue_params
|
| 986 | *qparam, int ac)
|
| 987 | {
|
| 988 | struct ieee80211_chanctx_conf *chanctx_conf;
|
| 989 | const struct ieee80211_reg_rule *rrule;
|
| 990 | const struct ieee80211_wmm_ac *wmm_ac;
|
| 991 | u16 center_freq = 0;
|
| 992 |
|
| 993 | if (sdata->vif.type != NL80211_IFTYPE_AP &&
|
| 994 | sdata->vif.type != NL80211_IFTYPE_STATION)
|
| 995 | return;
|
| 996 |
|
| 997 | rcu_read_lock();
|
| 998 | chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
|
| 999 | if (chanctx_conf)
|
| 1000 | center_freq = chanctx_conf->def.chan->center_freq;
|
| 1001 |
|
| 1002 | if (!center_freq) {
|
| 1003 | rcu_read_unlock();
|
| 1004 | return;
|
| 1005 | }
|
| 1006 |
|
| 1007 | rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq));
|
| 1008 |
|
| 1009 | if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) {
|
| 1010 | rcu_read_unlock();
|
| 1011 | return;
|
| 1012 | }
|
| 1013 |
|
| 1014 | if (sdata->vif.type == NL80211_IFTYPE_AP)
|
| 1015 | wmm_ac = &rrule->wmm_rule.ap[ac];
|
| 1016 | else
|
| 1017 | wmm_ac = &rrule->wmm_rule.client[ac];
|
| 1018 | qparam->cw_min = max_t(u16, qparam->cw_min, wmm_ac->cw_min);
|
| 1019 | qparam->cw_max = max_t(u16, qparam->cw_max, wmm_ac->cw_max);
|
| 1020 | qparam->aifs = max_t(u8, qparam->aifs, wmm_ac->aifsn);
|
| 1021 | qparam->txop = min_t(u16, qparam->txop, wmm_ac->cot / 32);
|
| 1022 | rcu_read_unlock();
|
| 1023 | }
|
| 1024 |
|
| 1025 | void ieee80211_set_wmm_default(struct ieee80211_link_data *link,
|
| 1026 | bool bss_notify, bool enable_qos)
|
| 1027 | {
|
| 1028 | struct ieee80211_sub_if_data *sdata = link->sdata;
|
| 1029 | struct ieee80211_local *local = sdata->local;
|
| 1030 | struct ieee80211_tx_queue_params qparam;
|
| 1031 | struct ieee80211_chanctx_conf *chanctx_conf;
|
| 1032 | int ac;
|
| 1033 | bool use_11b;
|
| 1034 | bool is_ocb;
|
| 1035 | int aCWmin, aCWmax;
|
| 1036 |
|
| 1037 | if (!local->ops->conf_tx)
|
| 1038 | return;
|
| 1039 |
|
| 1040 | if (local->hw.queues < IEEE80211_NUM_ACS)
|
| 1041 | return;
|
| 1042 |
|
| 1043 | memset(&qparam, 0, sizeof(qparam));
|
| 1044 |
|
| 1045 | rcu_read_lock();
|
| 1046 | chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
|
| 1047 | use_11b = (chanctx_conf &&
|
| 1048 | chanctx_conf->def.chan->band == NL80211_BAND_2GHZ) &&
|
| 1049 | !link->operating_11g_mode;
|
| 1050 | rcu_read_unlock();
|
| 1051 |
|
| 1052 | is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
|
| 1053 |
|
| 1054 |
|
| 1055 | aCWmax = 1023;
|
| 1056 | if (use_11b)
|
| 1057 | aCWmin = 31;
|
| 1058 | else
|
| 1059 | aCWmin = 15;
|
| 1060 |
|
| 1061 |
|
| 1062 | qparam.cw_max = aCWmax;
|
| 1063 | qparam.cw_min = aCWmin;
|
| 1064 | qparam.txop = 0;
|
| 1065 | qparam.aifs = 2;
|
| 1066 |
|
| 1067 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
| 1068 |
|
| 1069 | if (enable_qos) {
|
| 1070 | switch (ac) {
|
| 1071 | case IEEE80211_AC_BK:
|
| 1072 | qparam.cw_max = aCWmax;
|
| 1073 | qparam.cw_min = aCWmin;
|
| 1074 | qparam.txop = 0;
|
| 1075 | if (is_ocb)
|
| 1076 | qparam.aifs = 9;
|
| 1077 | else
|
| 1078 | qparam.aifs = 7;
|
| 1079 | break;
|
| 1080 |
|
| 1081 | default:
|
| 1082 | case IEEE80211_AC_BE:
|
| 1083 | qparam.cw_max = aCWmax;
|
| 1084 | qparam.cw_min = aCWmin;
|
| 1085 | qparam.txop = 0;
|
| 1086 | if (is_ocb)
|
| 1087 | qparam.aifs = 6;
|
| 1088 | else
|
| 1089 | qparam.aifs = 3;
|
| 1090 | break;
|
| 1091 | case IEEE80211_AC_VI:
|
| 1092 | qparam.cw_max = aCWmin;
|
| 1093 | qparam.cw_min = (aCWmin + 1) / 2 - 1;
|
| 1094 | if (is_ocb)
|
| 1095 | qparam.txop = 0;
|
| 1096 | else if (use_11b)
|
| 1097 | qparam.txop = 6016/32;
|
| 1098 | else
|
| 1099 | qparam.txop = 3008/32;
|
| 1100 |
|
| 1101 | if (is_ocb)
|
| 1102 | qparam.aifs = 3;
|
| 1103 | else
|
| 1104 | qparam.aifs = 2;
|
| 1105 | break;
|
| 1106 | case IEEE80211_AC_VO:
|
| 1107 | qparam.cw_max = (aCWmin + 1) / 2 - 1;
|
| 1108 | qparam.cw_min = (aCWmin + 1) / 4 - 1;
|
| 1109 | if (is_ocb)
|
| 1110 | qparam.txop = 0;
|
| 1111 | else if (use_11b)
|
| 1112 | qparam.txop = 3264/32;
|
| 1113 | else
|
| 1114 | qparam.txop = 1504/32;
|
| 1115 | qparam.aifs = 2;
|
| 1116 | break;
|
| 1117 | }
|
| 1118 | }
|
| 1119 | ieee80211_regulatory_limit_wmm_params(sdata, &qparam, ac);
|
| 1120 |
|
| 1121 | qparam.uapsd = false;
|
| 1122 |
|
| 1123 | link->tx_conf[ac] = qparam;
|
| 1124 | drv_conf_tx(local, link, ac, &qparam);
|
| 1125 | }
|
| 1126 |
|
| 1127 | if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
|
| 1128 | sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
|
| 1129 | sdata->vif.type != NL80211_IFTYPE_NAN) {
|
| 1130 | link->conf->qos = enable_qos;
|
| 1131 | if (bss_notify)
|
| 1132 | ieee80211_link_info_change_notify(sdata, link,
|
| 1133 | BSS_CHANGED_QOS);
|
| 1134 | }
|
| 1135 | }
|
| 1136 |
|
| 1137 | void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
| 1138 | u16 transaction, u16 auth_alg, u16 status,
|
| 1139 | const u8 *extra, size_t extra_len, const u8 *da,
|
| 1140 | const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
|
| 1141 | u32 tx_flags)
|
| 1142 | {
|
| 1143 | struct ieee80211_local *local = sdata->local;
|
| 1144 | struct sk_buff *skb;
|
| 1145 | struct ieee80211_mgmt *mgmt;
|
| 1146 | bool multi_link = ieee80211_vif_is_mld(&sdata->vif);
|
| 1147 | struct {
|
| 1148 | u8 id;
|
| 1149 | u8 len;
|
| 1150 | u8 ext_id;
|
| 1151 | struct ieee80211_multi_link_elem ml;
|
| 1152 | struct ieee80211_mle_basic_common_info basic;
|
| 1153 | } __packed mle = {
|
| 1154 | .id = WLAN_EID_EXTENSION,
|
| 1155 | .len = sizeof(mle) - 2,
|
| 1156 | .ext_id = WLAN_EID_EXT_EHT_MULTI_LINK,
|
| 1157 | .ml.control = cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC),
|
| 1158 | .basic.len = sizeof(mle.basic),
|
| 1159 | };
|
| 1160 | bool add_mle;
|
| 1161 | int err;
|
| 1162 |
|
| 1163 | add_mle = (multi_link &&
|
| 1164 | !cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_MULTI_LINK,
|
| 1165 | extra, extra_len));
|
| 1166 |
|
| 1167 |
|
| 1168 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN +
|
| 1169 | 24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN +
|
| 1170 | add_mle * sizeof(mle));
|
| 1171 | if (!skb)
|
| 1172 | return;
|
| 1173 |
|
| 1174 | skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN);
|
| 1175 |
|
| 1176 | mgmt = skb_put_zero(skb, 24 + 6);
|
| 1177 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
| 1178 | IEEE80211_STYPE_AUTH);
|
| 1179 | memcpy(mgmt->da, da, ETH_ALEN);
|
| 1180 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
| 1181 | memcpy(mgmt->bssid, bssid, ETH_ALEN);
|
| 1182 | mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
|
| 1183 | mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
|
| 1184 | mgmt->u.auth.status_code = cpu_to_le16(status);
|
| 1185 | if (extra)
|
| 1186 | skb_put_data(skb, extra, extra_len);
|
| 1187 |
|
| 1188 | if (add_mle) {
|
| 1189 | memcpy(mle.basic.mld_mac_addr, sdata->vif.addr, ETH_ALEN);
|
| 1190 | skb_put_data(skb, &mle, sizeof(mle));
|
| 1191 | }
|
| 1192 |
|
| 1193 | if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
|
| 1194 | mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
| 1195 | err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
|
| 1196 | if (WARN_ON(err)) {
|
| 1197 | kfree_skb(skb);
|
| 1198 | return;
|
| 1199 | }
|
| 1200 | }
|
| 1201 |
|
| 1202 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
|
| 1203 | tx_flags;
|
| 1204 | ieee80211_tx_skb(sdata, skb);
|
| 1205 | }
|
| 1206 |
|
| 1207 | void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
| 1208 | const u8 *da, const u8 *bssid,
|
| 1209 | u16 stype, u16 reason,
|
| 1210 | bool send_frame, u8 *frame_buf)
|
| 1211 | {
|
| 1212 | struct ieee80211_local *local = sdata->local;
|
| 1213 | struct sk_buff *skb;
|
| 1214 | struct ieee80211_mgmt *mgmt = (void *)frame_buf;
|
| 1215 |
|
| 1216 |
|
| 1217 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
|
| 1218 | mgmt->duration = 0;
|
| 1219 | mgmt->seq_ctrl = 0;
|
| 1220 | memcpy(mgmt->da, da, ETH_ALEN);
|
| 1221 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
| 1222 | memcpy(mgmt->bssid, bssid, ETH_ALEN);
|
| 1223 |
|
| 1224 | mgmt->u.deauth.reason_code = cpu_to_le16(reason);
|
| 1225 |
|
| 1226 | if (send_frame) {
|
| 1227 | skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
| 1228 | IEEE80211_DEAUTH_FRAME_LEN);
|
| 1229 | if (!skb)
|
| 1230 | return;
|
| 1231 |
|
| 1232 | skb_reserve(skb, local->hw.extra_tx_headroom);
|
| 1233 |
|
| 1234 |
|
| 1235 | skb_put_data(skb, mgmt, IEEE80211_DEAUTH_FRAME_LEN);
|
| 1236 |
|
| 1237 | if (sdata->vif.type != NL80211_IFTYPE_STATION ||
|
| 1238 | !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
|
| 1239 | IEEE80211_SKB_CB(skb)->flags |=
|
| 1240 | IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
| 1241 |
|
| 1242 | ieee80211_tx_skb(sdata, skb);
|
| 1243 | }
|
| 1244 | }
|
| 1245 |
|
| 1246 | static int ieee80211_put_s1g_cap(struct sk_buff *skb,
|
| 1247 | struct ieee80211_sta_s1g_cap *s1g_cap)
|
| 1248 | {
|
| 1249 | if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_s1g_cap))
|
| 1250 | return -ENOBUFS;
|
| 1251 |
|
| 1252 | skb_put_u8(skb, WLAN_EID_S1G_CAPABILITIES);
|
| 1253 | skb_put_u8(skb, sizeof(struct ieee80211_s1g_cap));
|
| 1254 |
|
| 1255 | skb_put_data(skb, &s1g_cap->cap, sizeof(s1g_cap->cap));
|
| 1256 | skb_put_data(skb, &s1g_cap->nss_mcs, sizeof(s1g_cap->nss_mcs));
|
| 1257 |
|
| 1258 | return 0;
|
| 1259 | }
|
| 1260 |
|
| 1261 | static int ieee80211_put_preq_ies_band(struct sk_buff *skb,
|
| 1262 | struct ieee80211_sub_if_data *sdata,
|
| 1263 | const u8 *ie, size_t ie_len,
|
| 1264 | size_t *offset,
|
| 1265 | enum nl80211_band band,
|
| 1266 | u32 rate_mask,
|
| 1267 | struct cfg80211_chan_def *chandef,
|
| 1268 | u32 flags)
|
| 1269 | {
|
| 1270 | struct ieee80211_local *local = sdata->local;
|
| 1271 | struct ieee80211_supported_band *sband;
|
| 1272 | int i, err;
|
| 1273 | size_t noffset;
|
| 1274 | bool have_80mhz = false;
|
| 1275 |
|
| 1276 | *offset = 0;
|
| 1277 |
|
| 1278 | sband = local->hw.wiphy->bands[band];
|
| 1279 | if (WARN_ON_ONCE(!sband))
|
| 1280 | return 0;
|
| 1281 |
|
| 1282 |
|
| 1283 | if (band == NL80211_BAND_S1GHZ)
|
| 1284 | return ieee80211_put_s1g_cap(skb, &sband->s1g_cap);
|
| 1285 |
|
| 1286 | err = ieee80211_put_srates_elem(skb, sband, 0,
|
| 1287 | ~rate_mask, WLAN_EID_SUPP_RATES);
|
| 1288 | if (err)
|
| 1289 | return err;
|
| 1290 |
|
| 1291 |
|
| 1292 | if (ie && ie_len) {
|
| 1293 | static const u8 before_extrates[] = {
|
| 1294 | WLAN_EID_SSID,
|
| 1295 | WLAN_EID_SUPP_RATES,
|
| 1296 | WLAN_EID_REQUEST,
|
| 1297 | };
|
| 1298 | noffset = ieee80211_ie_split(ie, ie_len,
|
| 1299 | before_extrates,
|
| 1300 | ARRAY_SIZE(before_extrates),
|
| 1301 | *offset);
|
| 1302 | if (skb_tailroom(skb) < noffset - *offset)
|
| 1303 | return -ENOBUFS;
|
| 1304 | skb_put_data(skb, ie + *offset, noffset - *offset);
|
| 1305 | *offset = noffset;
|
| 1306 | }
|
| 1307 |
|
| 1308 | err = ieee80211_put_srates_elem(skb, sband, 0,
|
| 1309 | ~rate_mask, WLAN_EID_EXT_SUPP_RATES);
|
| 1310 | if (err)
|
| 1311 | return err;
|
| 1312 |
|
| 1313 | if (chandef->chan && sband->band == NL80211_BAND_2GHZ) {
|
| 1314 | if (skb_tailroom(skb) < 3)
|
| 1315 | return -ENOBUFS;
|
| 1316 | skb_put_u8(skb, WLAN_EID_DS_PARAMS);
|
| 1317 | skb_put_u8(skb, 1);
|
| 1318 | skb_put_u8(skb,
|
| 1319 | ieee80211_frequency_to_channel(chandef->chan->center_freq));
|
| 1320 | }
|
| 1321 |
|
| 1322 | if (flags & IEEE80211_PROBE_FLAG_MIN_CONTENT)
|
| 1323 | return 0;
|
| 1324 |
|
| 1325 |
|
| 1326 | if (ie && ie_len) {
|
| 1327 | static const u8 before_ht[] = {
|
| 1328 | |
| 1329 | * no need to list the ones split off already
|
| 1330 | * (or generated here)
|
| 1331 |
|
| 1332 | WLAN_EID_DS_PARAMS,
|
| 1333 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
| 1334 | };
|
| 1335 | noffset = ieee80211_ie_split(ie, ie_len,
|
| 1336 | before_ht, ARRAY_SIZE(before_ht),
|
| 1337 | *offset);
|
| 1338 | if (skb_tailroom(skb) < noffset - *offset)
|
| 1339 | return -ENOBUFS;
|
| 1340 | skb_put_data(skb, ie + *offset, noffset - *offset);
|
| 1341 | *offset = noffset;
|
| 1342 | }
|
| 1343 |
|
| 1344 | if (sband->ht_cap.ht_supported) {
|
| 1345 | u8 *pos;
|
| 1346 |
|
| 1347 | if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
|
| 1348 | return -ENOBUFS;
|
| 1349 |
|
| 1350 | pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap));
|
| 1351 | ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
|
| 1352 | sband->ht_cap.cap);
|
| 1353 | }
|
| 1354 |
|
| 1355 |
|
| 1356 | if (ie && ie_len) {
|
| 1357 | static const u8 before_vht[] = {
|
| 1358 | |
| 1359 | * no need to list the ones split off already
|
| 1360 | * (or generated here)
|
| 1361 |
|
| 1362 | WLAN_EID_BSS_COEX_2040,
|
| 1363 | WLAN_EID_EXT_CAPABILITY,
|
| 1364 | WLAN_EID_SSID_LIST,
|
| 1365 | WLAN_EID_CHANNEL_USAGE,
|
| 1366 | WLAN_EID_INTERWORKING,
|
| 1367 | WLAN_EID_MESH_ID,
|
| 1368 |
|
| 1369 | };
|
| 1370 | noffset = ieee80211_ie_split(ie, ie_len,
|
| 1371 | before_vht, ARRAY_SIZE(before_vht),
|
| 1372 | *offset);
|
| 1373 | if (skb_tailroom(skb) < noffset - *offset)
|
| 1374 | return -ENOBUFS;
|
| 1375 | skb_put_data(skb, ie + *offset, noffset - *offset);
|
| 1376 | *offset = noffset;
|
| 1377 | }
|
| 1378 |
|
| 1379 |
|
| 1380 | for (i = 0; i < sband->n_channels; i++) {
|
| 1381 | if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
|
| 1382 | IEEE80211_CHAN_NO_80MHZ))
|
| 1383 | continue;
|
| 1384 |
|
| 1385 | have_80mhz = true;
|
| 1386 | break;
|
| 1387 | }
|
| 1388 |
|
| 1389 | if (sband->vht_cap.vht_supported && have_80mhz) {
|
| 1390 | u8 *pos;
|
| 1391 |
|
| 1392 | if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_cap))
|
| 1393 | return -ENOBUFS;
|
| 1394 |
|
| 1395 | pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_cap));
|
| 1396 | ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
|
| 1397 | sband->vht_cap.cap);
|
| 1398 | }
|
| 1399 |
|
| 1400 |
|
| 1401 | if (ie && ie_len) {
|
| 1402 | static const u8 before_he[] = {
|
| 1403 | |
| 1404 | * no need to list the ones split off before VHT
|
| 1405 | * or generated here
|
| 1406 |
|
| 1407 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS,
|
| 1408 | WLAN_EID_AP_CSN,
|
| 1409 |
|
| 1410 | };
|
| 1411 | noffset = ieee80211_ie_split(ie, ie_len,
|
| 1412 | before_he, ARRAY_SIZE(before_he),
|
| 1413 | *offset);
|
| 1414 | if (skb_tailroom(skb) < noffset - *offset)
|
| 1415 | return -ENOBUFS;
|
| 1416 | skb_put_data(skb, ie + *offset, noffset - *offset);
|
| 1417 | *offset = noffset;
|
| 1418 | }
|
| 1419 |
|
| 1420 | if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
|
| 1421 | IEEE80211_CHAN_NO_HE)) {
|
| 1422 | err = ieee80211_put_he_cap(skb, sdata, sband, NULL);
|
| 1423 | if (err)
|
| 1424 | return err;
|
| 1425 | }
|
| 1426 |
|
| 1427 | if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
|
| 1428 | IEEE80211_CHAN_NO_HE |
|
| 1429 | IEEE80211_CHAN_NO_EHT)) {
|
| 1430 | err = ieee80211_put_eht_cap(skb, sdata, sband, NULL);
|
| 1431 | if (err)
|
| 1432 | return err;
|
| 1433 | }
|
| 1434 |
|
| 1435 | err = ieee80211_put_he_6ghz_cap(skb, sdata, IEEE80211_SMPS_OFF);
|
| 1436 | if (err)
|
| 1437 | return err;
|
| 1438 |
|
| 1439 | if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
|
| 1440 | IEEE80211_CHAN_NO_UHR)) {
|
| 1441 | err = ieee80211_put_uhr_cap(skb, sdata, sband);
|
| 1442 | if (err)
|
| 1443 | return err;
|
| 1444 | }
|
| 1445 |
|
| 1446 | |
| 1447 | * If adding more here, adjust code in main.c
|
| 1448 | * that calculates local->scan_ies_len.
|
| 1449 |
|
| 1450 |
|
| 1451 | return 0;
|
| 1452 | }
|
| 1453 |
|
| 1454 | static int ieee80211_put_preq_ies(struct sk_buff *skb,
|
| 1455 | struct ieee80211_sub_if_data *sdata,
|
| 1456 | struct ieee80211_scan_ies *ie_desc,
|
| 1457 | const u8 *ie, size_t ie_len,
|
| 1458 | u8 bands_used, u32 *rate_masks,
|
| 1459 | struct cfg80211_chan_def *chandef,
|
| 1460 | u32 flags)
|
| 1461 | {
|
| 1462 | size_t custom_ie_offset = 0;
|
| 1463 | int i, err;
|
| 1464 |
|
| 1465 | memset(ie_desc, 0, sizeof(*ie_desc));
|
| 1466 |
|
| 1467 | for (i = 0; i < NUM_NL80211_BANDS; i++) {
|
| 1468 | if (bands_used & BIT(i)) {
|
| 1469 | ie_desc->ies[i] = skb_tail_pointer(skb);
|
| 1470 | err = ieee80211_put_preq_ies_band(skb, sdata,
|
| 1471 | ie, ie_len,
|
| 1472 | &custom_ie_offset,
|
| 1473 | i, rate_masks[i],
|
| 1474 | chandef, flags);
|
| 1475 | if (err)
|
| 1476 | return err;
|
| 1477 | ie_desc->len[i] = skb_tail_pointer(skb) -
|
| 1478 | ie_desc->ies[i];
|
| 1479 | }
|
| 1480 | }
|
| 1481 |
|
| 1482 |
|
| 1483 | if (ie && ie_len) {
|
| 1484 | if (WARN_ONCE(skb_tailroom(skb) < ie_len - custom_ie_offset,
|
| 1485 | "not enough space for preq custom IEs\n"))
|
| 1486 | return -ENOBUFS;
|
| 1487 | ie_desc->common_ies = skb_tail_pointer(skb);
|
| 1488 | skb_put_data(skb, ie + custom_ie_offset,
|
| 1489 | ie_len - custom_ie_offset);
|
| 1490 | ie_desc->common_ie_len = skb_tail_pointer(skb) -
|
| 1491 | ie_desc->common_ies;
|
| 1492 | }
|
| 1493 |
|
| 1494 | return 0;
|
| 1495 | };
|
| 1496 |
|
| 1497 | int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer,
|
| 1498 | size_t buffer_len,
|
| 1499 | struct ieee80211_scan_ies *ie_desc,
|
| 1500 | const u8 *ie, size_t ie_len,
|
| 1501 | u8 bands_used, u32 *rate_masks,
|
| 1502 | struct cfg80211_chan_def *chandef,
|
| 1503 | u32 flags)
|
| 1504 | {
|
| 1505 | struct sk_buff *skb = alloc_skb(buffer_len, GFP_KERNEL);
|
| 1506 | uintptr_t offs;
|
| 1507 | int ret, i;
|
| 1508 | u8 *start;
|
| 1509 |
|
| 1510 | if (!skb)
|
| 1511 | return -ENOMEM;
|
| 1512 |
|
| 1513 | start = skb_tail_pointer(skb);
|
| 1514 | memset(start, 0, skb_tailroom(skb));
|
| 1515 | ret = ieee80211_put_preq_ies(skb, sdata, ie_desc, ie, ie_len,
|
| 1516 | bands_used, rate_masks, chandef,
|
| 1517 | flags);
|
| 1518 | if (ret < 0) {
|
| 1519 | goto out;
|
| 1520 | }
|
| 1521 |
|
| 1522 | if (skb->len > buffer_len) {
|
| 1523 | ret = -ENOBUFS;
|
| 1524 | goto out;
|
| 1525 | }
|
| 1526 |
|
| 1527 | memcpy(buffer, start, skb->len);
|
| 1528 |
|
| 1529 |
|
| 1530 | for (i = 0; i < NUM_NL80211_BANDS; i++) {
|
| 1531 | offs = ie_desc->ies[i] - start;
|
| 1532 | ie_desc->ies[i] = buffer + offs;
|
| 1533 | }
|
| 1534 | offs = ie_desc->common_ies - start;
|
| 1535 | ie_desc->common_ies = buffer + offs;
|
| 1536 |
|
| 1537 | ret = skb->len;
|
| 1538 | out:
|
| 1539 | consume_skb(skb);
|
| 1540 | return ret;
|
| 1541 | }
|
| 1542 |
|
| 1543 | struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
| 1544 | const u8 *src, const u8 *dst,
|
| 1545 | u32 ratemask,
|
| 1546 | struct ieee80211_channel *chan,
|
| 1547 | const u8 *ssid, size_t ssid_len,
|
| 1548 | const u8 *ie, size_t ie_len,
|
| 1549 | u32 flags)
|
| 1550 | {
|
| 1551 | struct ieee80211_local *local = sdata->local;
|
| 1552 | struct cfg80211_chan_def chandef;
|
| 1553 | struct sk_buff *skb;
|
| 1554 | struct ieee80211_mgmt *mgmt;
|
| 1555 | u32 rate_masks[NUM_NL80211_BANDS] = {};
|
| 1556 | struct ieee80211_scan_ies dummy_ie_desc;
|
| 1557 |
|
| 1558 | |
| 1559 | * Do not send DS Channel parameter for directed probe requests
|
| 1560 | * in order to maximize the chance that we get a response. Some
|
| 1561 | * badly-behaved APs don't respond when this parameter is included.
|
| 1562 |
|
| 1563 | chandef.width = sdata->vif.bss_conf.chanreq.oper.width;
|
| 1564 | if (flags & IEEE80211_PROBE_FLAG_DIRECTED)
|
| 1565 | chandef.chan = NULL;
|
| 1566 | else
|
| 1567 | chandef.chan = chan;
|
| 1568 |
|
| 1569 | skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len,
|
| 1570 | local->scan_ies_len + ie_len);
|
| 1571 | if (!skb)
|
| 1572 | return NULL;
|
| 1573 |
|
| 1574 | rate_masks[chan->band] = ratemask;
|
| 1575 | ieee80211_put_preq_ies(skb, sdata, &dummy_ie_desc,
|
| 1576 | ie, ie_len, BIT(chan->band),
|
| 1577 | rate_masks, &chandef, flags);
|
| 1578 |
|
| 1579 | if (dst) {
|
| 1580 | mgmt = (struct ieee80211_mgmt *) skb->data;
|
| 1581 | memcpy(mgmt->da, dst, ETH_ALEN);
|
| 1582 | memcpy(mgmt->bssid, dst, ETH_ALEN);
|
| 1583 | }
|
| 1584 |
|
| 1585 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
| 1586 |
|
| 1587 | return skb;
|
| 1588 | }
|
| 1589 |
|
| 1590 | u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
|
| 1591 | struct ieee802_11_elems *elems,
|
| 1592 | enum nl80211_band band, u32 *basic_rates)
|
| 1593 | {
|
| 1594 | struct ieee80211_supported_band *sband;
|
| 1595 | size_t num_rates;
|
| 1596 | u32 supp_rates;
|
| 1597 | int i, j;
|
| 1598 |
|
| 1599 | sband = sdata->local->hw.wiphy->bands[band];
|
| 1600 | if (WARN_ON(!sband))
|
| 1601 | return 1;
|
| 1602 |
|
| 1603 | num_rates = sband->n_bitrates;
|
| 1604 | supp_rates = 0;
|
| 1605 | for (i = 0; i < elems->supp_rates_len +
|
| 1606 | elems->ext_supp_rates_len; i++) {
|
| 1607 | u8 rate = 0;
|
| 1608 | int own_rate;
|
| 1609 | bool is_basic;
|
| 1610 | if (i < elems->supp_rates_len)
|
| 1611 | rate = elems->supp_rates[i];
|
| 1612 | else if (elems->ext_supp_rates)
|
| 1613 | rate = elems->ext_supp_rates
|
| 1614 | [i - elems->supp_rates_len];
|
| 1615 | own_rate = 5 * (rate & 0x7f);
|
| 1616 | is_basic = !!(rate & 0x80);
|
| 1617 |
|
| 1618 | if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
|
| 1619 | continue;
|
| 1620 |
|
| 1621 | for (j = 0; j < num_rates; j++) {
|
| 1622 | int brate = sband->bitrates[j].bitrate;
|
| 1623 |
|
| 1624 | if (brate == own_rate) {
|
| 1625 | supp_rates |= BIT(j);
|
| 1626 | if (basic_rates && is_basic)
|
| 1627 | *basic_rates |= BIT(j);
|
| 1628 | }
|
| 1629 | }
|
| 1630 | }
|
| 1631 | return supp_rates;
|
| 1632 | }
|
| 1633 |
|
| 1634 | void ieee80211_stop_device(struct ieee80211_local *local, bool suspend)
|
| 1635 | {
|
| 1636 | local_bh_disable();
|
| 1637 | ieee80211_handle_queued_frames(local);
|
| 1638 | local_bh_enable();
|
| 1639 |
|
| 1640 | ieee80211_led_radio(local, false);
|
| 1641 | ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO);
|
| 1642 |
|
| 1643 | wiphy_work_cancel(local->hw.wiphy, &local->reconfig_filter);
|
| 1644 |
|
| 1645 | flush_workqueue(local->workqueue);
|
| 1646 | wiphy_work_flush(local->hw.wiphy, NULL);
|
| 1647 | drv_stop(local, suspend);
|
| 1648 | }
|
| 1649 |
|
| 1650 | static void ieee80211_flush_completed_scan(struct ieee80211_local *local,
|
| 1651 | bool aborted)
|
| 1652 | {
|
| 1653 | |
| 1654 | * time during suspend, so if it's still marked as completed
|
| 1655 | * here, queue the work and flush it to clean things up.
|
| 1656 | * Instead of calling the worker function directly here, we
|
| 1657 | * really queue it to avoid potential races with other flows
|
| 1658 | * scheduling the same work.
|
| 1659 |
|
| 1660 | if (test_bit(SCAN_COMPLETED, &local->scanning)) {
|
| 1661 | |
| 1662 | * we don't attempt to continue a partial HW scan - which is
|
| 1663 | * possible otherwise if (e.g.) the 2.4 GHz portion was the
|
| 1664 | * completed scan, and a 5 GHz portion is still pending.
|
| 1665 |
|
| 1666 | if (aborted)
|
| 1667 | set_bit(SCAN_ABORTED, &local->scanning);
|
| 1668 | wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0);
|
| 1669 | wiphy_delayed_work_flush(local->hw.wiphy, &local->scan_work);
|
| 1670 | }
|
| 1671 | }
|
| 1672 |
|
| 1673 | static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
|
| 1674 | {
|
| 1675 | struct ieee80211_sub_if_data *sdata;
|
| 1676 | struct ieee80211_chanctx *ctx;
|
| 1677 |
|
| 1678 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 1679 |
|
| 1680 | |
| 1681 | * We get here if during resume the device can't be restarted properly.
|
| 1682 | * We might also get here if this happens during HW reset, which is a
|
| 1683 | * slightly different situation and we need to drop all connections in
|
| 1684 | * the latter case.
|
| 1685 | *
|
| 1686 | * Ask cfg80211 to turn off all interfaces, this will result in more
|
| 1687 | * warnings but at least we'll then get into a clean stopped state.
|
| 1688 |
|
| 1689 |
|
| 1690 | local->resuming = false;
|
| 1691 | local->suspended = false;
|
| 1692 | local->in_reconfig = false;
|
| 1693 | local->reconfig_failure = true;
|
| 1694 |
|
| 1695 | ieee80211_flush_completed_scan(local, true);
|
| 1696 |
|
| 1697 | |
| 1698 | * cfg80211 and clear local state
|
| 1699 |
|
| 1700 | ieee80211_sched_scan_end(local);
|
| 1701 |
|
| 1702 | list_for_each_entry(sdata, &local->interfaces, list)
|
| 1703 | sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
|
| 1704 |
|
| 1705 | |
| 1706 | * removing them from the driver during the shutdown process...
|
| 1707 |
|
| 1708 | list_for_each_entry(ctx, &local->chanctx_list, list)
|
| 1709 | ctx->driver_present = false;
|
| 1710 | }
|
| 1711 |
|
| 1712 | static void ieee80211_assign_chanctx(struct ieee80211_local *local,
|
| 1713 | struct ieee80211_sub_if_data *sdata,
|
| 1714 | struct ieee80211_link_data *link)
|
| 1715 | {
|
| 1716 | struct ieee80211_chanctx_conf *conf;
|
| 1717 | struct ieee80211_chanctx *ctx;
|
| 1718 |
|
| 1719 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 1720 |
|
| 1721 | conf = rcu_dereference_protected(link->conf->chanctx_conf,
|
| 1722 | lockdep_is_held(&local->hw.wiphy->mtx));
|
| 1723 | if (conf) {
|
| 1724 | ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
| 1725 | drv_assign_vif_chanctx(local, sdata, link->conf, ctx);
|
| 1726 | }
|
| 1727 | }
|
| 1728 |
|
| 1729 | static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata)
|
| 1730 | {
|
| 1731 | struct ieee80211_local *local = sdata->local;
|
| 1732 | struct sta_info *sta;
|
| 1733 |
|
| 1734 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 1735 |
|
| 1736 |
|
| 1737 | list_for_each_entry(sta, &local->sta_list, list) {
|
| 1738 | enum ieee80211_sta_state state;
|
| 1739 |
|
| 1740 | if (!sta->uploaded || sta->sdata != sdata)
|
| 1741 | continue;
|
| 1742 |
|
| 1743 | for (state = IEEE80211_STA_NOTEXIST;
|
| 1744 | state < sta->sta_state; state++)
|
| 1745 | WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
|
| 1746 | state + 1));
|
| 1747 | }
|
| 1748 | }
|
| 1749 |
|
| 1750 | static int
|
| 1751 | ieee80211_reconfig_nan_offload_de(struct ieee80211_sub_if_data *sdata)
|
| 1752 | {
|
| 1753 | struct cfg80211_nan_func *func, **funcs;
|
| 1754 | int res, id, i = 0;
|
| 1755 |
|
| 1756 | funcs = kzalloc_objs(*funcs, sdata->local->hw.max_nan_de_entries + 1);
|
| 1757 | if (!funcs)
|
| 1758 | return -ENOMEM;
|
| 1759 |
|
| 1760 | |
| 1761 | * This is a little bit ugly. We need to call a potentially sleeping
|
| 1762 | * callback for each NAN function, so we can't hold the spinlock.
|
| 1763 |
|
| 1764 | spin_lock_bh(&sdata->u.nan.de.func_lock);
|
| 1765 |
|
| 1766 | idr_for_each_entry(&sdata->u.nan.de.function_inst_ids, func, id)
|
| 1767 | funcs[i++] = func;
|
| 1768 |
|
| 1769 | spin_unlock_bh(&sdata->u.nan.de.func_lock);
|
| 1770 |
|
| 1771 | for (i = 0; funcs[i]; i++) {
|
| 1772 | res = drv_add_nan_func(sdata->local, sdata, funcs[i]);
|
| 1773 | if (WARN_ON(res))
|
| 1774 | ieee80211_nan_func_terminated(&sdata->vif,
|
| 1775 | funcs[i]->instance_id,
|
| 1776 | NL80211_NAN_FUNC_TERM_REASON_ERROR,
|
| 1777 | GFP_KERNEL);
|
| 1778 | }
|
| 1779 |
|
| 1780 | kfree(funcs);
|
| 1781 | return res;
|
| 1782 | }
|
| 1783 |
|
| 1784 | static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata)
|
| 1785 | {
|
| 1786 | struct ieee80211_local *local = sdata->local;
|
| 1787 | struct ieee80211_sub_if_data *ndi_sdata;
|
| 1788 | struct sta_info *sta;
|
| 1789 | int res;
|
| 1790 |
|
| 1791 | res = drv_start_nan(local, sdata, &sdata->u.nan.conf);
|
| 1792 | if (WARN_ON(res))
|
| 1793 | return res;
|
| 1794 |
|
| 1795 | if (!(sdata->local->hw.wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE))
|
| 1796 | return ieee80211_reconfig_nan_offload_de(sdata);
|
| 1797 |
|
| 1798 | drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED);
|
| 1799 |
|
| 1800 |
|
| 1801 | list_for_each_entry(ndi_sdata, &local->interfaces, list) {
|
| 1802 | if (ndi_sdata->vif.type == NL80211_IFTYPE_NAN_DATA) {
|
| 1803 | res = drv_add_interface(local, ndi_sdata);
|
| 1804 | if (WARN_ON(res))
|
| 1805 | return res;
|
| 1806 | }
|
| 1807 | }
|
| 1808 |
|
| 1809 |
|
| 1810 | list_for_each_entry(sta, &local->sta_list, list) {
|
| 1811 | enum ieee80211_sta_state state;
|
| 1812 |
|
| 1813 | if (!sta->uploaded || sta->sdata != sdata)
|
| 1814 | continue;
|
| 1815 |
|
| 1816 | for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state;
|
| 1817 | state++) {
|
| 1818 | res = drv_sta_state(local, sdata, sta, state,
|
| 1819 | state + 1);
|
| 1820 | if (WARN_ON(res))
|
| 1821 | return res;
|
| 1822 | }
|
| 1823 |
|
| 1824 |
|
| 1825 | if (!sta->sta.nan_sched)
|
| 1826 | continue;
|
| 1827 |
|
| 1828 | res = drv_nan_peer_sched_changed(local, sdata, sta);
|
| 1829 | if (WARN_ON(res))
|
| 1830 | return res;
|
| 1831 | }
|
| 1832 |
|
| 1833 |
|
| 1834 | list_for_each_entry(sta, &local->sta_list, list) {
|
| 1835 | enum ieee80211_sta_state state;
|
| 1836 |
|
| 1837 | if (!sta->uploaded ||
|
| 1838 | sta->sdata->vif.type != NL80211_IFTYPE_NAN_DATA)
|
| 1839 | continue;
|
| 1840 |
|
| 1841 | if (WARN_ON(!sta->sta.nmi))
|
| 1842 | continue;
|
| 1843 |
|
| 1844 | for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state;
|
| 1845 | state++) {
|
| 1846 | res = drv_sta_state(local, sta->sdata, sta, state,
|
| 1847 | state + 1);
|
| 1848 | if (WARN_ON(res))
|
| 1849 | return res;
|
| 1850 | }
|
| 1851 | }
|
| 1852 |
|
| 1853 | return 0;
|
| 1854 | }
|
| 1855 |
|
| 1856 | static void ieee80211_reconfig_ap_links(struct ieee80211_local *local,
|
| 1857 | struct ieee80211_sub_if_data *sdata,
|
| 1858 | u64 changed)
|
| 1859 | {
|
| 1860 | int link_id;
|
| 1861 |
|
| 1862 | for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
|
| 1863 | struct ieee80211_link_data *link;
|
| 1864 |
|
| 1865 | if (!(sdata->vif.active_links & BIT(link_id)))
|
| 1866 | continue;
|
| 1867 |
|
| 1868 | link = sdata_dereference(sdata->link[link_id], sdata);
|
| 1869 | if (!link)
|
| 1870 | continue;
|
| 1871 |
|
| 1872 | if (rcu_access_pointer(link->u.ap.beacon))
|
| 1873 | drv_start_ap(local, sdata, link->conf);
|
| 1874 |
|
| 1875 | if (!link->conf->enable_beacon)
|
| 1876 | continue;
|
| 1877 |
|
| 1878 | changed |= BSS_CHANGED_BEACON |
|
| 1879 | BSS_CHANGED_BEACON_ENABLED;
|
| 1880 |
|
| 1881 | ieee80211_link_info_change_notify(sdata, link, changed);
|
| 1882 | }
|
| 1883 | }
|
| 1884 |
|
| 1885 | int ieee80211_reconfig(struct ieee80211_local *local)
|
| 1886 | {
|
| 1887 | struct ieee80211_hw *hw = &local->hw;
|
| 1888 | struct ieee80211_sub_if_data *sdata;
|
| 1889 | struct ieee80211_chanctx *ctx;
|
| 1890 | struct sta_info *sta;
|
| 1891 | int res, i;
|
| 1892 | bool reconfig_due_to_wowlan = false;
|
| 1893 | struct ieee80211_sub_if_data *sched_scan_sdata;
|
| 1894 | struct cfg80211_sched_scan_request *sched_scan_req;
|
| 1895 | bool sched_scan_stopped = false;
|
| 1896 | bool suspended = local->suspended;
|
| 1897 | bool in_reconfig = false;
|
| 1898 |
|
| 1899 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 1900 |
|
| 1901 |
|
| 1902 | if (!local->open_count)
|
| 1903 | goto wake_up;
|
| 1904 |
|
| 1905 | #ifdef CONFIG_PM
|
| 1906 | if (suspended)
|
| 1907 | local->resuming = true;
|
| 1908 |
|
| 1909 | if (local->wowlan) {
|
| 1910 | |
| 1911 | * In the wowlan case, both mac80211 and the device
|
| 1912 | * are functional when the resume op is called, so
|
| 1913 | * clear local->suspended so the device could operate
|
| 1914 | * normally (e.g. pass rx frames).
|
| 1915 |
|
| 1916 | local->suspended = false;
|
| 1917 | res = drv_resume(local);
|
| 1918 | local->wowlan = false;
|
| 1919 | if (res < 0) {
|
| 1920 | local->resuming = false;
|
| 1921 | return res;
|
| 1922 | }
|
| 1923 | if (res == 0)
|
| 1924 | goto wake_up;
|
| 1925 | WARN_ON(res > 1);
|
| 1926 | |
| 1927 | * res is 1, which means the driver requested
|
| 1928 | * to go through a regular reset on wakeup.
|
| 1929 | * restore local->suspended in this case.
|
| 1930 |
|
| 1931 | reconfig_due_to_wowlan = true;
|
| 1932 | local->suspended = true;
|
| 1933 | }
|
| 1934 | #endif
|
| 1935 |
|
| 1936 | |
| 1937 | * In case of hw_restart during suspend (without wowlan),
|
| 1938 | * cancel restart work, as we are reconfiguring the device
|
| 1939 | * anyway.
|
| 1940 | * Note that restart_work is scheduled on a frozen workqueue,
|
| 1941 | * so we can't deadlock in this case.
|
| 1942 |
|
| 1943 | if (suspended && local->in_reconfig && !reconfig_due_to_wowlan)
|
| 1944 | cancel_work_sync(&local->restart_work);
|
| 1945 |
|
| 1946 | local->started = false;
|
| 1947 |
|
| 1948 | |
| 1949 | * Upon resume hardware can sometimes be goofy due to
|
| 1950 | * various platform / driver / bus issues, so restarting
|
| 1951 | * the device may at times not work immediately. Propagate
|
| 1952 | * the error.
|
| 1953 |
|
| 1954 | res = drv_start(local);
|
| 1955 | if (res) {
|
| 1956 | if (suspended)
|
| 1957 | WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
|
| 1958 | else
|
| 1959 | WARN(1, "Hardware became unavailable during restart.\n");
|
| 1960 | ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
|
| 1961 | IEEE80211_QUEUE_STOP_REASON_SUSPEND,
|
| 1962 | false);
|
| 1963 | ieee80211_handle_reconfig_failure(local);
|
| 1964 | return res;
|
| 1965 | }
|
| 1966 |
|
| 1967 |
|
| 1968 | drv_set_frag_threshold(local, -1, hw->wiphy->frag_threshold);
|
| 1969 |
|
| 1970 |
|
| 1971 | if (hw->wiphy->n_radio > 0) {
|
| 1972 | for (i = 0; i < hw->wiphy->n_radio; i++) {
|
| 1973 | u32 rts_threshold =
|
| 1974 | hw->wiphy->radio_cfg[i].rts_threshold;
|
| 1975 |
|
| 1976 | drv_set_rts_threshold(local, i, rts_threshold);
|
| 1977 | }
|
| 1978 | } else {
|
| 1979 | drv_set_rts_threshold(local, -1, hw->wiphy->rts_threshold);
|
| 1980 | }
|
| 1981 |
|
| 1982 |
|
| 1983 | drv_set_coverage_class(local, -1, hw->wiphy->coverage_class);
|
| 1984 |
|
| 1985 | ieee80211_led_radio(local, true);
|
| 1986 | ieee80211_mod_tpt_led_trig(local,
|
| 1987 | IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
|
| 1988 |
|
| 1989 |
|
| 1990 | sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
|
| 1991 | if (sdata && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) {
|
| 1992 |
|
| 1993 | WARN_ON(local->resuming);
|
| 1994 | res = drv_add_interface(local, sdata);
|
| 1995 | if (WARN_ON(res)) {
|
| 1996 | RCU_INIT_POINTER(local->monitor_sdata, NULL);
|
| 1997 | synchronize_net();
|
| 1998 | kfree(sdata);
|
| 1999 | }
|
| 2000 | }
|
| 2001 |
|
| 2002 | list_for_each_entry(sdata, &local->interfaces, list) {
|
| 2003 | if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
|
| 2004 | !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
|
| 2005 | continue;
|
| 2006 |
|
| 2007 | if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
|
| 2008 | continue;
|
| 2009 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
|
| 2010 | ieee80211_sdata_running(sdata)) {
|
| 2011 | res = drv_add_interface(local, sdata);
|
| 2012 | if (WARN_ON(res))
|
| 2013 | break;
|
| 2014 | }
|
| 2015 | }
|
| 2016 |
|
| 2017 | |
| 2018 | * report failure.
|
| 2019 |
|
| 2020 | if (res) {
|
| 2021 | list_for_each_entry_continue_reverse(sdata, &local->interfaces,
|
| 2022 | list) {
|
| 2023 | if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
|
| 2024 | !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
|
| 2025 | continue;
|
| 2026 | if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
|
| 2027 | continue;
|
| 2028 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
|
| 2029 | ieee80211_sdata_running(sdata))
|
| 2030 | drv_remove_interface(local, sdata);
|
| 2031 | }
|
| 2032 | ieee80211_handle_reconfig_failure(local);
|
| 2033 | return res;
|
| 2034 | }
|
| 2035 |
|
| 2036 |
|
| 2037 | list_for_each_entry(ctx, &local->chanctx_list, list)
|
| 2038 | if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
|
| 2039 | WARN_ON(drv_add_chanctx(local, ctx));
|
| 2040 |
|
| 2041 | sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
|
| 2042 | if (sdata && ieee80211_sdata_running(sdata))
|
| 2043 | ieee80211_assign_chanctx(local, sdata, &sdata->deflink);
|
| 2044 |
|
| 2045 |
|
| 2046 | ieee80211_hw_config(local, -1, IEEE80211_CONF_CHANGE_LISTEN_INTERVAL |
|
| 2047 | IEEE80211_CONF_CHANGE_MONITOR |
|
| 2048 | IEEE80211_CONF_CHANGE_PS |
|
| 2049 | IEEE80211_CONF_CHANGE_RETRY_LIMITS |
|
| 2050 | IEEE80211_CONF_CHANGE_IDLE);
|
| 2051 |
|
| 2052 | ieee80211_configure_filter(local);
|
| 2053 |
|
| 2054 |
|
| 2055 | list_for_each_entry(sdata, &local->interfaces, list) {
|
| 2056 |
|
| 2057 | u64 changed = BSS_CHANGED_ERP_CTS_PROT |
|
| 2058 | BSS_CHANGED_ERP_PREAMBLE |
|
| 2059 | BSS_CHANGED_ERP_SLOT |
|
| 2060 | BSS_CHANGED_HT |
|
| 2061 | BSS_CHANGED_BASIC_RATES |
|
| 2062 | BSS_CHANGED_BEACON_INT |
|
| 2063 | BSS_CHANGED_BSSID |
|
| 2064 | BSS_CHANGED_CQM |
|
| 2065 | BSS_CHANGED_QOS |
|
| 2066 | BSS_CHANGED_TXPOWER |
|
| 2067 | BSS_CHANGED_MCAST_RATE;
|
| 2068 | struct ieee80211_link_data *link = NULL;
|
| 2069 | unsigned int link_id;
|
| 2070 | u32 active_links = 0;
|
| 2071 |
|
| 2072 | if (!ieee80211_sdata_running(sdata))
|
| 2073 | continue;
|
| 2074 |
|
| 2075 | if (ieee80211_vif_is_mld(&sdata->vif)) {
|
| 2076 | struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS] = {
|
| 2077 | [0] = &sdata->vif.bss_conf,
|
| 2078 | };
|
| 2079 |
|
| 2080 | if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
| 2081 |
|
| 2082 | active_links = sdata->vif.active_links;
|
| 2083 | link_id = ffs(active_links) - 1;
|
| 2084 | sdata->vif.active_links = BIT(link_id);
|
| 2085 | }
|
| 2086 |
|
| 2087 | drv_change_vif_links(local, sdata, 0,
|
| 2088 | sdata->vif.active_links,
|
| 2089 | old);
|
| 2090 | }
|
| 2091 |
|
| 2092 | sdata->restart_active_links = active_links;
|
| 2093 |
|
| 2094 | for (link_id = 0;
|
| 2095 | link_id < ARRAY_SIZE(sdata->vif.link_conf);
|
| 2096 | link_id++) {
|
| 2097 | if (!ieee80211_vif_link_active(&sdata->vif, link_id))
|
| 2098 | continue;
|
| 2099 |
|
| 2100 | link = sdata_dereference(sdata->link[link_id], sdata);
|
| 2101 | if (!link)
|
| 2102 | continue;
|
| 2103 |
|
| 2104 | ieee80211_assign_chanctx(local, sdata, link);
|
| 2105 | }
|
| 2106 |
|
| 2107 | switch (sdata->vif.type) {
|
| 2108 | case NL80211_IFTYPE_AP_VLAN:
|
| 2109 | case NL80211_IFTYPE_MONITOR:
|
| 2110 | break;
|
| 2111 | case NL80211_IFTYPE_NAN:
|
| 2112 | case NL80211_IFTYPE_NAN_DATA:
|
| 2113 |
|
| 2114 | break;
|
| 2115 | case NL80211_IFTYPE_ADHOC:
|
| 2116 | if (sdata->vif.cfg.ibss_joined)
|
| 2117 | WARN_ON(drv_join_ibss(local, sdata));
|
| 2118 | fallthrough;
|
| 2119 | default:
|
| 2120 | ieee80211_reconfig_stations(sdata);
|
| 2121 | fallthrough;
|
| 2122 | case NL80211_IFTYPE_AP:
|
| 2123 | for (i = 0; i < IEEE80211_NUM_ACS; i++)
|
| 2124 | drv_conf_tx(local, &sdata->deflink, i,
|
| 2125 | &sdata->deflink.tx_conf[i]);
|
| 2126 | break;
|
| 2127 | }
|
| 2128 |
|
| 2129 | if (sdata->vif.bss_conf.mu_mimo_owner)
|
| 2130 | changed |= BSS_CHANGED_MU_GROUPS;
|
| 2131 |
|
| 2132 | if (!ieee80211_vif_is_mld(&sdata->vif))
|
| 2133 | changed |= BSS_CHANGED_IDLE;
|
| 2134 |
|
| 2135 | switch (sdata->vif.type) {
|
| 2136 | case NL80211_IFTYPE_STATION:
|
| 2137 | if (!ieee80211_vif_is_mld(&sdata->vif)) {
|
| 2138 | changed |= BSS_CHANGED_ASSOC |
|
| 2139 | BSS_CHANGED_ARP_FILTER |
|
| 2140 | BSS_CHANGED_PS;
|
| 2141 |
|
| 2142 |
|
| 2143 | if (sdata->deflink.u.mgd.have_beacon)
|
| 2144 | changed |= BSS_CHANGED_BEACON_INFO;
|
| 2145 |
|
| 2146 | if (sdata->vif.bss_conf.max_idle_period ||
|
| 2147 | sdata->vif.bss_conf.protected_keep_alive)
|
| 2148 | changed |= BSS_CHANGED_KEEP_ALIVE;
|
| 2149 |
|
| 2150 | ieee80211_bss_info_change_notify(sdata,
|
| 2151 | changed);
|
| 2152 | } else if (!WARN_ON(!link)) {
|
| 2153 | if (link->conf->npca.enabled)
|
| 2154 | changed |= BSS_CHANGED_NPCA;
|
| 2155 |
|
| 2156 | ieee80211_link_info_change_notify(sdata, link,
|
| 2157 | changed);
|
| 2158 | changed = BSS_CHANGED_ASSOC |
|
| 2159 | BSS_CHANGED_IDLE |
|
| 2160 | BSS_CHANGED_PS |
|
| 2161 | BSS_CHANGED_ARP_FILTER;
|
| 2162 | ieee80211_vif_cfg_change_notify(sdata, changed);
|
| 2163 | }
|
| 2164 | break;
|
| 2165 | case NL80211_IFTYPE_OCB:
|
| 2166 | changed |= BSS_CHANGED_OCB;
|
| 2167 | ieee80211_bss_info_change_notify(sdata, changed);
|
| 2168 | break;
|
| 2169 | case NL80211_IFTYPE_ADHOC:
|
| 2170 | changed |= BSS_CHANGED_IBSS;
|
| 2171 | fallthrough;
|
| 2172 | case NL80211_IFTYPE_AP:
|
| 2173 | changed |= BSS_CHANGED_P2P_PS;
|
| 2174 |
|
| 2175 | if (ieee80211_vif_is_mld(&sdata->vif))
|
| 2176 | ieee80211_vif_cfg_change_notify(sdata,
|
| 2177 | BSS_CHANGED_SSID);
|
| 2178 | else
|
| 2179 | changed |= BSS_CHANGED_SSID;
|
| 2180 |
|
| 2181 | if (sdata->vif.bss_conf.ftm_responder == 1 &&
|
| 2182 | wiphy_ext_feature_isset(sdata->local->hw.wiphy,
|
| 2183 | NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
|
| 2184 | changed |= BSS_CHANGED_FTM_RESPONDER;
|
| 2185 |
|
| 2186 | if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
| 2187 | changed |= BSS_CHANGED_AP_PROBE_RESP;
|
| 2188 |
|
| 2189 | if (ieee80211_vif_is_mld(&sdata->vif)) {
|
| 2190 | ieee80211_reconfig_ap_links(local,
|
| 2191 | sdata,
|
| 2192 | changed);
|
| 2193 | break;
|
| 2194 | }
|
| 2195 |
|
| 2196 | if (rcu_access_pointer(sdata->deflink.u.ap.beacon))
|
| 2197 | drv_start_ap(local, sdata,
|
| 2198 | sdata->deflink.conf);
|
| 2199 | }
|
| 2200 | fallthrough;
|
| 2201 | case NL80211_IFTYPE_MESH_POINT:
|
| 2202 | if (sdata->vif.bss_conf.enable_beacon) {
|
| 2203 | changed |= BSS_CHANGED_BEACON |
|
| 2204 | BSS_CHANGED_BEACON_ENABLED;
|
| 2205 | ieee80211_bss_info_change_notify(sdata, changed);
|
| 2206 | }
|
| 2207 | break;
|
| 2208 | case NL80211_IFTYPE_NAN:
|
| 2209 | WARN_ON(ieee80211_reconfig_nan(sdata));
|
| 2210 | break;
|
| 2211 | case NL80211_IFTYPE_NAN_DATA:
|
| 2212 | case NL80211_IFTYPE_AP_VLAN:
|
| 2213 | case NL80211_IFTYPE_MONITOR:
|
| 2214 | case NL80211_IFTYPE_P2P_DEVICE:
|
| 2215 | case NL80211_IFTYPE_PD:
|
| 2216 |
|
| 2217 | break;
|
| 2218 | case NL80211_IFTYPE_UNSPECIFIED:
|
| 2219 | case NUM_NL80211_IFTYPES:
|
| 2220 | case NL80211_IFTYPE_P2P_CLIENT:
|
| 2221 | case NL80211_IFTYPE_P2P_GO:
|
| 2222 | case NL80211_IFTYPE_WDS:
|
| 2223 | WARN_ON(1);
|
| 2224 | break;
|
| 2225 | }
|
| 2226 | }
|
| 2227 |
|
| 2228 | ieee80211_recalc_ps(local);
|
| 2229 |
|
| 2230 | |
| 2231 | * The sta might be in psm against the ap (e.g. because
|
| 2232 | * this was the state before a hw restart), so we
|
| 2233 | * explicitly send a null packet in order to make sure
|
| 2234 | * it'll sync against the ap (and get out of psm).
|
| 2235 |
|
| 2236 | if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) {
|
| 2237 | list_for_each_entry(sdata, &local->interfaces, list) {
|
| 2238 | if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
| 2239 | continue;
|
| 2240 | if (!sdata->u.mgd.associated)
|
| 2241 | continue;
|
| 2242 |
|
| 2243 | ieee80211_send_nullfunc(local, sdata, false);
|
| 2244 | }
|
| 2245 | }
|
| 2246 |
|
| 2247 |
|
| 2248 | list_for_each_entry(sdata, &local->interfaces, list) {
|
| 2249 | if (!ieee80211_sdata_running(sdata))
|
| 2250 | continue;
|
| 2251 |
|
| 2252 | switch (sdata->vif.type) {
|
| 2253 | case NL80211_IFTYPE_AP_VLAN:
|
| 2254 | case NL80211_IFTYPE_AP:
|
| 2255 | ieee80211_reconfig_stations(sdata);
|
| 2256 | break;
|
| 2257 | default:
|
| 2258 | break;
|
| 2259 | }
|
| 2260 | }
|
| 2261 |
|
| 2262 |
|
| 2263 | list_for_each_entry(sdata, &local->interfaces, list)
|
| 2264 | ieee80211_reenable_keys(sdata);
|
| 2265 |
|
| 2266 |
|
| 2267 | list_for_each_entry(sdata, &local->interfaces, list) {
|
| 2268 | if (sdata->restart_active_links)
|
| 2269 | ieee80211_set_active_links(&sdata->vif,
|
| 2270 | sdata->restart_active_links);
|
| 2271 | |
| 2272 | * If a link switch was scheduled before the restart, and ran
|
| 2273 | * before reconfig, it will do nothing, so re-schedule.
|
| 2274 |
|
| 2275 | if (sdata->desired_active_links)
|
| 2276 | wiphy_work_queue(sdata->local->hw.wiphy,
|
| 2277 | &sdata->activate_links_work);
|
| 2278 | }
|
| 2279 |
|
| 2280 |
|
| 2281 | sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
|
| 2282 | lockdep_is_held(&local->hw.wiphy->mtx));
|
| 2283 | sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
|
| 2284 | lockdep_is_held(&local->hw.wiphy->mtx));
|
| 2285 | if (sched_scan_sdata && sched_scan_req)
|
| 2286 | |
| 2287 | * Sched scan stopped, but we don't want to report it. Instead,
|
| 2288 | * we're trying to reschedule. However, if more than one scan
|
| 2289 | * plan was set, we cannot reschedule since we don't know which
|
| 2290 | * scan plan was currently running (and some scan plans may have
|
| 2291 | * already finished).
|
| 2292 |
|
| 2293 | if (sched_scan_req->n_scan_plans > 1 ||
|
| 2294 | __ieee80211_request_sched_scan_start(sched_scan_sdata,
|
| 2295 | sched_scan_req)) {
|
| 2296 | RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
|
| 2297 | RCU_INIT_POINTER(local->sched_scan_req, NULL);
|
| 2298 | sched_scan_stopped = true;
|
| 2299 | }
|
| 2300 |
|
| 2301 | if (sched_scan_stopped)
|
| 2302 | cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0);
|
| 2303 |
|
| 2304 | wake_up:
|
| 2305 | |
| 2306 | * Clear the WLAN_STA_BLOCK_BA flag so new aggregation
|
| 2307 | * sessions can be established after a resume.
|
| 2308 | *
|
| 2309 | * Also tear down aggregation sessions since reconfiguring
|
| 2310 | * them in a hardware restart scenario is not easily done
|
| 2311 | * right now, and the hardware will have lost information
|
| 2312 | * about the sessions, but we and the AP still think they
|
| 2313 | * are active. This is really a workaround though.
|
| 2314 |
|
| 2315 | if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) {
|
| 2316 | list_for_each_entry(sta, &local->sta_list, list) {
|
| 2317 | if (!local->resuming)
|
| 2318 | ieee80211_sta_tear_down_BA_sessions(
|
| 2319 | sta, AGG_STOP_LOCAL_REQUEST);
|
| 2320 | clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
|
| 2321 | }
|
| 2322 | }
|
| 2323 |
|
| 2324 | |
| 2325 | * If this is for hw restart things are still running.
|
| 2326 | * We may want to change that later, however.
|
| 2327 |
|
| 2328 | if (local->open_count && (!suspended || reconfig_due_to_wowlan))
|
| 2329 | drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
|
| 2330 |
|
| 2331 | if (local->in_reconfig) {
|
| 2332 | in_reconfig = local->in_reconfig;
|
| 2333 | local->in_reconfig = false;
|
| 2334 | barrier();
|
| 2335 |
|
| 2336 | ieee80211_reconfig_roc(local);
|
| 2337 |
|
| 2338 |
|
| 2339 | list_for_each_entry(sdata, &local->interfaces, list) {
|
| 2340 | if (ieee80211_sdata_running(sdata))
|
| 2341 | wiphy_work_queue(local->hw.wiphy, &sdata->work);
|
| 2342 | }
|
| 2343 | }
|
| 2344 |
|
| 2345 | ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
|
| 2346 | IEEE80211_QUEUE_STOP_REASON_SUSPEND,
|
| 2347 | false);
|
| 2348 |
|
| 2349 | if (in_reconfig) {
|
| 2350 | list_for_each_entry(sdata, &local->interfaces, list) {
|
| 2351 | if (!ieee80211_sdata_running(sdata))
|
| 2352 | continue;
|
| 2353 | if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
| 2354 | ieee80211_sta_restart(sdata);
|
| 2355 | }
|
| 2356 | }
|
| 2357 |
|
| 2358 |
|
| 2359 | if (local->virt_monitors > 0 &&
|
| 2360 | local->virt_monitors == local->open_count)
|
| 2361 | ieee80211_add_virtual_monitor(local, NULL);
|
| 2362 |
|
| 2363 | if (!suspended)
|
| 2364 | return 0;
|
| 2365 |
|
| 2366 | #ifdef CONFIG_PM
|
| 2367 |
|
| 2368 | local->suspended = false;
|
| 2369 | mb();
|
| 2370 | local->resuming = false;
|
| 2371 |
|
| 2372 | ieee80211_flush_completed_scan(local, false);
|
| 2373 |
|
| 2374 | if (local->open_count && !reconfig_due_to_wowlan)
|
| 2375 | drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
|
| 2376 |
|
| 2377 | list_for_each_entry(sdata, &local->interfaces, list) {
|
| 2378 | if (!ieee80211_sdata_running(sdata))
|
| 2379 | continue;
|
| 2380 | if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
| 2381 | ieee80211_sta_restart(sdata);
|
| 2382 | }
|
| 2383 |
|
| 2384 | mod_timer(&local->sta_cleanup, jiffies + 1);
|
| 2385 | #else
|
| 2386 | WARN_ON(1);
|
| 2387 | #endif
|
| 2388 |
|
| 2389 | return 0;
|
| 2390 | }
|
| 2391 |
|
| 2392 | static void ieee80211_reconfig_disconnect(struct ieee80211_vif *vif, u8 flag)
|
| 2393 | {
|
| 2394 | struct ieee80211_sub_if_data *sdata;
|
| 2395 | struct ieee80211_local *local;
|
| 2396 | struct ieee80211_key *key;
|
| 2397 |
|
| 2398 | if (WARN_ON(!vif))
|
| 2399 | return;
|
| 2400 |
|
| 2401 | sdata = vif_to_sdata(vif);
|
| 2402 | local = sdata->local;
|
| 2403 |
|
| 2404 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 2405 |
|
| 2406 | if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_RESUME &&
|
| 2407 | !local->resuming))
|
| 2408 | return;
|
| 2409 |
|
| 2410 | if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_HW_RESTART &&
|
| 2411 | !local->in_reconfig))
|
| 2412 | return;
|
| 2413 |
|
| 2414 | if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
|
| 2415 | return;
|
| 2416 |
|
| 2417 | sdata->flags |= flag;
|
| 2418 |
|
| 2419 | list_for_each_entry(key, &sdata->key_list, list)
|
| 2420 | key->flags |= KEY_FLAG_TAINTED;
|
| 2421 | }
|
| 2422 |
|
| 2423 | void ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif)
|
| 2424 | {
|
| 2425 | ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_HW_RESTART);
|
| 2426 | }
|
| 2427 | EXPORT_SYMBOL_GPL(ieee80211_hw_restart_disconnect);
|
| 2428 |
|
| 2429 | void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
|
| 2430 | {
|
| 2431 | ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_RESUME);
|
| 2432 | }
|
| 2433 | EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
|
| 2434 |
|
| 2435 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata,
|
| 2436 | struct ieee80211_link_data *link)
|
| 2437 | {
|
| 2438 | struct ieee80211_local *local = sdata->local;
|
| 2439 | struct ieee80211_chanctx_conf *chanctx_conf;
|
| 2440 | struct ieee80211_chanctx *chanctx;
|
| 2441 |
|
| 2442 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 2443 |
|
| 2444 | chanctx_conf = rcu_dereference_protected(link->conf->chanctx_conf,
|
| 2445 | lockdep_is_held(&local->hw.wiphy->mtx));
|
| 2446 |
|
| 2447 | |
| 2448 | * This function can be called from a work, thus it may be possible
|
| 2449 | * that the chanctx_conf is removed (due to a disconnection, for
|
| 2450 | * example).
|
| 2451 | * So nothing should be done in such case.
|
| 2452 |
|
| 2453 | if (!chanctx_conf)
|
| 2454 | return;
|
| 2455 |
|
| 2456 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
| 2457 | ieee80211_recalc_smps_chanctx(local, chanctx);
|
| 2458 | }
|
| 2459 |
|
| 2460 | void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata,
|
| 2461 | int link_id)
|
| 2462 | {
|
| 2463 | struct ieee80211_local *local = sdata->local;
|
| 2464 | struct ieee80211_chanctx_conf *chanctx_conf;
|
| 2465 | struct ieee80211_chanctx *chanctx;
|
| 2466 | int i;
|
| 2467 |
|
| 2468 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 2469 |
|
| 2470 | for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) {
|
| 2471 | struct ieee80211_bss_conf *bss_conf;
|
| 2472 |
|
| 2473 | if (link_id >= 0 && link_id != i)
|
| 2474 | continue;
|
| 2475 |
|
| 2476 | rcu_read_lock();
|
| 2477 | bss_conf = rcu_dereference(sdata->vif.link_conf[i]);
|
| 2478 | if (!bss_conf) {
|
| 2479 | rcu_read_unlock();
|
| 2480 | continue;
|
| 2481 | }
|
| 2482 |
|
| 2483 | chanctx_conf = rcu_dereference_protected(bss_conf->chanctx_conf,
|
| 2484 | lockdep_is_held(&local->hw.wiphy->mtx));
|
| 2485 | |
| 2486 | * Since we hold the wiphy mutex (checked above)
|
| 2487 | * we can take the chanctx_conf pointer out of the
|
| 2488 | * RCU critical section, it cannot go away without
|
| 2489 | * the mutex. Just the way we reached it could - in
|
| 2490 | * theory - go away, but we don't really care and
|
| 2491 | * it really shouldn't happen anyway.
|
| 2492 |
|
| 2493 | rcu_read_unlock();
|
| 2494 |
|
| 2495 | if (!chanctx_conf)
|
| 2496 | return;
|
| 2497 |
|
| 2498 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx,
|
| 2499 | conf);
|
| 2500 | ieee80211_recalc_chanctx_min_def(local, chanctx);
|
| 2501 | }
|
| 2502 | }
|
| 2503 |
|
| 2504 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
|
| 2505 | {
|
| 2506 | size_t pos = offset;
|
| 2507 |
|
| 2508 | while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC)
|
| 2509 | pos += 2 + ies[pos + 1];
|
| 2510 |
|
| 2511 | return pos;
|
| 2512 | }
|
| 2513 |
|
| 2514 | u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
|
| 2515 | u16 cap)
|
| 2516 | {
|
| 2517 | __le16 tmp;
|
| 2518 |
|
| 2519 | *pos++ = WLAN_EID_HT_CAPABILITY;
|
| 2520 | *pos++ = sizeof(struct ieee80211_ht_cap);
|
| 2521 | memset(pos, 0, sizeof(struct ieee80211_ht_cap));
|
| 2522 |
|
| 2523 |
|
| 2524 | tmp = cpu_to_le16(cap);
|
| 2525 | memcpy(pos, &tmp, sizeof(u16));
|
| 2526 | pos += sizeof(u16);
|
| 2527 |
|
| 2528 |
|
| 2529 | *pos++ = ht_cap->ampdu_factor |
|
| 2530 | (ht_cap->ampdu_density <<
|
| 2531 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
|
| 2532 |
|
| 2533 |
|
| 2534 | memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
|
| 2535 | pos += sizeof(ht_cap->mcs);
|
| 2536 |
|
| 2537 |
|
| 2538 | pos += sizeof(__le16);
|
| 2539 |
|
| 2540 |
|
| 2541 | pos += sizeof(__le32);
|
| 2542 |
|
| 2543 |
|
| 2544 | pos += sizeof(u8);
|
| 2545 |
|
| 2546 | return pos;
|
| 2547 | }
|
| 2548 |
|
| 2549 | u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
|
| 2550 | u32 cap)
|
| 2551 | {
|
| 2552 | __le32 tmp;
|
| 2553 |
|
| 2554 | *pos++ = WLAN_EID_VHT_CAPABILITY;
|
| 2555 | *pos++ = sizeof(struct ieee80211_vht_cap);
|
| 2556 | memset(pos, 0, sizeof(struct ieee80211_vht_cap));
|
| 2557 |
|
| 2558 |
|
| 2559 | tmp = cpu_to_le32(cap);
|
| 2560 | memcpy(pos, &tmp, sizeof(u32));
|
| 2561 | pos += sizeof(u32);
|
| 2562 |
|
| 2563 |
|
| 2564 | memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
|
| 2565 | pos += sizeof(vht_cap->vht_mcs);
|
| 2566 |
|
| 2567 | return pos;
|
| 2568 | }
|
| 2569 |
|
| 2570 |
|
| 2571 | u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata)
|
| 2572 | {
|
| 2573 | const struct ieee80211_sta_he_cap *he_cap;
|
| 2574 | struct ieee80211_supported_band *sband;
|
| 2575 | u8 n;
|
| 2576 |
|
| 2577 | sband = ieee80211_get_sband(sdata);
|
| 2578 | if (!sband)
|
| 2579 | return 0;
|
| 2580 |
|
| 2581 | he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
|
| 2582 | if (!he_cap)
|
| 2583 | return 0;
|
| 2584 |
|
| 2585 | n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem);
|
| 2586 | return 2 + 1 +
|
| 2587 | sizeof(he_cap->he_cap_elem) + n +
|
| 2588 | ieee80211_he_ppe_size(he_cap->ppe_thres[0],
|
| 2589 | he_cap->he_cap_elem.phy_cap_info);
|
| 2590 | }
|
| 2591 |
|
| 2592 | static void
|
| 2593 | ieee80211_get_adjusted_he_cap(const struct ieee80211_conn_settings *conn,
|
| 2594 | const struct ieee80211_sta_he_cap *he_cap,
|
| 2595 | struct ieee80211_he_cap_elem *elem)
|
| 2596 | {
|
| 2597 | u8 ru_limit, max_ru;
|
| 2598 |
|
| 2599 | *elem = he_cap->he_cap_elem;
|
| 2600 |
|
| 2601 | switch (conn->bw_limit) {
|
| 2602 | case IEEE80211_CONN_BW_LIMIT_20:
|
| 2603 | ru_limit = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242;
|
| 2604 | break;
|
| 2605 | case IEEE80211_CONN_BW_LIMIT_40:
|
| 2606 | ru_limit = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484;
|
| 2607 | break;
|
| 2608 | case IEEE80211_CONN_BW_LIMIT_80:
|
| 2609 | ru_limit = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996;
|
| 2610 | break;
|
| 2611 | default:
|
| 2612 | ru_limit = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996;
|
| 2613 | break;
|
| 2614 | }
|
| 2615 |
|
| 2616 | max_ru = elem->phy_cap_info[8] & IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK;
|
| 2617 | max_ru = min(max_ru, ru_limit);
|
| 2618 | elem->phy_cap_info[8] &= ~IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK;
|
| 2619 | elem->phy_cap_info[8] |= max_ru;
|
| 2620 |
|
| 2621 | if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) {
|
| 2622 | elem->phy_cap_info[0] &=
|
| 2623 | ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
|
| 2624 | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G);
|
| 2625 | elem->phy_cap_info[9] &=
|
| 2626 | ~IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM;
|
| 2627 | }
|
| 2628 |
|
| 2629 | if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160) {
|
| 2630 | elem->phy_cap_info[0] &=
|
| 2631 | ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
|
| 2632 | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G);
|
| 2633 | elem->phy_cap_info[5] &=
|
| 2634 | ~IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK;
|
| 2635 | elem->phy_cap_info[7] &=
|
| 2636 | ~(IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ |
|
| 2637 | IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ);
|
| 2638 | }
|
| 2639 | }
|
| 2640 |
|
| 2641 | int ieee80211_put_he_cap(struct sk_buff *skb,
|
| 2642 | struct ieee80211_sub_if_data *sdata,
|
| 2643 | const struct ieee80211_supported_band *sband,
|
| 2644 | const struct ieee80211_conn_settings *conn)
|
| 2645 | {
|
| 2646 | const struct ieee80211_sta_he_cap *he_cap;
|
| 2647 | struct ieee80211_he_cap_elem elem;
|
| 2648 | u8 *len;
|
| 2649 | u8 n;
|
| 2650 | u8 ie_len;
|
| 2651 |
|
| 2652 | if (!conn)
|
| 2653 | conn = &ieee80211_conn_settings_unlimited;
|
| 2654 |
|
| 2655 | he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
|
| 2656 | if (!he_cap)
|
| 2657 | return 0;
|
| 2658 |
|
| 2659 |
|
| 2660 | ieee80211_get_adjusted_he_cap(conn, he_cap, &elem);
|
| 2661 |
|
| 2662 | n = ieee80211_he_mcs_nss_size(&elem);
|
| 2663 | ie_len = 2 + 1 +
|
| 2664 | sizeof(he_cap->he_cap_elem) + n +
|
| 2665 | ieee80211_he_ppe_size(he_cap->ppe_thres[0],
|
| 2666 | he_cap->he_cap_elem.phy_cap_info);
|
| 2667 |
|
| 2668 | if (skb_tailroom(skb) < ie_len)
|
| 2669 | return -ENOBUFS;
|
| 2670 |
|
| 2671 | skb_put_u8(skb, WLAN_EID_EXTENSION);
|
| 2672 | len = skb_put(skb, 1);
|
| 2673 | skb_put_u8(skb, WLAN_EID_EXT_HE_CAPABILITY);
|
| 2674 |
|
| 2675 |
|
| 2676 | skb_put_data(skb, &elem, sizeof(elem));
|
| 2677 |
|
| 2678 | skb_put_data(skb, &he_cap->he_mcs_nss_supp, n);
|
| 2679 |
|
| 2680 |
|
| 2681 | if ((he_cap->he_cap_elem.phy_cap_info[6] &
|
| 2682 | IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0)
|
| 2683 | goto end;
|
| 2684 |
|
| 2685 | |
| 2686 | * Calculate how many PPET16/PPET8 pairs are to come. Algorithm:
|
| 2687 | * (NSS_M1 + 1) x (num of 1 bits in RU_INDEX_BITMASK)
|
| 2688 |
|
| 2689 | n = hweight8(he_cap->ppe_thres[0] &
|
| 2690 | IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK);
|
| 2691 | n *= (1 + ((he_cap->ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) >>
|
| 2692 | IEEE80211_PPE_THRES_NSS_POS));
|
| 2693 |
|
| 2694 | |
| 2695 | * Each pair is 6 bits, and we need to add the 7 "header" bits to the
|
| 2696 | * total size.
|
| 2697 |
|
| 2698 | n = (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) + 7;
|
| 2699 | n = DIV_ROUND_UP(n, 8);
|
| 2700 |
|
| 2701 |
|
| 2702 | skb_put_data(skb, &he_cap->ppe_thres, n);
|
| 2703 |
|
| 2704 | end:
|
| 2705 | *len = skb_tail_pointer(skb) - len - 1;
|
| 2706 | return 0;
|
| 2707 | }
|
| 2708 |
|
| 2709 | void ieee80211_put_reg_conn(struct ieee80211_sub_if_data *sdata,
|
| 2710 | struct sk_buff *skb)
|
| 2711 | {
|
| 2712 | struct ieee80211_local *local = sdata->local;
|
| 2713 | u8 reg_conn = IEEE80211_REG_CONN_LPI_VALID |
|
| 2714 | IEEE80211_REG_CONN_LPI_VALUE |
|
| 2715 | IEEE80211_REG_CONN_SP_VALID;
|
| 2716 | struct ieee80211_supported_band *sband;
|
| 2717 | bool available_channels = false;
|
| 2718 | u32 flags = 0;
|
| 2719 | int i;
|
| 2720 |
|
| 2721 | sband = local->hw.wiphy->bands[NL80211_BAND_6GHZ];
|
| 2722 | if (!sband)
|
| 2723 | return;
|
| 2724 |
|
| 2725 | for (i = 0; i < sband->n_channels; i++) {
|
| 2726 | if (sband->channels[i].flags & IEEE80211_CHAN_DISABLED)
|
| 2727 | continue;
|
| 2728 | flags |= sband->channels[i].flags;
|
| 2729 | available_channels = true;
|
| 2730 | }
|
| 2731 |
|
| 2732 | if (!available_channels)
|
| 2733 | return;
|
| 2734 |
|
| 2735 | if (!(flags & IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT))
|
| 2736 | reg_conn |= IEEE80211_REG_CONN_SP_VALUE;
|
| 2737 |
|
| 2738 | skb_put_u8(skb, WLAN_EID_EXTENSION);
|
| 2739 | skb_put_u8(skb, 1 + sizeof(reg_conn));
|
| 2740 | skb_put_u8(skb, WLAN_EID_EXT_NON_AP_STA_REG_CON);
|
| 2741 | skb_put_u8(skb, reg_conn);
|
| 2742 | }
|
| 2743 |
|
| 2744 | int ieee80211_put_he_6ghz_cap(struct sk_buff *skb,
|
| 2745 | struct ieee80211_sub_if_data *sdata,
|
| 2746 | enum ieee80211_smps_mode smps_mode)
|
| 2747 | {
|
| 2748 | struct ieee80211_supported_band *sband;
|
| 2749 | const struct ieee80211_sband_iftype_data *iftd;
|
| 2750 | enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif);
|
| 2751 | __le16 cap;
|
| 2752 |
|
| 2753 | if (!cfg80211_any_usable_channels(sdata->local->hw.wiphy,
|
| 2754 | BIT(NL80211_BAND_6GHZ),
|
| 2755 | IEEE80211_CHAN_NO_HE))
|
| 2756 | return 0;
|
| 2757 |
|
| 2758 | sband = sdata->local->hw.wiphy->bands[NL80211_BAND_6GHZ];
|
| 2759 |
|
| 2760 | iftd = ieee80211_get_sband_iftype_data(sband, iftype);
|
| 2761 | if (!iftd)
|
| 2762 | return 0;
|
| 2763 |
|
| 2764 |
|
| 2765 | if (!iftd->he_6ghz_capa.capa)
|
| 2766 | return 0;
|
| 2767 |
|
| 2768 | cap = iftd->he_6ghz_capa.capa;
|
| 2769 | cap &= cpu_to_le16(~IEEE80211_HE_6GHZ_CAP_SM_PS);
|
| 2770 |
|
| 2771 | switch (smps_mode) {
|
| 2772 | case IEEE80211_SMPS_AUTOMATIC:
|
| 2773 | case IEEE80211_SMPS_NUM_MODES:
|
| 2774 | WARN_ON(1);
|
| 2775 | fallthrough;
|
| 2776 | case IEEE80211_SMPS_OFF:
|
| 2777 | cap |= le16_encode_bits(WLAN_HT_CAP_SM_PS_DISABLED,
|
| 2778 | IEEE80211_HE_6GHZ_CAP_SM_PS);
|
| 2779 | break;
|
| 2780 | case IEEE80211_SMPS_STATIC:
|
| 2781 | cap |= le16_encode_bits(WLAN_HT_CAP_SM_PS_STATIC,
|
| 2782 | IEEE80211_HE_6GHZ_CAP_SM_PS);
|
| 2783 | break;
|
| 2784 | case IEEE80211_SMPS_DYNAMIC:
|
| 2785 | cap |= le16_encode_bits(WLAN_HT_CAP_SM_PS_DYNAMIC,
|
| 2786 | IEEE80211_HE_6GHZ_CAP_SM_PS);
|
| 2787 | break;
|
| 2788 | }
|
| 2789 |
|
| 2790 | if (skb_tailroom(skb) < 2 + 1 + sizeof(cap))
|
| 2791 | return -ENOBUFS;
|
| 2792 |
|
| 2793 | skb_put_u8(skb, WLAN_EID_EXTENSION);
|
| 2794 | skb_put_u8(skb, 1 + sizeof(cap));
|
| 2795 | skb_put_u8(skb, WLAN_EID_EXT_HE_6GHZ_CAPA);
|
| 2796 | skb_put_data(skb, &cap, sizeof(cap));
|
| 2797 | return 0;
|
| 2798 | }
|
| 2799 |
|
| 2800 | u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
|
| 2801 | const struct cfg80211_chan_def *chandef,
|
| 2802 | u16 prot_mode, bool rifs_mode)
|
| 2803 | {
|
| 2804 | struct ieee80211_ht_operation *ht_oper;
|
| 2805 |
|
| 2806 | *pos++ = WLAN_EID_HT_OPERATION;
|
| 2807 | *pos++ = sizeof(struct ieee80211_ht_operation);
|
| 2808 | ht_oper = (struct ieee80211_ht_operation *)pos;
|
| 2809 | ht_oper->primary_chan = ieee80211_frequency_to_channel(
|
| 2810 | chandef->chan->center_freq);
|
| 2811 | switch (chandef->width) {
|
| 2812 | case NL80211_CHAN_WIDTH_160:
|
| 2813 | case NL80211_CHAN_WIDTH_80P80:
|
| 2814 | case NL80211_CHAN_WIDTH_80:
|
| 2815 | case NL80211_CHAN_WIDTH_40:
|
| 2816 | if (chandef->center_freq1 > chandef->chan->center_freq)
|
| 2817 | ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
| 2818 | else
|
| 2819 | ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
| 2820 | break;
|
| 2821 | case NL80211_CHAN_WIDTH_320:
|
| 2822 |
|
| 2823 | WARN_ON(1);
|
| 2824 | return pos;
|
| 2825 | default:
|
| 2826 | ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
|
| 2827 | break;
|
| 2828 | }
|
| 2829 | if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
|
| 2830 | chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
|
| 2831 | chandef->width != NL80211_CHAN_WIDTH_20)
|
| 2832 | ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
|
| 2833 |
|
| 2834 | if (rifs_mode)
|
| 2835 | ht_oper->ht_param |= IEEE80211_HT_PARAM_RIFS_MODE;
|
| 2836 |
|
| 2837 | ht_oper->operation_mode = cpu_to_le16(prot_mode);
|
| 2838 | ht_oper->stbc_param = 0x0000;
|
| 2839 |
|
| 2840 | |
| 2841 | are identical for the first 10 bytes
|
| 2842 | memset(&ht_oper->basic_set, 0, 16);
|
| 2843 | memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
|
| 2844 |
|
| 2845 | return pos + sizeof(struct ieee80211_ht_operation);
|
| 2846 | }
|
| 2847 |
|
| 2848 | void ieee80211_ie_build_wide_bw_cs(u8 *pos,
|
| 2849 | const struct cfg80211_chan_def *chandef)
|
| 2850 | {
|
| 2851 | *pos++ = WLAN_EID_WIDE_BW_CHANNEL_SWITCH;
|
| 2852 | *pos++ = 3;
|
| 2853 |
|
| 2854 | switch (chandef->width) {
|
| 2855 | case NL80211_CHAN_WIDTH_80:
|
| 2856 | *pos++ = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
| 2857 | break;
|
| 2858 | case NL80211_CHAN_WIDTH_160:
|
| 2859 | *pos++ = IEEE80211_VHT_CHANWIDTH_160MHZ;
|
| 2860 | break;
|
| 2861 | case NL80211_CHAN_WIDTH_80P80:
|
| 2862 | *pos++ = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
|
| 2863 | break;
|
| 2864 | case NL80211_CHAN_WIDTH_320:
|
| 2865 |
|
| 2866 | WARN_ON(1);
|
| 2867 | fallthrough;
|
| 2868 | default:
|
| 2869 | *pos++ = IEEE80211_VHT_CHANWIDTH_USE_HT;
|
| 2870 | }
|
| 2871 |
|
| 2872 |
|
| 2873 | *pos++ = ieee80211_frequency_to_channel(chandef->center_freq1);
|
| 2874 |
|
| 2875 | if (chandef->center_freq2)
|
| 2876 | *pos++ = ieee80211_frequency_to_channel(chandef->center_freq2);
|
| 2877 | else
|
| 2878 | *pos++ = 0;
|
| 2879 | }
|
| 2880 |
|
| 2881 | u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
|
| 2882 | const struct cfg80211_chan_def *chandef)
|
| 2883 | {
|
| 2884 | struct ieee80211_vht_operation *vht_oper;
|
| 2885 |
|
| 2886 | *pos++ = WLAN_EID_VHT_OPERATION;
|
| 2887 | *pos++ = sizeof(struct ieee80211_vht_operation);
|
| 2888 | vht_oper = (struct ieee80211_vht_operation *)pos;
|
| 2889 | vht_oper->center_freq_seg0_idx = ieee80211_frequency_to_channel(
|
| 2890 | chandef->center_freq1);
|
| 2891 | if (chandef->center_freq2)
|
| 2892 | vht_oper->center_freq_seg1_idx =
|
| 2893 | ieee80211_frequency_to_channel(chandef->center_freq2);
|
| 2894 | else
|
| 2895 | vht_oper->center_freq_seg1_idx = 0x00;
|
| 2896 |
|
| 2897 | switch (chandef->width) {
|
| 2898 | case NL80211_CHAN_WIDTH_160:
|
| 2899 | |
| 2900 | * Convert 160 MHz channel width to new style as interop
|
| 2901 | * workaround.
|
| 2902 |
|
| 2903 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
| 2904 | vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx;
|
| 2905 | if (chandef->chan->center_freq < chandef->center_freq1)
|
| 2906 | vht_oper->center_freq_seg0_idx -= 8;
|
| 2907 | else
|
| 2908 | vht_oper->center_freq_seg0_idx += 8;
|
| 2909 | break;
|
| 2910 | case NL80211_CHAN_WIDTH_80P80:
|
| 2911 | |
| 2912 | * Convert 80+80 MHz channel width to new style as interop
|
| 2913 | * workaround.
|
| 2914 |
|
| 2915 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
| 2916 | break;
|
| 2917 | case NL80211_CHAN_WIDTH_80:
|
| 2918 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
| 2919 | break;
|
| 2920 | case NL80211_CHAN_WIDTH_320:
|
| 2921 |
|
| 2922 | WARN_ON(1);
|
| 2923 | return pos;
|
| 2924 | default:
|
| 2925 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
|
| 2926 | break;
|
| 2927 | }
|
| 2928 |
|
| 2929 |
|
| 2930 | vht_oper->basic_mcs_set = cpu_to_le16(0xffff);
|
| 2931 |
|
| 2932 | return pos + sizeof(struct ieee80211_vht_operation);
|
| 2933 | }
|
| 2934 |
|
| 2935 | u8 *ieee80211_ie_build_he_oper(u8 *pos, const struct cfg80211_chan_def *chandef)
|
| 2936 | {
|
| 2937 | struct ieee80211_he_operation *he_oper;
|
| 2938 | struct ieee80211_he_6ghz_oper *he_6ghz_op;
|
| 2939 | struct cfg80211_chan_def he_chandef;
|
| 2940 | u32 he_oper_params;
|
| 2941 | u8 ie_len = 1 + sizeof(struct ieee80211_he_operation);
|
| 2942 |
|
| 2943 | if (chandef->chan->band == NL80211_BAND_6GHZ)
|
| 2944 | ie_len += sizeof(struct ieee80211_he_6ghz_oper);
|
| 2945 |
|
| 2946 | *pos++ = WLAN_EID_EXTENSION;
|
| 2947 | *pos++ = ie_len;
|
| 2948 | *pos++ = WLAN_EID_EXT_HE_OPERATION;
|
| 2949 |
|
| 2950 | he_oper_params = 0;
|
| 2951 | he_oper_params |= u32_encode_bits(1023,
|
| 2952 | IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
|
| 2953 | he_oper_params |= u32_encode_bits(1,
|
| 2954 | IEEE80211_HE_OPERATION_ER_SU_DISABLE);
|
| 2955 | he_oper_params |= u32_encode_bits(1,
|
| 2956 | IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED);
|
| 2957 | if (chandef->chan->band == NL80211_BAND_6GHZ)
|
| 2958 | he_oper_params |= u32_encode_bits(1,
|
| 2959 | IEEE80211_HE_OPERATION_6GHZ_OP_INFO);
|
| 2960 |
|
| 2961 | he_oper = (struct ieee80211_he_operation *)pos;
|
| 2962 | he_oper->he_oper_params = cpu_to_le32(he_oper_params);
|
| 2963 |
|
| 2964 |
|
| 2965 | he_oper->he_mcs_nss_set = cpu_to_le16(0xffff);
|
| 2966 | pos += sizeof(struct ieee80211_he_operation);
|
| 2967 |
|
| 2968 | if (chandef->chan->band != NL80211_BAND_6GHZ)
|
| 2969 | goto out;
|
| 2970 |
|
| 2971 | cfg80211_chandef_create(&he_chandef, chandef->chan, NL80211_CHAN_NO_HT);
|
| 2972 | he_chandef.center_freq1 = chandef->center_freq1;
|
| 2973 | he_chandef.center_freq2 = chandef->center_freq2;
|
| 2974 | he_chandef.width = chandef->width;
|
| 2975 |
|
| 2976 |
|
| 2977 | he_6ghz_op = (struct ieee80211_he_6ghz_oper *)pos;
|
| 2978 | he_6ghz_op->minrate = 6;
|
| 2979 | he_6ghz_op->primary =
|
| 2980 | ieee80211_frequency_to_channel(he_chandef.chan->center_freq);
|
| 2981 | he_6ghz_op->ccfs0 =
|
| 2982 | ieee80211_frequency_to_channel(he_chandef.center_freq1);
|
| 2983 | if (he_chandef.center_freq2)
|
| 2984 | he_6ghz_op->ccfs1 =
|
| 2985 | ieee80211_frequency_to_channel(he_chandef.center_freq2);
|
| 2986 | else
|
| 2987 | he_6ghz_op->ccfs1 = 0;
|
| 2988 |
|
| 2989 | switch (he_chandef.width) {
|
| 2990 | case NL80211_CHAN_WIDTH_320:
|
| 2991 | |
| 2992 | * center_freq1
|
| 2993 |
|
| 2994 | ieee80211_chandef_downgrade(&he_chandef, NULL);
|
| 2995 | he_6ghz_op->ccfs0 =
|
| 2996 | ieee80211_frequency_to_channel(he_chandef.center_freq1);
|
| 2997 | fallthrough;
|
| 2998 | case NL80211_CHAN_WIDTH_160:
|
| 2999 | |
| 3000 | * workaround.
|
| 3001 |
|
| 3002 | he_6ghz_op->control =
|
| 3003 | IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
|
| 3004 | he_6ghz_op->ccfs1 = he_6ghz_op->ccfs0;
|
| 3005 | if (he_chandef.chan->center_freq < he_chandef.center_freq1)
|
| 3006 | he_6ghz_op->ccfs0 -= 8;
|
| 3007 | else
|
| 3008 | he_6ghz_op->ccfs0 += 8;
|
| 3009 | fallthrough;
|
| 3010 | case NL80211_CHAN_WIDTH_80P80:
|
| 3011 | he_6ghz_op->control =
|
| 3012 | IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
|
| 3013 | break;
|
| 3014 | case NL80211_CHAN_WIDTH_80:
|
| 3015 | he_6ghz_op->control =
|
| 3016 | IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ;
|
| 3017 | break;
|
| 3018 | case NL80211_CHAN_WIDTH_40:
|
| 3019 | he_6ghz_op->control =
|
| 3020 | IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ;
|
| 3021 | break;
|
| 3022 | default:
|
| 3023 | he_6ghz_op->control =
|
| 3024 | IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ;
|
| 3025 | break;
|
| 3026 | }
|
| 3027 |
|
| 3028 | pos += sizeof(struct ieee80211_he_6ghz_oper);
|
| 3029 |
|
| 3030 | out:
|
| 3031 | return pos;
|
| 3032 | }
|
| 3033 |
|
| 3034 | u8 *ieee80211_ie_build_eht_oper(u8 *pos, const struct cfg80211_chan_def *chandef,
|
| 3035 | const struct ieee80211_sta_eht_cap *eht_cap)
|
| 3036 |
|
| 3037 | {
|
| 3038 | const struct ieee80211_eht_mcs_nss_supp_20mhz_only *eht_mcs_nss =
|
| 3039 | &eht_cap->eht_mcs_nss_supp.only_20mhz;
|
| 3040 | struct ieee80211_eht_operation *eht_oper;
|
| 3041 | struct ieee80211_eht_operation_info *eht_oper_info;
|
| 3042 | u8 eht_oper_len = offsetof(struct ieee80211_eht_operation, optional);
|
| 3043 | u8 eht_oper_info_len =
|
| 3044 | offsetof(struct ieee80211_eht_operation_info, optional);
|
| 3045 | u8 chan_width = 0;
|
| 3046 |
|
| 3047 | *pos++ = WLAN_EID_EXTENSION;
|
| 3048 | *pos++ = 1 + eht_oper_len + eht_oper_info_len;
|
| 3049 | *pos++ = WLAN_EID_EXT_EHT_OPERATION;
|
| 3050 |
|
| 3051 | eht_oper = (struct ieee80211_eht_operation *)pos;
|
| 3052 |
|
| 3053 | memcpy(&eht_oper->basic_mcs_nss, eht_mcs_nss, sizeof(*eht_mcs_nss));
|
| 3054 | eht_oper->params |= IEEE80211_EHT_OPER_INFO_PRESENT;
|
| 3055 | pos += eht_oper_len;
|
| 3056 |
|
| 3057 | eht_oper_info =
|
| 3058 | (struct ieee80211_eht_operation_info *)eht_oper->optional;
|
| 3059 |
|
| 3060 | eht_oper_info->ccfs0 =
|
| 3061 | ieee80211_frequency_to_channel(chandef->center_freq1);
|
| 3062 | if (chandef->center_freq2)
|
| 3063 | eht_oper_info->ccfs1 =
|
| 3064 | ieee80211_frequency_to_channel(chandef->center_freq2);
|
| 3065 | else
|
| 3066 | eht_oper_info->ccfs1 = 0;
|
| 3067 |
|
| 3068 | switch (chandef->width) {
|
| 3069 | case NL80211_CHAN_WIDTH_320:
|
| 3070 | chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ;
|
| 3071 | eht_oper_info->ccfs1 = eht_oper_info->ccfs0;
|
| 3072 | if (chandef->chan->center_freq < chandef->center_freq1)
|
| 3073 | eht_oper_info->ccfs0 -= 16;
|
| 3074 | else
|
| 3075 | eht_oper_info->ccfs0 += 16;
|
| 3076 | break;
|
| 3077 | case NL80211_CHAN_WIDTH_160:
|
| 3078 | eht_oper_info->ccfs1 = eht_oper_info->ccfs0;
|
| 3079 | if (chandef->chan->center_freq < chandef->center_freq1)
|
| 3080 | eht_oper_info->ccfs0 -= 8;
|
| 3081 | else
|
| 3082 | eht_oper_info->ccfs0 += 8;
|
| 3083 | fallthrough;
|
| 3084 | case NL80211_CHAN_WIDTH_80P80:
|
| 3085 | chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ;
|
| 3086 | break;
|
| 3087 | case NL80211_CHAN_WIDTH_80:
|
| 3088 | chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ;
|
| 3089 | break;
|
| 3090 | case NL80211_CHAN_WIDTH_40:
|
| 3091 | chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ;
|
| 3092 | break;
|
| 3093 | default:
|
| 3094 | chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ;
|
| 3095 | break;
|
| 3096 | }
|
| 3097 | eht_oper_info->control = chan_width;
|
| 3098 | pos += eht_oper_info_len;
|
| 3099 |
|
| 3100 |
|
| 3101 |
|
| 3102 | return pos;
|
| 3103 | }
|
| 3104 |
|
| 3105 | bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
|
| 3106 | struct cfg80211_chan_def *chandef)
|
| 3107 | {
|
| 3108 | enum nl80211_channel_type channel_type;
|
| 3109 |
|
| 3110 | if (!ht_oper)
|
| 3111 | return false;
|
| 3112 |
|
| 3113 | switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
| 3114 | case IEEE80211_HT_PARAM_CHA_SEC_NONE:
|
| 3115 | channel_type = NL80211_CHAN_HT20;
|
| 3116 | break;
|
| 3117 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
| 3118 | channel_type = NL80211_CHAN_HT40PLUS;
|
| 3119 | break;
|
| 3120 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
| 3121 | channel_type = NL80211_CHAN_HT40MINUS;
|
| 3122 | break;
|
| 3123 | default:
|
| 3124 | return false;
|
| 3125 | }
|
| 3126 |
|
| 3127 | cfg80211_chandef_create(chandef, chandef->chan, channel_type);
|
| 3128 | return true;
|
| 3129 | }
|
| 3130 |
|
| 3131 | bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
|
| 3132 | const struct ieee80211_vht_operation *oper,
|
| 3133 | const struct ieee80211_ht_operation *htop,
|
| 3134 | struct cfg80211_chan_def *chandef)
|
| 3135 | {
|
| 3136 | struct cfg80211_chan_def new = *chandef;
|
| 3137 | int cf0, cf1;
|
| 3138 | int ccfs0, ccfs1, ccfs2;
|
| 3139 | int ccf0, ccf1;
|
| 3140 | u32 vht_cap;
|
| 3141 | bool support_80_80 = false;
|
| 3142 | bool support_160 = false;
|
| 3143 | u8 ext_nss_bw_supp = u32_get_bits(vht_cap_info,
|
| 3144 | IEEE80211_VHT_CAP_EXT_NSS_BW_MASK);
|
| 3145 | u8 supp_chwidth = u32_get_bits(vht_cap_info,
|
| 3146 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
|
| 3147 |
|
| 3148 | if (!oper || !htop)
|
| 3149 | return false;
|
| 3150 |
|
| 3151 | vht_cap = hw->wiphy->bands[chandef->chan->band]->vht_cap.cap;
|
| 3152 | support_160 = (vht_cap & (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK |
|
| 3153 | IEEE80211_VHT_CAP_EXT_NSS_BW_MASK));
|
| 3154 | support_80_80 = ((vht_cap &
|
| 3155 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) ||
|
| 3156 | (vht_cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
|
| 3157 | vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) ||
|
| 3158 | ((vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) >>
|
| 3159 | IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT > 1));
|
| 3160 | ccfs0 = oper->center_freq_seg0_idx;
|
| 3161 | ccfs1 = oper->center_freq_seg1_idx;
|
| 3162 | ccfs2 = (le16_to_cpu(htop->operation_mode) &
|
| 3163 | IEEE80211_HT_OP_MODE_CCFS2_MASK)
|
| 3164 | >> IEEE80211_HT_OP_MODE_CCFS2_SHIFT;
|
| 3165 |
|
| 3166 | ccf0 = ccfs0;
|
| 3167 |
|
| 3168 |
|
| 3169 | if (!ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW))
|
| 3170 | ext_nss_bw_supp = 0;
|
| 3171 |
|
| 3172 | |
| 3173 | * Cf. IEEE 802.11-2020 Table 9-272 - Setting of the Supported Channel
|
| 3174 | * Width Set subfield and Extended NSS BW Support subfield at a STA
|
| 3175 | * transmitting the VHT Capabilities Information field
|
| 3176 | *
|
| 3177 | * We really just consider that because it's inefficient to connect
|
| 3178 | * at a higher bandwidth than we'll actually be able to use.
|
| 3179 |
|
| 3180 | switch ((supp_chwidth << 4) | ext_nss_bw_supp) {
|
| 3181 | default:
|
| 3182 | case 0x00:
|
| 3183 | ccf1 = 0;
|
| 3184 | support_160 = false;
|
| 3185 | support_80_80 = false;
|
| 3186 | break;
|
| 3187 | case 0x01:
|
| 3188 | support_80_80 = false;
|
| 3189 | fallthrough;
|
| 3190 | case 0x02:
|
| 3191 | case 0x03:
|
| 3192 | ccf1 = ccfs2;
|
| 3193 | break;
|
| 3194 | case 0x10:
|
| 3195 | ccf1 = ccfs1;
|
| 3196 | break;
|
| 3197 | case 0x11:
|
| 3198 | case 0x12:
|
| 3199 | if (!ccfs1)
|
| 3200 | ccf1 = ccfs2;
|
| 3201 | else
|
| 3202 | ccf1 = ccfs1;
|
| 3203 | break;
|
| 3204 | case 0x13:
|
| 3205 | case 0x20:
|
| 3206 | case 0x23:
|
| 3207 | ccf1 = ccfs1;
|
| 3208 | break;
|
| 3209 | }
|
| 3210 |
|
| 3211 | cf0 = ieee80211_channel_to_frequency(ccf0, chandef->chan->band);
|
| 3212 | cf1 = ieee80211_channel_to_frequency(ccf1, chandef->chan->band);
|
| 3213 |
|
| 3214 | switch (oper->chan_width) {
|
| 3215 | case IEEE80211_VHT_CHANWIDTH_USE_HT:
|
| 3216 |
|
| 3217 | break;
|
| 3218 | case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
| 3219 | new.width = NL80211_CHAN_WIDTH_80;
|
| 3220 | new.center_freq1 = cf0;
|
| 3221 |
|
| 3222 | if (ccf1) {
|
| 3223 | unsigned int diff;
|
| 3224 |
|
| 3225 | diff = abs(ccf1 - ccf0);
|
| 3226 | if ((diff == 8) && support_160) {
|
| 3227 | new.width = NL80211_CHAN_WIDTH_160;
|
| 3228 | new.center_freq1 = cf1;
|
| 3229 | } else if ((diff > 8) && support_80_80) {
|
| 3230 | new.width = NL80211_CHAN_WIDTH_80P80;
|
| 3231 | new.center_freq2 = cf1;
|
| 3232 | }
|
| 3233 | }
|
| 3234 | break;
|
| 3235 | case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
| 3236 |
|
| 3237 | new.width = NL80211_CHAN_WIDTH_160;
|
| 3238 | new.center_freq1 = cf0;
|
| 3239 | break;
|
| 3240 | case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
| 3241 |
|
| 3242 | new.width = NL80211_CHAN_WIDTH_80P80;
|
| 3243 | new.center_freq1 = cf0;
|
| 3244 | new.center_freq2 = cf1;
|
| 3245 | break;
|
| 3246 | default:
|
| 3247 | return false;
|
| 3248 | }
|
| 3249 |
|
| 3250 | if (!cfg80211_chandef_valid(&new))
|
| 3251 | return false;
|
| 3252 |
|
| 3253 | *chandef = new;
|
| 3254 | return true;
|
| 3255 | }
|
| 3256 |
|
| 3257 | void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation_info *info,
|
| 3258 | struct cfg80211_chan_def *chandef)
|
| 3259 | {
|
| 3260 | chandef->center_freq1 =
|
| 3261 | ieee80211_channel_to_frequency(info->ccfs0,
|
| 3262 | chandef->chan->band);
|
| 3263 |
|
| 3264 | switch (u8_get_bits(info->control,
|
| 3265 | IEEE80211_EHT_OPER_CHAN_WIDTH)) {
|
| 3266 | case IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ:
|
| 3267 | chandef->width = NL80211_CHAN_WIDTH_20;
|
| 3268 | break;
|
| 3269 | case IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ:
|
| 3270 | chandef->width = NL80211_CHAN_WIDTH_40;
|
| 3271 | break;
|
| 3272 | case IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ:
|
| 3273 | chandef->width = NL80211_CHAN_WIDTH_80;
|
| 3274 | break;
|
| 3275 | case IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ:
|
| 3276 | chandef->width = NL80211_CHAN_WIDTH_160;
|
| 3277 | chandef->center_freq1 =
|
| 3278 | ieee80211_channel_to_frequency(info->ccfs1,
|
| 3279 | chandef->chan->band);
|
| 3280 | break;
|
| 3281 | case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ:
|
| 3282 | chandef->width = NL80211_CHAN_WIDTH_320;
|
| 3283 | chandef->center_freq1 =
|
| 3284 | ieee80211_channel_to_frequency(info->ccfs1,
|
| 3285 | chandef->chan->band);
|
| 3286 | break;
|
| 3287 | }
|
| 3288 | }
|
| 3289 |
|
| 3290 | bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_local *local,
|
| 3291 | const struct ieee80211_he_operation *he_oper,
|
| 3292 | const struct ieee80211_eht_operation *eht_oper,
|
| 3293 | struct cfg80211_chan_def *chandef)
|
| 3294 | {
|
| 3295 | struct cfg80211_chan_def he_chandef = *chandef;
|
| 3296 | const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
|
| 3297 | u32 freq;
|
| 3298 |
|
| 3299 | if (chandef->chan->band != NL80211_BAND_6GHZ)
|
| 3300 | return true;
|
| 3301 |
|
| 3302 | if (!he_oper)
|
| 3303 | return false;
|
| 3304 |
|
| 3305 | he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper);
|
| 3306 | if (!he_6ghz_oper)
|
| 3307 | return false;
|
| 3308 |
|
| 3309 | |
| 3310 | * The EHT operation IE does not contain the primary channel so the
|
| 3311 | * primary channel frequency should be taken from the 6 GHz operation
|
| 3312 | * information.
|
| 3313 |
|
| 3314 | freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary,
|
| 3315 | NL80211_BAND_6GHZ);
|
| 3316 | he_chandef.chan = ieee80211_get_channel(local->hw.wiphy, freq);
|
| 3317 |
|
| 3318 | if (!he_chandef.chan)
|
| 3319 | return false;
|
| 3320 |
|
| 3321 | if (!eht_oper ||
|
| 3322 | !(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) {
|
| 3323 | switch (u8_get_bits(he_6ghz_oper->control,
|
| 3324 | IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) {
|
| 3325 | case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ:
|
| 3326 | he_chandef.width = NL80211_CHAN_WIDTH_20;
|
| 3327 | break;
|
| 3328 | case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ:
|
| 3329 | he_chandef.width = NL80211_CHAN_WIDTH_40;
|
| 3330 | break;
|
| 3331 | case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ:
|
| 3332 | he_chandef.width = NL80211_CHAN_WIDTH_80;
|
| 3333 | break;
|
| 3334 | case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ:
|
| 3335 | he_chandef.width = NL80211_CHAN_WIDTH_80;
|
| 3336 | if (!he_6ghz_oper->ccfs1)
|
| 3337 | break;
|
| 3338 | if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8)
|
| 3339 | he_chandef.width = NL80211_CHAN_WIDTH_160;
|
| 3340 | else
|
| 3341 | he_chandef.width = NL80211_CHAN_WIDTH_80P80;
|
| 3342 | break;
|
| 3343 | }
|
| 3344 |
|
| 3345 | if (he_chandef.width == NL80211_CHAN_WIDTH_160) {
|
| 3346 | he_chandef.center_freq1 =
|
| 3347 | ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1,
|
| 3348 | NL80211_BAND_6GHZ);
|
| 3349 | } else {
|
| 3350 | he_chandef.center_freq1 =
|
| 3351 | ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0,
|
| 3352 | NL80211_BAND_6GHZ);
|
| 3353 | he_chandef.center_freq2 =
|
| 3354 | ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1,
|
| 3355 | NL80211_BAND_6GHZ);
|
| 3356 | }
|
| 3357 | } else {
|
| 3358 | ieee80211_chandef_eht_oper((const void *)eht_oper->optional,
|
| 3359 | &he_chandef);
|
| 3360 | he_chandef.punctured =
|
| 3361 | ieee80211_eht_oper_dis_subchan_bitmap(eht_oper);
|
| 3362 | }
|
| 3363 |
|
| 3364 | if (!cfg80211_chandef_valid(&he_chandef))
|
| 3365 | return false;
|
| 3366 |
|
| 3367 | *chandef = he_chandef;
|
| 3368 |
|
| 3369 | return true;
|
| 3370 | }
|
| 3371 |
|
| 3372 | bool ieee80211_chandef_s1g_oper(struct ieee80211_local *local,
|
| 3373 | const struct ieee80211_s1g_oper_ie *oper,
|
| 3374 | struct cfg80211_chan_def *chandef)
|
| 3375 | {
|
| 3376 | u32 oper_khz, pri_1mhz_khz, pri_2mhz_khz;
|
| 3377 |
|
| 3378 | if (!oper)
|
| 3379 | return false;
|
| 3380 |
|
| 3381 | switch (FIELD_GET(S1G_OPER_CH_WIDTH_OPER, oper->ch_width)) {
|
| 3382 | case IEEE80211_S1G_CHANWIDTH_1MHZ:
|
| 3383 | chandef->width = NL80211_CHAN_WIDTH_1;
|
| 3384 | break;
|
| 3385 | case IEEE80211_S1G_CHANWIDTH_2MHZ:
|
| 3386 | chandef->width = NL80211_CHAN_WIDTH_2;
|
| 3387 | break;
|
| 3388 | case IEEE80211_S1G_CHANWIDTH_4MHZ:
|
| 3389 | chandef->width = NL80211_CHAN_WIDTH_4;
|
| 3390 | break;
|
| 3391 | case IEEE80211_S1G_CHANWIDTH_8MHZ:
|
| 3392 | chandef->width = NL80211_CHAN_WIDTH_8;
|
| 3393 | break;
|
| 3394 | case IEEE80211_S1G_CHANWIDTH_16MHZ:
|
| 3395 | chandef->width = NL80211_CHAN_WIDTH_16;
|
| 3396 | break;
|
| 3397 | default:
|
| 3398 | return false;
|
| 3399 | }
|
| 3400 |
|
| 3401 | chandef->s1g_primary_2mhz = false;
|
| 3402 |
|
| 3403 | switch (u8_get_bits(oper->ch_width, S1G_OPER_CH_WIDTH_PRIMARY)) {
|
| 3404 | case IEEE80211_S1G_PRI_CHANWIDTH_1MHZ:
|
| 3405 | pri_1mhz_khz = ieee80211_channel_to_freq_khz(
|
| 3406 | oper->primary_ch, NL80211_BAND_S1GHZ);
|
| 3407 | break;
|
| 3408 | case IEEE80211_S1G_PRI_CHANWIDTH_2MHZ:
|
| 3409 | chandef->s1g_primary_2mhz = true;
|
| 3410 | pri_2mhz_khz = ieee80211_channel_to_freq_khz(
|
| 3411 | oper->primary_ch, NL80211_BAND_S1GHZ);
|
| 3412 |
|
| 3413 | if (u8_get_bits(oper->ch_width, S1G_OPER_CH_PRIMARY_LOCATION) ==
|
| 3414 | S1G_2M_PRIMARY_LOCATION_LOWER)
|
| 3415 | pri_1mhz_khz = pri_2mhz_khz - 500;
|
| 3416 | else
|
| 3417 | pri_1mhz_khz = pri_2mhz_khz + 500;
|
| 3418 | break;
|
| 3419 | default:
|
| 3420 | return false;
|
| 3421 | }
|
| 3422 |
|
| 3423 | oper_khz = ieee80211_channel_to_freq_khz(oper->oper_ch,
|
| 3424 | NL80211_BAND_S1GHZ);
|
| 3425 | chandef->center_freq1 = KHZ_TO_MHZ(oper_khz);
|
| 3426 | chandef->freq1_offset = oper_khz % 1000;
|
| 3427 | chandef->chan =
|
| 3428 | ieee80211_get_channel_khz(local->hw.wiphy, pri_1mhz_khz);
|
| 3429 |
|
| 3430 | return chandef->chan;
|
| 3431 | }
|
| 3432 |
|
| 3433 | int ieee80211_put_srates_elem(struct sk_buff *skb,
|
| 3434 | const struct ieee80211_supported_band *sband,
|
| 3435 | u32 basic_rates, u32 masked_rates,
|
| 3436 | u8 element_id)
|
| 3437 | {
|
| 3438 | u8 i, rates, skip;
|
| 3439 |
|
| 3440 | rates = 0;
|
| 3441 | for (i = 0; i < sband->n_bitrates; i++) {
|
| 3442 | if (masked_rates & BIT(i))
|
| 3443 | continue;
|
| 3444 | rates++;
|
| 3445 | }
|
| 3446 |
|
| 3447 | if (element_id == WLAN_EID_SUPP_RATES) {
|
| 3448 | rates = min_t(u8, rates, 8);
|
| 3449 | skip = 0;
|
| 3450 | } else {
|
| 3451 | skip = 8;
|
| 3452 | if (rates <= skip)
|
| 3453 | return 0;
|
| 3454 | rates -= skip;
|
| 3455 | }
|
| 3456 |
|
| 3457 | if (skb_tailroom(skb) < rates + 2)
|
| 3458 | return -ENOBUFS;
|
| 3459 |
|
| 3460 | skb_put_u8(skb, element_id);
|
| 3461 | skb_put_u8(skb, rates);
|
| 3462 |
|
| 3463 | for (i = 0; i < sband->n_bitrates && rates; i++) {
|
| 3464 | int rate;
|
| 3465 | u8 basic;
|
| 3466 |
|
| 3467 | if (masked_rates & BIT(i))
|
| 3468 | continue;
|
| 3469 |
|
| 3470 | if (skip > 0) {
|
| 3471 | skip--;
|
| 3472 | continue;
|
| 3473 | }
|
| 3474 |
|
| 3475 | basic = basic_rates & BIT(i) ? 0x80 : 0;
|
| 3476 |
|
| 3477 | rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5);
|
| 3478 | skb_put_u8(skb, basic | (u8)rate);
|
| 3479 | rates--;
|
| 3480 | }
|
| 3481 |
|
| 3482 | WARN(rates > 0, "rates confused: rates:%d, element:%d\n",
|
| 3483 | rates, element_id);
|
| 3484 |
|
| 3485 | return 0;
|
| 3486 | }
|
| 3487 |
|
| 3488 | int ieee80211_ave_rssi(struct ieee80211_vif *vif, int link_id)
|
| 3489 | {
|
| 3490 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
| 3491 | struct ieee80211_link_data *link_data;
|
| 3492 |
|
| 3493 | if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
|
| 3494 | return 0;
|
| 3495 |
|
| 3496 | if (link_id < 0)
|
| 3497 | link_data = &sdata->deflink;
|
| 3498 | else
|
| 3499 | link_data = wiphy_dereference(sdata->local->hw.wiphy,
|
| 3500 | sdata->link[link_id]);
|
| 3501 |
|
| 3502 | if (WARN_ON_ONCE(!link_data))
|
| 3503 | return -99;
|
| 3504 |
|
| 3505 | return -ewma_beacon_signal_read(&link_data->u.mgd.ave_beacon_signal);
|
| 3506 | }
|
| 3507 | EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
|
| 3508 |
|
| 3509 | u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
|
| 3510 | {
|
| 3511 | if (!mcs)
|
| 3512 | return 1;
|
| 3513 |
|
| 3514 |
|
| 3515 |
|
| 3516 | if (mcs->rx_mask[3])
|
| 3517 | return 4;
|
| 3518 | if (mcs->rx_mask[2])
|
| 3519 | return 3;
|
| 3520 | if (mcs->rx_mask[1])
|
| 3521 | return 2;
|
| 3522 | return 1;
|
| 3523 | }
|
| 3524 |
|
| 3525 | u64 ieee80211_calculate_rx_timestamp(struct ieee80211_hw *hw,
|
| 3526 | struct ieee80211_rx_status *status,
|
| 3527 | unsigned int mpdu_len,
|
| 3528 | unsigned int mpdu_offset)
|
| 3529 | {
|
| 3530 | u64 ts = status->mactime;
|
| 3531 | bool mactime_plcp_start;
|
| 3532 | struct rate_info ri;
|
| 3533 | u16 rate;
|
| 3534 | u8 n_ltf;
|
| 3535 |
|
| 3536 | if (WARN_ON(!ieee80211_have_rx_timestamp(status)))
|
| 3537 | return 0;
|
| 3538 |
|
| 3539 | mactime_plcp_start = (status->flag & RX_FLAG_MACTIME) ==
|
| 3540 | RX_FLAG_MACTIME_PLCP_START;
|
| 3541 |
|
| 3542 | memset(&ri, 0, sizeof(ri));
|
| 3543 |
|
| 3544 | ri.bw = status->bw;
|
| 3545 |
|
| 3546 |
|
| 3547 | switch (status->encoding) {
|
| 3548 | case RX_ENC_EHT:
|
| 3549 | ri.flags |= RATE_INFO_FLAGS_EHT_MCS;
|
| 3550 | ri.mcs = status->rate_idx;
|
| 3551 | ri.nss = status->nss;
|
| 3552 | ri.eht_ru_alloc = status->eht.ru;
|
| 3553 | if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
|
| 3554 | ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
|
| 3555 |
|
| 3556 | if (mactime_plcp_start) {
|
| 3557 | mpdu_offset += 2;
|
| 3558 | ts += 36;
|
| 3559 | }
|
| 3560 | break;
|
| 3561 | case RX_ENC_HE:
|
| 3562 | ri.flags |= RATE_INFO_FLAGS_HE_MCS;
|
| 3563 | ri.mcs = status->rate_idx;
|
| 3564 | ri.nss = status->nss;
|
| 3565 | ri.he_ru_alloc = status->he_ru;
|
| 3566 | if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
|
| 3567 | ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
|
| 3568 |
|
| 3569 | |
| 3570 | * See P802.11ax_D6.0, section 27.3.4 for
|
| 3571 | * VHT PPDU format.
|
| 3572 |
|
| 3573 | if (mactime_plcp_start) {
|
| 3574 | mpdu_offset += 2;
|
| 3575 | ts += 36;
|
| 3576 |
|
| 3577 | |
| 3578 | * TODO:
|
| 3579 | * For HE MU PPDU, add the HE-SIG-B.
|
| 3580 | * For HE ER PPDU, add 8us for the HE-SIG-A.
|
| 3581 | * For HE TB PPDU, add 4us for the HE-STF.
|
| 3582 | * Add the HE-LTF durations - variable.
|
| 3583 |
|
| 3584 | }
|
| 3585 |
|
| 3586 | break;
|
| 3587 | case RX_ENC_HT:
|
| 3588 | ri.mcs = status->rate_idx;
|
| 3589 | ri.flags |= RATE_INFO_FLAGS_MCS;
|
| 3590 | if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
|
| 3591 | ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
|
| 3592 |
|
| 3593 | |
| 3594 | * See P802.11REVmd_D3.0, section 19.3.2 for
|
| 3595 | * HT PPDU format.
|
| 3596 |
|
| 3597 | if (mactime_plcp_start) {
|
| 3598 | mpdu_offset += 2;
|
| 3599 | if (status->enc_flags & RX_ENC_FLAG_HT_GF)
|
| 3600 | ts += 24;
|
| 3601 | else
|
| 3602 | ts += 32;
|
| 3603 |
|
| 3604 | |
| 3605 | * Add Data HT-LTFs per streams
|
| 3606 | * TODO: add Extension HT-LTFs, 4us per LTF
|
| 3607 |
|
| 3608 | n_ltf = ((ri.mcs >> 3) & 3) + 1;
|
| 3609 | n_ltf = n_ltf == 3 ? 4 : n_ltf;
|
| 3610 | ts += n_ltf * 4;
|
| 3611 | }
|
| 3612 |
|
| 3613 | break;
|
| 3614 | case RX_ENC_VHT:
|
| 3615 | ri.flags |= RATE_INFO_FLAGS_VHT_MCS;
|
| 3616 | ri.mcs = status->rate_idx;
|
| 3617 | ri.nss = status->nss;
|
| 3618 | if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
|
| 3619 | ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
|
| 3620 |
|
| 3621 | |
| 3622 | * See P802.11REVmd_D3.0, section 21.3.2 for
|
| 3623 | * VHT PPDU format.
|
| 3624 |
|
| 3625 | if (mactime_plcp_start) {
|
| 3626 | mpdu_offset += 2;
|
| 3627 | ts += 36;
|
| 3628 |
|
| 3629 | |
| 3630 | * Add VHT-LTFs per streams
|
| 3631 |
|
| 3632 | n_ltf = (ri.nss != 1) && (ri.nss % 2) ?
|
| 3633 | ri.nss + 1 : ri.nss;
|
| 3634 | ts += 4 * n_ltf;
|
| 3635 | }
|
| 3636 |
|
| 3637 | break;
|
| 3638 | default:
|
| 3639 | WARN_ON(1);
|
| 3640 | fallthrough;
|
| 3641 | case RX_ENC_LEGACY: {
|
| 3642 | struct ieee80211_supported_band *sband;
|
| 3643 |
|
| 3644 | sband = hw->wiphy->bands[status->band];
|
| 3645 | ri.legacy = sband->bitrates[status->rate_idx].bitrate;
|
| 3646 |
|
| 3647 | if (mactime_plcp_start) {
|
| 3648 | if (status->band == NL80211_BAND_5GHZ) {
|
| 3649 | ts += 20;
|
| 3650 | mpdu_offset += 2;
|
| 3651 | } else if (status->enc_flags & RX_ENC_FLAG_SHORTPRE) {
|
| 3652 | ts += 96;
|
| 3653 | } else {
|
| 3654 | ts += 192;
|
| 3655 | }
|
| 3656 | }
|
| 3657 | break;
|
| 3658 | }
|
| 3659 | }
|
| 3660 |
|
| 3661 | rate = cfg80211_calculate_bitrate(&ri);
|
| 3662 | if (WARN_ONCE(!rate,
|
| 3663 | "Invalid bitrate: flags=0x%llx, idx=%d, vht_nss=%d\n",
|
| 3664 | (unsigned long long)status->flag, status->rate_idx,
|
| 3665 | status->nss))
|
| 3666 | return 0;
|
| 3667 |
|
| 3668 |
|
| 3669 | if ((status->flag & RX_FLAG_MACTIME) == RX_FLAG_MACTIME_END)
|
| 3670 | ts -= mpdu_len * 8 * 10 / rate;
|
| 3671 |
|
| 3672 | ts += mpdu_offset * 8 * 10 / rate;
|
| 3673 |
|
| 3674 | return ts;
|
| 3675 | }
|
| 3676 | EXPORT_SYMBOL_GPL(ieee80211_calculate_rx_timestamp);
|
| 3677 |
|
| 3678 | |
| 3679 | * also provided, only the interfaces using that ctx will be canceled.
|
| 3680 |
|
| 3681 | void ieee80211_dfs_cac_cancel(struct ieee80211_local *local,
|
| 3682 | struct ieee80211_chanctx *ctx)
|
| 3683 | {
|
| 3684 | struct ieee80211_sub_if_data *sdata;
|
| 3685 | struct cfg80211_chan_def chandef;
|
| 3686 | struct ieee80211_link_data *link;
|
| 3687 | struct ieee80211_chanctx_conf *chanctx_conf;
|
| 3688 | unsigned int link_id;
|
| 3689 |
|
| 3690 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 3691 |
|
| 3692 | list_for_each_entry(sdata, &local->interfaces, list) {
|
| 3693 | for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS;
|
| 3694 | link_id++) {
|
| 3695 | link = sdata_dereference(sdata->link[link_id],
|
| 3696 | sdata);
|
| 3697 | if (!link)
|
| 3698 | continue;
|
| 3699 |
|
| 3700 | chanctx_conf = sdata_dereference(link->conf->chanctx_conf,
|
| 3701 | sdata);
|
| 3702 | if (ctx && &ctx->conf != chanctx_conf)
|
| 3703 | continue;
|
| 3704 |
|
| 3705 | wiphy_hrtimer_work_cancel(local->hw.wiphy,
|
| 3706 | &link->dfs_cac_timer_work);
|
| 3707 |
|
| 3708 | if (!sdata->wdev.links[link_id].cac_started)
|
| 3709 | continue;
|
| 3710 |
|
| 3711 | chandef = link->conf->chanreq.oper;
|
| 3712 | ieee80211_link_release_channel(link);
|
| 3713 | cfg80211_cac_event(sdata->dev, &chandef,
|
| 3714 | NL80211_RADAR_CAC_ABORTED,
|
| 3715 | GFP_KERNEL, link_id);
|
| 3716 | }
|
| 3717 | }
|
| 3718 | }
|
| 3719 |
|
| 3720 | void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
|
| 3721 | struct wiphy_work *work)
|
| 3722 | {
|
| 3723 | struct ieee80211_local *local =
|
| 3724 | container_of(work, struct ieee80211_local, radar_detected_work);
|
| 3725 | struct cfg80211_chan_def chandef;
|
| 3726 | struct ieee80211_chanctx *ctx, *tmp;
|
| 3727 |
|
| 3728 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 3729 |
|
| 3730 | list_for_each_entry_safe(ctx, tmp, &local->chanctx_list, list) {
|
| 3731 | if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
|
| 3732 | continue;
|
| 3733 |
|
| 3734 | if (!ctx->radar_detected)
|
| 3735 | continue;
|
| 3736 |
|
| 3737 | ctx->radar_detected = false;
|
| 3738 |
|
| 3739 | chandef = ctx->conf.def;
|
| 3740 |
|
| 3741 | ieee80211_dfs_cac_cancel(local, ctx);
|
| 3742 | cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
|
| 3743 | }
|
| 3744 | }
|
| 3745 |
|
| 3746 | static void
|
| 3747 | ieee80211_radar_mark_chan_ctx_iterator(struct ieee80211_hw *hw,
|
| 3748 | struct ieee80211_chanctx_conf *chanctx_conf,
|
| 3749 | void *data)
|
| 3750 | {
|
| 3751 | struct ieee80211_chanctx *ctx =
|
| 3752 | container_of(chanctx_conf, struct ieee80211_chanctx,
|
| 3753 | conf);
|
| 3754 |
|
| 3755 | if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
|
| 3756 | return;
|
| 3757 |
|
| 3758 | if (data && data != chanctx_conf)
|
| 3759 | return;
|
| 3760 |
|
| 3761 | ctx->radar_detected = true;
|
| 3762 | }
|
| 3763 |
|
| 3764 | void ieee80211_radar_detected(struct ieee80211_hw *hw,
|
| 3765 | struct ieee80211_chanctx_conf *chanctx_conf)
|
| 3766 | {
|
| 3767 | struct ieee80211_local *local = hw_to_local(hw);
|
| 3768 |
|
| 3769 | trace_api_radar_detected(local);
|
| 3770 |
|
| 3771 | ieee80211_iter_chan_contexts_atomic(hw, ieee80211_radar_mark_chan_ctx_iterator,
|
| 3772 | chanctx_conf);
|
| 3773 |
|
| 3774 | wiphy_work_queue(hw->wiphy, &local->radar_detected_work);
|
| 3775 | }
|
| 3776 | EXPORT_SYMBOL(ieee80211_radar_detected);
|
| 3777 |
|
| 3778 | void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c,
|
| 3779 | struct ieee80211_conn_settings *conn)
|
| 3780 | {
|
| 3781 | enum nl80211_chan_width new_primary_width;
|
| 3782 | struct ieee80211_conn_settings _ignored = {};
|
| 3783 |
|
| 3784 |
|
| 3785 | if (!conn)
|
| 3786 | conn = &_ignored;
|
| 3787 |
|
| 3788 | again:
|
| 3789 |
|
| 3790 | new_primary_width = NL80211_CHAN_WIDTH_20_NOHT;
|
| 3791 |
|
| 3792 | switch (c->width) {
|
| 3793 | default:
|
| 3794 | case NL80211_CHAN_WIDTH_20_NOHT:
|
| 3795 | WARN_ON_ONCE(1);
|
| 3796 | fallthrough;
|
| 3797 | case NL80211_CHAN_WIDTH_20:
|
| 3798 | c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
| 3799 | conn->mode = IEEE80211_CONN_MODE_LEGACY;
|
| 3800 | conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20;
|
| 3801 | c->punctured = 0;
|
| 3802 | break;
|
| 3803 | case NL80211_CHAN_WIDTH_40:
|
| 3804 | c->width = NL80211_CHAN_WIDTH_20;
|
| 3805 | c->center_freq1 = c->chan->center_freq;
|
| 3806 | if (conn->mode == IEEE80211_CONN_MODE_VHT)
|
| 3807 | conn->mode = IEEE80211_CONN_MODE_HT;
|
| 3808 | conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20;
|
| 3809 | c->punctured = 0;
|
| 3810 | break;
|
| 3811 | case NL80211_CHAN_WIDTH_80:
|
| 3812 | new_primary_width = NL80211_CHAN_WIDTH_40;
|
| 3813 | if (conn->mode == IEEE80211_CONN_MODE_VHT)
|
| 3814 | conn->mode = IEEE80211_CONN_MODE_HT;
|
| 3815 | conn->bw_limit = IEEE80211_CONN_BW_LIMIT_40;
|
| 3816 | break;
|
| 3817 | case NL80211_CHAN_WIDTH_80P80:
|
| 3818 | c->center_freq2 = 0;
|
| 3819 | c->width = NL80211_CHAN_WIDTH_80;
|
| 3820 | conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80;
|
| 3821 | break;
|
| 3822 | case NL80211_CHAN_WIDTH_160:
|
| 3823 | new_primary_width = NL80211_CHAN_WIDTH_80;
|
| 3824 | conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80;
|
| 3825 | break;
|
| 3826 | case NL80211_CHAN_WIDTH_320:
|
| 3827 | new_primary_width = NL80211_CHAN_WIDTH_160;
|
| 3828 | conn->bw_limit = IEEE80211_CONN_BW_LIMIT_160;
|
| 3829 | break;
|
| 3830 | case NL80211_CHAN_WIDTH_1:
|
| 3831 | case NL80211_CHAN_WIDTH_2:
|
| 3832 | case NL80211_CHAN_WIDTH_4:
|
| 3833 | case NL80211_CHAN_WIDTH_8:
|
| 3834 | case NL80211_CHAN_WIDTH_16:
|
| 3835 | WARN_ON_ONCE(1);
|
| 3836 |
|
| 3837 | conn->mode = IEEE80211_CONN_MODE_S1G;
|
| 3838 | conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20;
|
| 3839 | break;
|
| 3840 | }
|
| 3841 |
|
| 3842 | if (new_primary_width != NL80211_CHAN_WIDTH_20_NOHT) {
|
| 3843 | c->center_freq1 = cfg80211_chandef_primary(c, new_primary_width,
|
| 3844 | &c->punctured);
|
| 3845 | c->width = new_primary_width;
|
| 3846 | }
|
| 3847 |
|
| 3848 |
|
| 3849 | c->npca_chan = NULL;
|
| 3850 | c->npca_punctured = 0;
|
| 3851 |
|
| 3852 | |
| 3853 | * With an 80 MHz channel, we might have the puncturing in the primary
|
| 3854 | * 40 Mhz channel, but that's not valid when downgraded to 40 MHz width.
|
| 3855 | * In that case, downgrade again.
|
| 3856 |
|
| 3857 | if (!cfg80211_chandef_valid(c) && c->punctured)
|
| 3858 | goto again;
|
| 3859 |
|
| 3860 | WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
| 3861 | }
|
| 3862 |
|
| 3863 | enum nl80211_chan_width
|
| 3864 | ieee80211_sta_rx_bw_to_chan_width(enum ieee80211_sta_rx_bandwidth bw)
|
| 3865 | {
|
| 3866 | switch (bw) {
|
| 3867 | case IEEE80211_STA_RX_BW_20:
|
| 3868 | return NL80211_CHAN_WIDTH_20;
|
| 3869 | case IEEE80211_STA_RX_BW_40:
|
| 3870 | return NL80211_CHAN_WIDTH_40;
|
| 3871 | case IEEE80211_STA_RX_BW_80:
|
| 3872 | return NL80211_CHAN_WIDTH_80;
|
| 3873 | case IEEE80211_STA_RX_BW_160:
|
| 3874 | return NL80211_CHAN_WIDTH_160;
|
| 3875 | case IEEE80211_STA_RX_BW_320:
|
| 3876 | return NL80211_CHAN_WIDTH_320;
|
| 3877 | default:
|
| 3878 | return NL80211_CHAN_WIDTH_20;
|
| 3879 | }
|
| 3880 | }
|
| 3881 |
|
| 3882 | int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
|
| 3883 | struct cfg80211_csa_settings *csa_settings)
|
| 3884 | {
|
| 3885 | int hdr_len = IEEE80211_MIN_ACTION_SIZE(chan_switch);
|
| 3886 | struct sk_buff *skb;
|
| 3887 | struct ieee80211_mgmt *mgmt;
|
| 3888 | struct ieee80211_local *local = sdata->local;
|
| 3889 | int freq;
|
| 3890 | u8 *pos;
|
| 3891 |
|
| 3892 | if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
| 3893 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
|
| 3894 | return -EOPNOTSUPP;
|
| 3895 |
|
| 3896 | skb = dev_alloc_skb(local->tx_headroom + hdr_len +
|
| 3897 | 5 +
|
| 3898 | 3 +
|
| 3899 | 5 +
|
| 3900 | 8);
|
| 3901 | if (!skb)
|
| 3902 | return -ENOMEM;
|
| 3903 |
|
| 3904 | skb_reserve(skb, local->tx_headroom);
|
| 3905 | mgmt = skb_put_zero(skb, hdr_len);
|
| 3906 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
| 3907 | IEEE80211_STYPE_ACTION);
|
| 3908 |
|
| 3909 | eth_broadcast_addr(mgmt->da);
|
| 3910 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
| 3911 | if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
| 3912 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
|
| 3913 | } else {
|
| 3914 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
| 3915 | memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
|
| 3916 | }
|
| 3917 | mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
|
| 3918 | mgmt->u.action.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
|
| 3919 | pos = skb_put(skb, 5);
|
| 3920 | *pos++ = WLAN_EID_CHANNEL_SWITCH;
|
| 3921 | *pos++ = 3;
|
| 3922 | *pos++ = csa_settings->block_tx ? 1 : 0;
|
| 3923 | freq = csa_settings->chandef.chan->center_freq;
|
| 3924 | *pos++ = ieee80211_frequency_to_channel(freq);
|
| 3925 | *pos++ = csa_settings->count;
|
| 3926 |
|
| 3927 | if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
|
| 3928 | enum nl80211_channel_type ch_type;
|
| 3929 |
|
| 3930 | skb_put(skb, 3);
|
| 3931 | *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
|
| 3932 | *pos++ = 1;
|
| 3933 | ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
|
| 3934 | if (ch_type == NL80211_CHAN_HT40PLUS)
|
| 3935 | *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
| 3936 | else
|
| 3937 | *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
| 3938 | }
|
| 3939 |
|
| 3940 | if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
| 3941 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
| 3942 |
|
| 3943 | skb_put(skb, 8);
|
| 3944 | *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
|
| 3945 | *pos++ = 6;
|
| 3946 | *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;
|
| 3947 | *pos = 0x00;
|
| 3948 | *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
|
| 3949 | *pos++ |= csa_settings->block_tx ?
|
| 3950 | WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
|
| 3951 | put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
|
| 3952 | pos += 2;
|
| 3953 | put_unaligned_le16(ifmsh->pre_value, pos);
|
| 3954 | pos += 2;
|
| 3955 | }
|
| 3956 |
|
| 3957 | if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_80 ||
|
| 3958 | csa_settings->chandef.width == NL80211_CHAN_WIDTH_80P80 ||
|
| 3959 | csa_settings->chandef.width == NL80211_CHAN_WIDTH_160) {
|
| 3960 | skb_put(skb, 5);
|
| 3961 | ieee80211_ie_build_wide_bw_cs(pos, &csa_settings->chandef);
|
| 3962 | }
|
| 3963 |
|
| 3964 | ieee80211_tx_skb(sdata, skb);
|
| 3965 | return 0;
|
| 3966 | }
|
| 3967 |
|
| 3968 | static bool
|
| 3969 | ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i)
|
| 3970 | {
|
| 3971 | s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1);
|
| 3972 | int skip;
|
| 3973 |
|
| 3974 | if (end > 0)
|
| 3975 | return false;
|
| 3976 |
|
| 3977 |
|
| 3978 | if (data->count[i] == 1)
|
| 3979 | return false;
|
| 3980 |
|
| 3981 | if (data->desc[i].interval == 0)
|
| 3982 | return false;
|
| 3983 |
|
| 3984 |
|
| 3985 | skip = DIV_ROUND_UP(-end, data->desc[i].interval);
|
| 3986 | if (data->count[i] < 255) {
|
| 3987 | if (data->count[i] <= skip) {
|
| 3988 | data->count[i] = 0;
|
| 3989 | return false;
|
| 3990 | }
|
| 3991 |
|
| 3992 | data->count[i] -= skip;
|
| 3993 | }
|
| 3994 |
|
| 3995 | data->desc[i].start += skip * data->desc[i].interval;
|
| 3996 |
|
| 3997 | return true;
|
| 3998 | }
|
| 3999 |
|
| 4000 | static bool
|
| 4001 | ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf,
|
| 4002 | s32 *offset)
|
| 4003 | {
|
| 4004 | bool ret = false;
|
| 4005 | int i;
|
| 4006 |
|
| 4007 | for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
|
| 4008 | s32 cur;
|
| 4009 |
|
| 4010 | if (!data->count[i])
|
| 4011 | continue;
|
| 4012 |
|
| 4013 | if (ieee80211_extend_noa_desc(data, tsf + *offset, i))
|
| 4014 | ret = true;
|
| 4015 |
|
| 4016 | cur = data->desc[i].start - tsf;
|
| 4017 | if (cur > *offset)
|
| 4018 | continue;
|
| 4019 |
|
| 4020 | cur = data->desc[i].start + data->desc[i].duration - tsf;
|
| 4021 | if (cur > *offset)
|
| 4022 | *offset = cur;
|
| 4023 | }
|
| 4024 |
|
| 4025 | return ret;
|
| 4026 | }
|
| 4027 |
|
| 4028 | static u32
|
| 4029 | ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf)
|
| 4030 | {
|
| 4031 | s32 offset = 0;
|
| 4032 | int tries = 0;
|
| 4033 | |
| 4034 | * arbitrary limit, used to avoid infinite loops when combined NoA
|
| 4035 | * descriptors cover the full time period.
|
| 4036 |
|
| 4037 | int max_tries = 5;
|
| 4038 |
|
| 4039 | ieee80211_extend_absent_time(data, tsf, &offset);
|
| 4040 | do {
|
| 4041 | if (!ieee80211_extend_absent_time(data, tsf, &offset))
|
| 4042 | break;
|
| 4043 |
|
| 4044 | tries++;
|
| 4045 | } while (tries < max_tries);
|
| 4046 |
|
| 4047 | return offset;
|
| 4048 | }
|
| 4049 |
|
| 4050 | void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf)
|
| 4051 | {
|
| 4052 | u32 next_offset = BIT(31) - 1;
|
| 4053 | int i;
|
| 4054 |
|
| 4055 | data->absent = 0;
|
| 4056 | data->has_next_tsf = false;
|
| 4057 | for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
|
| 4058 | s32 start;
|
| 4059 |
|
| 4060 | if (!data->count[i])
|
| 4061 | continue;
|
| 4062 |
|
| 4063 | ieee80211_extend_noa_desc(data, tsf, i);
|
| 4064 | start = data->desc[i].start - tsf;
|
| 4065 | if (start <= 0)
|
| 4066 | data->absent |= BIT(i);
|
| 4067 |
|
| 4068 | if (next_offset > start)
|
| 4069 | next_offset = start;
|
| 4070 |
|
| 4071 | data->has_next_tsf = true;
|
| 4072 | }
|
| 4073 |
|
| 4074 | if (data->absent)
|
| 4075 | next_offset = ieee80211_get_noa_absent_time(data, tsf);
|
| 4076 |
|
| 4077 | data->next_tsf = tsf + next_offset;
|
| 4078 | }
|
| 4079 | EXPORT_SYMBOL(ieee80211_update_p2p_noa);
|
| 4080 |
|
| 4081 | int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
|
| 4082 | struct ieee80211_noa_data *data, u32 tsf)
|
| 4083 | {
|
| 4084 | int ret = 0;
|
| 4085 | int i;
|
| 4086 |
|
| 4087 | memset(data, 0, sizeof(*data));
|
| 4088 |
|
| 4089 | for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
|
| 4090 | const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i];
|
| 4091 |
|
| 4092 | if (!desc->count || !desc->duration)
|
| 4093 | continue;
|
| 4094 |
|
| 4095 | data->count[i] = desc->count;
|
| 4096 | data->desc[i].start = le32_to_cpu(desc->start_time);
|
| 4097 | data->desc[i].duration = le32_to_cpu(desc->duration);
|
| 4098 | data->desc[i].interval = le32_to_cpu(desc->interval);
|
| 4099 |
|
| 4100 | if (data->count[i] > 1 &&
|
| 4101 | data->desc[i].interval < data->desc[i].duration)
|
| 4102 | continue;
|
| 4103 |
|
| 4104 | ieee80211_extend_noa_desc(data, tsf, i);
|
| 4105 | ret++;
|
| 4106 | }
|
| 4107 |
|
| 4108 | if (ret)
|
| 4109 | ieee80211_update_p2p_noa(data, tsf);
|
| 4110 |
|
| 4111 | return ret;
|
| 4112 | }
|
| 4113 | EXPORT_SYMBOL(ieee80211_parse_p2p_noa);
|
| 4114 |
|
| 4115 | void ieee80211_recalc_dtim(struct ieee80211_sub_if_data *sdata, u64 tsf)
|
| 4116 | {
|
| 4117 | u64 dtim_count = 0;
|
| 4118 | u32 beacon_int = sdata->vif.bss_conf.beacon_int * 1024;
|
| 4119 | u8 dtim_period = sdata->vif.bss_conf.dtim_period;
|
| 4120 | struct ps_data *ps;
|
| 4121 | u8 bcns_from_dtim;
|
| 4122 |
|
| 4123 | if (tsf == -1ULL || !beacon_int || !dtim_period)
|
| 4124 | return;
|
| 4125 |
|
| 4126 | if (sdata->vif.type == NL80211_IFTYPE_AP ||
|
| 4127 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
| 4128 | if (!sdata->bss)
|
| 4129 | return;
|
| 4130 |
|
| 4131 | ps = &sdata->bss->ps;
|
| 4132 | } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
| 4133 | ps = &sdata->u.mesh.ps;
|
| 4134 | } else {
|
| 4135 | return;
|
| 4136 | }
|
| 4137 |
|
| 4138 | |
| 4139 | * actually finds last dtim_count, mac80211 will update in
|
| 4140 | * __beacon_add_tim().
|
| 4141 | * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period
|
| 4142 |
|
| 4143 | do_div(tsf, beacon_int);
|
| 4144 | bcns_from_dtim = do_div(tsf, dtim_period);
|
| 4145 |
|
| 4146 | if (!bcns_from_dtim)
|
| 4147 | dtim_count = 0;
|
| 4148 | else
|
| 4149 | dtim_count = dtim_period - bcns_from_dtim;
|
| 4150 |
|
| 4151 | ps->dtim_count = dtim_count;
|
| 4152 | }
|
| 4153 |
|
| 4154 | |
| 4155 | * Given a long beacon period, calculate the current index into
|
| 4156 | * that period to determine the number of TSBTTs until the next TBTT.
|
| 4157 | * It is completely valid to have a short beacon period that differs
|
| 4158 | * from the dtim period (i.e a TBTT thats not a DTIM).
|
| 4159 |
|
| 4160 | void ieee80211_recalc_sb_count(struct ieee80211_sub_if_data *sdata, u64 tsf)
|
| 4161 | {
|
| 4162 | u32 sb_idx;
|
| 4163 | struct ps_data *ps = &sdata->bss->ps;
|
| 4164 | u8 lb_period = sdata->vif.bss_conf.s1g_long_beacon_period;
|
| 4165 | u32 beacon_int = sdata->vif.bss_conf.beacon_int * 1024;
|
| 4166 |
|
| 4167 |
|
| 4168 | if (tsf == -1ULL || !lb_period ||
|
| 4169 | (sdata->vif.type != NL80211_IFTYPE_AP &&
|
| 4170 | sdata->vif.type != NL80211_IFTYPE_AP_VLAN))
|
| 4171 | return;
|
| 4172 |
|
| 4173 |
|
| 4174 | do_div(tsf, beacon_int);
|
| 4175 | sb_idx = do_div(tsf, lb_period);
|
| 4176 |
|
| 4177 |
|
| 4178 | ps->sb_count = sb_idx ? lb_period - sb_idx : 0;
|
| 4179 | }
|
| 4180 |
|
| 4181 | static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local,
|
| 4182 | struct ieee80211_chanctx *ctx)
|
| 4183 | {
|
| 4184 | struct ieee80211_link_data *link;
|
| 4185 | u8 radar_detect = 0;
|
| 4186 |
|
| 4187 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 4188 |
|
| 4189 | if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED))
|
| 4190 | return 0;
|
| 4191 |
|
| 4192 | for_each_sdata_link(local, link) {
|
| 4193 | if (rcu_access_pointer(link->conf->chanctx_conf) == &ctx->conf) {
|
| 4194 | |
| 4195 | * An in-place reservation context should not have any
|
| 4196 | * assigned links until it replaces the other context.
|
| 4197 |
|
| 4198 | WARN_ON(ctx->replace_state ==
|
| 4199 | IEEE80211_CHANCTX_REPLACES_OTHER);
|
| 4200 |
|
| 4201 | if (link->radar_required)
|
| 4202 | radar_detect |=
|
| 4203 | BIT(link->conf->chanreq.oper.width);
|
| 4204 | }
|
| 4205 |
|
| 4206 | if (link->reserved_chanctx == ctx &&
|
| 4207 | link->reserved_radar_required)
|
| 4208 | radar_detect |= BIT(link->reserved.oper.width);
|
| 4209 | }
|
| 4210 |
|
| 4211 | return radar_detect;
|
| 4212 | }
|
| 4213 |
|
| 4214 | bool ieee80211_is_radio_idx_in_scan_req(struct wiphy *wiphy,
|
| 4215 | struct cfg80211_scan_request *scan_req,
|
| 4216 | int radio_idx)
|
| 4217 | {
|
| 4218 | struct ieee80211_channel *chan;
|
| 4219 | int i, chan_radio_idx;
|
| 4220 |
|
| 4221 | for (i = 0; i < scan_req->n_channels; i++) {
|
| 4222 | chan = scan_req->channels[i];
|
| 4223 | chan_radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chan);
|
| 4224 |
|
| 4225 | |
| 4226 | * occurred. For example, if radio-level information is
|
| 4227 | * missing, the same error value is returned. This
|
| 4228 | * typically implies a single-radio setup, in which case
|
| 4229 | * the operation should not be allowed.
|
| 4230 |
|
| 4231 | if (chan_radio_idx == radio_idx)
|
| 4232 | return true;
|
| 4233 | }
|
| 4234 |
|
| 4235 | return false;
|
| 4236 | }
|
| 4237 |
|
| 4238 | static u32
|
| 4239 | __ieee80211_get_radio_mask(struct ieee80211_sub_if_data *sdata)
|
| 4240 | {
|
| 4241 | struct ieee80211_bss_conf *link_conf;
|
| 4242 | struct ieee80211_chanctx_conf *conf;
|
| 4243 | unsigned int link_id;
|
| 4244 | u32 mask = 0;
|
| 4245 |
|
| 4246 | for_each_vif_active_link(&sdata->vif, link_conf, link_id) {
|
| 4247 | conf = sdata_dereference(link_conf->chanctx_conf, sdata);
|
| 4248 | if (!conf || conf->radio_idx < 0)
|
| 4249 | continue;
|
| 4250 |
|
| 4251 | mask |= BIT(conf->radio_idx);
|
| 4252 | }
|
| 4253 |
|
| 4254 | return mask;
|
| 4255 | }
|
| 4256 |
|
| 4257 | u32 ieee80211_get_radio_mask(struct wiphy *wiphy, struct net_device *dev)
|
| 4258 | {
|
| 4259 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
| 4260 |
|
| 4261 | return __ieee80211_get_radio_mask(sdata);
|
| 4262 | }
|
| 4263 |
|
| 4264 | static bool
|
| 4265 | ieee80211_sdata_uses_radio(struct ieee80211_sub_if_data *sdata, int radio_idx)
|
| 4266 | {
|
| 4267 | if (radio_idx < 0)
|
| 4268 | return true;
|
| 4269 |
|
| 4270 | return __ieee80211_get_radio_mask(sdata) & BIT(radio_idx);
|
| 4271 | }
|
| 4272 |
|
| 4273 | static int
|
| 4274 | ieee80211_fill_ifcomb_params(struct ieee80211_local *local,
|
| 4275 | struct iface_combination_params *params,
|
| 4276 | const struct cfg80211_chan_def *chandef,
|
| 4277 | struct ieee80211_sub_if_data *sdata,
|
| 4278 | bool (*chanctx_filter)(struct ieee80211_chanctx *ctx,
|
| 4279 | void *filter_data),
|
| 4280 | void *filter_data)
|
| 4281 | {
|
| 4282 | struct ieee80211_sub_if_data *sdata_iter;
|
| 4283 | struct ieee80211_chanctx *ctx;
|
| 4284 | int total = !!sdata;
|
| 4285 |
|
| 4286 | list_for_each_entry(ctx, &local->chanctx_list, list) {
|
| 4287 | if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
| 4288 | continue;
|
| 4289 |
|
| 4290 | if (params->radio_idx >= 0 &&
|
| 4291 | ctx->conf.radio_idx != params->radio_idx)
|
| 4292 | continue;
|
| 4293 |
|
| 4294 | params->radar_detect |=
|
| 4295 | ieee80211_chanctx_radar_detect(local, ctx);
|
| 4296 |
|
| 4297 | if (chandef && ctx->mode != IEEE80211_CHANCTX_EXCLUSIVE &&
|
| 4298 | cfg80211_chandef_compatible(chandef, &ctx->conf.def))
|
| 4299 | continue;
|
| 4300 |
|
| 4301 | if (chanctx_filter &&
|
| 4302 | chanctx_filter(ctx, filter_data))
|
| 4303 | continue;
|
| 4304 |
|
| 4305 | params->num_different_channels++;
|
| 4306 | }
|
| 4307 |
|
| 4308 | list_for_each_entry(sdata_iter, &local->interfaces, list) {
|
| 4309 | struct wireless_dev *wdev_iter;
|
| 4310 |
|
| 4311 | wdev_iter = &sdata_iter->wdev;
|
| 4312 |
|
| 4313 | if (sdata_iter == sdata ||
|
| 4314 | !ieee80211_sdata_running(sdata_iter) ||
|
| 4315 | cfg80211_iftype_allowed(local->hw.wiphy,
|
| 4316 | wdev_iter->iftype, 0, 1))
|
| 4317 | continue;
|
| 4318 |
|
| 4319 | if (!ieee80211_sdata_uses_radio(sdata_iter, params->radio_idx))
|
| 4320 | continue;
|
| 4321 |
|
| 4322 | params->iftype_num[wdev_iter->iftype]++;
|
| 4323 | total++;
|
| 4324 | }
|
| 4325 |
|
| 4326 | return total;
|
| 4327 | }
|
| 4328 |
|
| 4329 | int ieee80211_check_combinations_ext(struct ieee80211_sub_if_data *sdata,
|
| 4330 | struct ieee80211_check_combinations_data *data)
|
| 4331 | {
|
| 4332 | const struct cfg80211_chan_def *chandef = data->chandef;
|
| 4333 | bool shared = data->chanmode == IEEE80211_CHANCTX_SHARED;
|
| 4334 | struct ieee80211_local *local = sdata->local;
|
| 4335 | enum nl80211_iftype iftype = sdata->wdev.iftype;
|
| 4336 | struct iface_combination_params params = {
|
| 4337 | .radar_detect = data->radar_detect,
|
| 4338 | .radio_idx = data->radio_idx,
|
| 4339 | };
|
| 4340 | int total;
|
| 4341 |
|
| 4342 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 4343 |
|
| 4344 | if (WARN_ON(hweight32(data->radar_detect) > 1))
|
| 4345 | return -EINVAL;
|
| 4346 |
|
| 4347 | if (WARN_ON(chandef && data->chanmode == IEEE80211_CHANCTX_SHARED &&
|
| 4348 | !chandef->chan))
|
| 4349 | return -EINVAL;
|
| 4350 |
|
| 4351 | if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
|
| 4352 | return -EINVAL;
|
| 4353 |
|
| 4354 | if (sdata->vif.type == NL80211_IFTYPE_AP ||
|
| 4355 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
|
| 4356 | |
| 4357 | * always passing this is harmless, since it'll be the
|
| 4358 | * same value that cfg80211 finds if it finds the same
|
| 4359 | * interface ... and that's always allowed
|
| 4360 |
|
| 4361 | params.new_beacon_int = sdata->vif.bss_conf.beacon_int;
|
| 4362 | }
|
| 4363 |
|
| 4364 |
|
| 4365 | if (cfg80211_iftype_allowed(local->hw.wiphy, iftype, 0, 1)) {
|
| 4366 | if (data->radar_detect)
|
| 4367 | return -EINVAL;
|
| 4368 | return 0;
|
| 4369 | }
|
| 4370 |
|
| 4371 | if (chandef)
|
| 4372 | params.num_different_channels = 1;
|
| 4373 |
|
| 4374 | if (iftype != NL80211_IFTYPE_UNSPECIFIED)
|
| 4375 | params.iftype_num[iftype] = 1;
|
| 4376 |
|
| 4377 | total = ieee80211_fill_ifcomb_params(local, ¶ms,
|
| 4378 | shared ? chandef : NULL,
|
| 4379 | sdata,
|
| 4380 | data->chanctx_filter,
|
| 4381 | data->filter_data);
|
| 4382 | if (total == 1 && !params.radar_detect)
|
| 4383 | return 0;
|
| 4384 |
|
| 4385 | return cfg80211_check_combinations(local->hw.wiphy, ¶ms);
|
| 4386 | }
|
| 4387 |
|
| 4388 | static void
|
| 4389 | ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c,
|
| 4390 | void *data)
|
| 4391 | {
|
| 4392 | u32 *max_num_different_channels = data;
|
| 4393 |
|
| 4394 | *max_num_different_channels = max(*max_num_different_channels,
|
| 4395 | c->num_different_channels);
|
| 4396 | }
|
| 4397 |
|
| 4398 | int ieee80211_max_num_channels(struct ieee80211_local *local, int radio_idx)
|
| 4399 | {
|
| 4400 | u32 max_num_different_channels = 1;
|
| 4401 | int err;
|
| 4402 | struct iface_combination_params params = {
|
| 4403 | .radio_idx = radio_idx,
|
| 4404 | };
|
| 4405 |
|
| 4406 | lockdep_assert_wiphy(local->hw.wiphy);
|
| 4407 |
|
| 4408 | ieee80211_fill_ifcomb_params(local, ¶ms, NULL, NULL, NULL, NULL);
|
| 4409 |
|
| 4410 | err = cfg80211_iter_combinations(local->hw.wiphy, ¶ms,
|
| 4411 | ieee80211_iter_max_chans,
|
| 4412 | &max_num_different_channels);
|
| 4413 | if (err < 0)
|
| 4414 | return err;
|
| 4415 |
|
| 4416 | return max_num_different_channels;
|
| 4417 | }
|
| 4418 |
|
| 4419 | void ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata,
|
| 4420 | struct ieee80211_sta_s1g_cap *caps,
|
| 4421 | struct sk_buff *skb)
|
| 4422 | {
|
| 4423 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
| 4424 | struct ieee80211_s1g_cap s1g_capab;
|
| 4425 | u8 *pos;
|
| 4426 | int i;
|
| 4427 |
|
| 4428 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
|
| 4429 | return;
|
| 4430 |
|
| 4431 | if (!caps->s1g)
|
| 4432 | return;
|
| 4433 |
|
| 4434 | memcpy(s1g_capab.capab_info, caps->cap, sizeof(caps->cap));
|
| 4435 | memcpy(s1g_capab.supp_mcs_nss, caps->nss_mcs, sizeof(caps->nss_mcs));
|
| 4436 |
|
| 4437 |
|
| 4438 | for (i = 0; i < sizeof(ifmgd->s1g_capa.capab_info); i++) {
|
| 4439 | u8 mask = ifmgd->s1g_capa_mask.capab_info[i];
|
| 4440 |
|
| 4441 | s1g_capab.capab_info[i] &= ~mask;
|
| 4442 | s1g_capab.capab_info[i] |= ifmgd->s1g_capa.capab_info[i] & mask;
|
| 4443 | }
|
| 4444 |
|
| 4445 |
|
| 4446 | for (i = 0; i < sizeof(ifmgd->s1g_capa.supp_mcs_nss); i++) {
|
| 4447 | u8 mask = ifmgd->s1g_capa_mask.supp_mcs_nss[i];
|
| 4448 |
|
| 4449 | s1g_capab.supp_mcs_nss[i] &= ~mask;
|
| 4450 | s1g_capab.supp_mcs_nss[i] |=
|
| 4451 | ifmgd->s1g_capa.supp_mcs_nss[i] & mask;
|
| 4452 | }
|
| 4453 |
|
| 4454 | pos = skb_put(skb, 2 + sizeof(s1g_capab));
|
| 4455 | *pos++ = WLAN_EID_S1G_CAPABILITIES;
|
| 4456 | *pos++ = sizeof(s1g_capab);
|
| 4457 |
|
| 4458 | memcpy(pos, &s1g_capab, sizeof(s1g_capab));
|
| 4459 | }
|
| 4460 |
|
| 4461 | void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata,
|
| 4462 | struct sk_buff *skb)
|
| 4463 | {
|
| 4464 | u8 *pos = skb_put(skb, 3);
|
| 4465 |
|
| 4466 | *pos++ = WLAN_EID_AID_REQUEST;
|
| 4467 | *pos++ = 1;
|
| 4468 | *pos++ = 0;
|
| 4469 | }
|
| 4470 |
|
| 4471 | u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
|
| 4472 | {
|
| 4473 | *buf++ = WLAN_EID_VENDOR_SPECIFIC;
|
| 4474 | *buf++ = 7;
|
| 4475 | *buf++ = 0x00;
|
| 4476 | *buf++ = 0x50;
|
| 4477 | *buf++ = 0xf2;
|
| 4478 | *buf++ = 2;
|
| 4479 | *buf++ = 0;
|
| 4480 | *buf++ = 1;
|
| 4481 | *buf++ = qosinfo;
|
| 4482 |
|
| 4483 | return buf;
|
| 4484 | }
|
| 4485 |
|
| 4486 | void ieee80211_txq_get_depth(struct ieee80211_txq *txq,
|
| 4487 | unsigned long *frame_cnt,
|
| 4488 | unsigned long *byte_cnt)
|
| 4489 | {
|
| 4490 | struct txq_info *txqi = to_txq_info(txq);
|
| 4491 | u32 frag_cnt = 0, frag_bytes = 0;
|
| 4492 | struct sk_buff *skb;
|
| 4493 |
|
| 4494 | skb_queue_walk(&txqi->frags, skb) {
|
| 4495 | frag_cnt++;
|
| 4496 | frag_bytes += skb->len;
|
| 4497 | }
|
| 4498 |
|
| 4499 | if (frame_cnt)
|
| 4500 | *frame_cnt = txqi->tin.backlog_packets + frag_cnt;
|
| 4501 |
|
| 4502 | if (byte_cnt)
|
| 4503 | *byte_cnt = txqi->tin.backlog_bytes + frag_bytes;
|
| 4504 | }
|
| 4505 | EXPORT_SYMBOL(ieee80211_txq_get_depth);
|
| 4506 |
|
| 4507 | const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS] = {
|
| 4508 | IEEE80211_WMM_IE_STA_QOSINFO_AC_VO,
|
| 4509 | IEEE80211_WMM_IE_STA_QOSINFO_AC_VI,
|
| 4510 | IEEE80211_WMM_IE_STA_QOSINFO_AC_BE,
|
| 4511 | IEEE80211_WMM_IE_STA_QOSINFO_AC_BK
|
| 4512 | };
|
| 4513 |
|
| 4514 | u16 ieee80211_encode_usf(int listen_interval)
|
| 4515 | {
|
| 4516 | static const int listen_int_usf[] = { 1, 10, 1000, 10000 };
|
| 4517 | u16 ui, usf = 0;
|
| 4518 |
|
| 4519 |
|
| 4520 | while (usf < IEEE80211_MAX_USF) {
|
| 4521 | if (listen_interval % listen_int_usf[usf + 1])
|
| 4522 | break;
|
| 4523 | usf += 1;
|
| 4524 | }
|
| 4525 | ui = listen_interval / listen_int_usf[usf];
|
| 4526 |
|
| 4527 |
|
| 4528 | WARN_ON_ONCE(ui > IEEE80211_MAX_UI);
|
| 4529 | listen_interval = FIELD_PREP(LISTEN_INT_USF, usf) |
|
| 4530 | FIELD_PREP(LISTEN_INT_UI, ui);
|
| 4531 |
|
| 4532 | return (u16) listen_interval;
|
| 4533 | }
|
| 4534 |
|
| 4535 |
|
| 4536 | u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata)
|
| 4537 | {
|
| 4538 | const struct ieee80211_sta_he_cap *he_cap;
|
| 4539 | const struct ieee80211_sta_eht_cap *eht_cap;
|
| 4540 | struct ieee80211_supported_band *sband;
|
| 4541 | bool is_ap;
|
| 4542 | u8 n;
|
| 4543 |
|
| 4544 | sband = ieee80211_get_sband(sdata);
|
| 4545 | if (!sband)
|
| 4546 | return 0;
|
| 4547 |
|
| 4548 | he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
|
| 4549 | eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif);
|
| 4550 | if (!he_cap || !eht_cap)
|
| 4551 | return 0;
|
| 4552 |
|
| 4553 | is_ap = sdata->vif.type == NL80211_IFTYPE_AP;
|
| 4554 |
|
| 4555 | n = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
|
| 4556 | &eht_cap->eht_cap_elem,
|
| 4557 | is_ap);
|
| 4558 | return 2 + 1 +
|
| 4559 | sizeof(eht_cap->eht_cap_elem) + n +
|
| 4560 | ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
|
| 4561 | eht_cap->eht_cap_elem.phy_cap_info);
|
| 4562 | return 0;
|
| 4563 | }
|
| 4564 |
|
| 4565 | int ieee80211_put_eht_cap(struct sk_buff *skb,
|
| 4566 | struct ieee80211_sub_if_data *sdata,
|
| 4567 | const struct ieee80211_supported_band *sband,
|
| 4568 | const struct ieee80211_conn_settings *conn)
|
| 4569 | {
|
| 4570 | const struct ieee80211_sta_he_cap *he_cap =
|
| 4571 | ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
|
| 4572 | const struct ieee80211_sta_eht_cap *eht_cap =
|
| 4573 | ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif);
|
| 4574 | bool for_ap = sdata->vif.type == NL80211_IFTYPE_AP;
|
| 4575 | struct ieee80211_eht_cap_elem_fixed fixed;
|
| 4576 | struct ieee80211_he_cap_elem he;
|
| 4577 | u8 mcs_nss_len, ppet_len;
|
| 4578 | u8 orig_mcs_nss_len;
|
| 4579 | u8 ie_len;
|
| 4580 |
|
| 4581 | if (!conn)
|
| 4582 | conn = &ieee80211_conn_settings_unlimited;
|
| 4583 |
|
| 4584 |
|
| 4585 | if (!he_cap || !eht_cap)
|
| 4586 | return 0;
|
| 4587 |
|
| 4588 | orig_mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
|
| 4589 | &eht_cap->eht_cap_elem,
|
| 4590 | for_ap);
|
| 4591 |
|
| 4592 | ieee80211_get_adjusted_he_cap(conn, he_cap, &he);
|
| 4593 |
|
| 4594 | fixed = eht_cap->eht_cap_elem;
|
| 4595 |
|
| 4596 | if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_80)
|
| 4597 | fixed.phy_cap_info[6] &=
|
| 4598 | ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_80MHZ;
|
| 4599 |
|
| 4600 | if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160) {
|
| 4601 | fixed.phy_cap_info[1] &=
|
| 4602 | ~IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK;
|
| 4603 | fixed.phy_cap_info[2] &=
|
| 4604 | ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK;
|
| 4605 | fixed.phy_cap_info[6] &=
|
| 4606 | ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_160MHZ;
|
| 4607 | }
|
| 4608 |
|
| 4609 | if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_320) {
|
| 4610 | fixed.phy_cap_info[0] &=
|
| 4611 | ~IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
|
| 4612 | fixed.phy_cap_info[1] &=
|
| 4613 | ~IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK;
|
| 4614 | fixed.phy_cap_info[2] &=
|
| 4615 | ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK;
|
| 4616 | fixed.phy_cap_info[6] &=
|
| 4617 | ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_320MHZ;
|
| 4618 | }
|
| 4619 |
|
| 4620 | if (conn->bw_limit == IEEE80211_CONN_BW_LIMIT_20)
|
| 4621 | fixed.phy_cap_info[0] &=
|
| 4622 | ~IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ;
|
| 4623 |
|
| 4624 | mcs_nss_len = ieee80211_eht_mcs_nss_size(&he, &fixed, for_ap);
|
| 4625 | ppet_len = ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
|
| 4626 | fixed.phy_cap_info);
|
| 4627 |
|
| 4628 | ie_len = 2 + 1 + sizeof(eht_cap->eht_cap_elem) + mcs_nss_len + ppet_len;
|
| 4629 | if (skb_tailroom(skb) < ie_len)
|
| 4630 | return -ENOBUFS;
|
| 4631 |
|
| 4632 | skb_put_u8(skb, WLAN_EID_EXTENSION);
|
| 4633 | skb_put_u8(skb, ie_len - 2);
|
| 4634 | skb_put_u8(skb, WLAN_EID_EXT_EHT_CAPABILITY);
|
| 4635 | skb_put_data(skb, &fixed, sizeof(fixed));
|
| 4636 |
|
| 4637 | if (mcs_nss_len == 4 && orig_mcs_nss_len != 4) {
|
| 4638 | |
| 4639 | * If the (non-AP) STA became 20 MHz only, then convert from
|
| 4640 | * <=80 to 20-MHz-only format, where MCSes are indicated in
|
| 4641 | * the groups 0-7, 8-9, 10-11, 12-13 rather than just 0-9,
|
| 4642 | * 10-11, 12-13. Thus, use 0-9 for 0-7 and 8-9.
|
| 4643 |
|
| 4644 | skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss);
|
| 4645 | skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss);
|
| 4646 | skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs11_max_nss);
|
| 4647 | skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss);
|
| 4648 | } else {
|
| 4649 | skb_put_data(skb, &eht_cap->eht_mcs_nss_supp, mcs_nss_len);
|
| 4650 | }
|
| 4651 |
|
| 4652 | if (ppet_len)
|
| 4653 | skb_put_data(skb, &eht_cap->eht_ppe_thres, ppet_len);
|
| 4654 |
|
| 4655 | return 0;
|
| 4656 | }
|
| 4657 |
|
| 4658 | int ieee80211_put_uhr_cap(struct sk_buff *skb,
|
| 4659 | struct ieee80211_sub_if_data *sdata,
|
| 4660 | const struct ieee80211_supported_band *sband)
|
| 4661 | {
|
| 4662 | const struct ieee80211_sta_uhr_cap *uhr_cap =
|
| 4663 | ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif);
|
| 4664 | int len;
|
| 4665 |
|
| 4666 | if (!uhr_cap)
|
| 4667 | return 0;
|
| 4668 |
|
| 4669 | len = 2 + 1 + sizeof(struct ieee80211_uhr_cap);
|
| 4670 |
|
| 4671 | if (skb_tailroom(skb) < len)
|
| 4672 | return -ENOBUFS;
|
| 4673 |
|
| 4674 | skb_put_u8(skb, WLAN_EID_EXTENSION);
|
| 4675 | skb_put_u8(skb, len - 2);
|
| 4676 | skb_put_u8(skb, WLAN_EID_EXT_UHR_CAPA);
|
| 4677 | skb_put_data(skb, &uhr_cap->mac, sizeof(uhr_cap->mac));
|
| 4678 | skb_put_data(skb, &uhr_cap->phy, sizeof(uhr_cap->phy));
|
| 4679 |
|
| 4680 | return 0;
|
| 4681 | }
|
| 4682 |
|
| 4683 | const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode)
|
| 4684 | {
|
| 4685 | static const char * const modes[] = {
|
| 4686 | [IEEE80211_CONN_MODE_S1G] = "S1G",
|
| 4687 | [IEEE80211_CONN_MODE_LEGACY] = "legacy",
|
| 4688 | [IEEE80211_CONN_MODE_HT] = "HT",
|
| 4689 | [IEEE80211_CONN_MODE_VHT] = "VHT",
|
| 4690 | [IEEE80211_CONN_MODE_HE] = "HE",
|
| 4691 | [IEEE80211_CONN_MODE_EHT] = "EHT",
|
| 4692 | [IEEE80211_CONN_MODE_UHR] = "UHR",
|
| 4693 | };
|
| 4694 |
|
| 4695 | if (WARN_ON(mode >= ARRAY_SIZE(modes)))
|
| 4696 | return "<out of range>";
|
| 4697 |
|
| 4698 | return modes[mode] ?: "<missing string>";
|
| 4699 | }
|
| 4700 |
|
| 4701 | enum ieee80211_conn_bw_limit
|
| 4702 | ieee80211_min_bw_limit_from_chandef(struct cfg80211_chan_def *chandef)
|
| 4703 | {
|
| 4704 | switch (chandef->width) {
|
| 4705 | case NL80211_CHAN_WIDTH_20_NOHT:
|
| 4706 | case NL80211_CHAN_WIDTH_20:
|
| 4707 | return IEEE80211_CONN_BW_LIMIT_20;
|
| 4708 | case NL80211_CHAN_WIDTH_40:
|
| 4709 | return IEEE80211_CONN_BW_LIMIT_40;
|
| 4710 | case NL80211_CHAN_WIDTH_80:
|
| 4711 | return IEEE80211_CONN_BW_LIMIT_80;
|
| 4712 | case NL80211_CHAN_WIDTH_80P80:
|
| 4713 | case NL80211_CHAN_WIDTH_160:
|
| 4714 | return IEEE80211_CONN_BW_LIMIT_160;
|
| 4715 | case NL80211_CHAN_WIDTH_320:
|
| 4716 | return IEEE80211_CONN_BW_LIMIT_320;
|
| 4717 | default:
|
| 4718 | WARN(1, "unhandled chandef width %d\n", chandef->width);
|
| 4719 | return IEEE80211_CONN_BW_LIMIT_20;
|
| 4720 | }
|
| 4721 | }
|
| 4722 |
|
| 4723 | void ieee80211_clear_tpe(struct ieee80211_parsed_tpe *tpe)
|
| 4724 | {
|
| 4725 | for (int i = 0; i < 2; i++) {
|
| 4726 | tpe->max_local[i].valid = false;
|
| 4727 | memset(tpe->max_local[i].power,
|
| 4728 | IEEE80211_TPE_MAX_TX_PWR_NO_CONSTRAINT,
|
| 4729 | sizeof(tpe->max_local[i].power));
|
| 4730 |
|
| 4731 | tpe->max_reg_client[i].valid = false;
|
| 4732 | memset(tpe->max_reg_client[i].power,
|
| 4733 | IEEE80211_TPE_MAX_TX_PWR_NO_CONSTRAINT,
|
| 4734 | sizeof(tpe->max_reg_client[i].power));
|
| 4735 |
|
| 4736 | tpe->psd_local[i].valid = false;
|
| 4737 | memset(tpe->psd_local[i].power,
|
| 4738 | IEEE80211_TPE_PSD_NO_LIMIT,
|
| 4739 | sizeof(tpe->psd_local[i].power));
|
| 4740 |
|
| 4741 | tpe->psd_reg_client[i].valid = false;
|
| 4742 | memset(tpe->psd_reg_client[i].power,
|
| 4743 | IEEE80211_TPE_PSD_NO_LIMIT,
|
| 4744 | sizeof(tpe->psd_reg_client[i].power));
|
| 4745 | }
|
| 4746 | }
|
| 4747 |
|
| 4748 | bool ieee80211_vif_nan_started(struct ieee80211_vif *vif)
|
| 4749 | {
|
| 4750 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
| 4751 |
|
| 4752 | return vif->type == NL80211_IFTYPE_NAN && sdata->u.nan.started;
|
| 4753 | }
|
| 4754 | EXPORT_SYMBOL_GPL(ieee80211_vif_nan_started);
|