Not much going on here right now:

- mac80211/hwsim:
    - some NAN related things
    - MCS/NSS rate issues with S1G
  - p54: port SPI version to device-tree
  - (a few other random things)
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmoPJbIACgkQ10qiO8sP
 aAAaPw/+N1dZbDbayDy2sA9oniqpiCJEWspfaK3fnkh1qdpit84tGxEbZtCUIeNB
 WYsvvce25qNifWH+xSoma0v+F70eLdlWf6cvjL+dtpf3ITX9HkL055vRtpR+t+vL
 /hTtLL8q4OXdu4I/UuCw47ftXzV2zPcHh3+VQK1uR0aM3kXpJhewCAYC3TY1tDVe
 +YiUynbidG/893sGnXPG0KMDT92TF/N6sIQLcVw7Yn3d+GjPPP1M0W89FfZzCO5+
 ngi529r4VQ8z/XDAWUFzK8SzCNbaVqZtAZt0/NuhfCWOCIXq76qYkJq3L4MxMQRg
 MPHDkgKQI5cZB6/zHq1DEdhEcu8f0zo7gTFnSrXbq4I3DkYVpFaOo9JVjcHLKE9f
 O6M4fr/a59Zi/jMFPvGDuSItOrR4fERD9H9y+U0DmV6fk4PxXYgE7qWcWDdHdKeD
 rtwp5FUHrNZxujKvxR2CPzW0JfCh4IZnb4IFLgloGI79SBJ6JL4WgriHavKGCihq
 7JDo3TEn1oh9hcbIytXIZ6ZD8PkGZk93f7HeRuCSDo27SfLOvAR0KKpC1opX0zQw
 dWEbTpz4jhmgskb0SM8BmuD+XxhNHPojzMmPdyjtrA7pDRfZfM255/F5++etmgQm
 ToS93CEPU4rCZsy7O2NoLQGVZSfXc2y5fCZwTJoBZdUDYRviG7A=
 =XLqG
 -----END PGP SIGNATURE-----

Merge tag 'wireless-next-2026-05-21' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next

Johannes Berg says:

====================
Not much going on here right now:
 - mac80211/hwsim:
   - some NAN related things
   - MCS/NSS rate issues with S1G
 - p54: port SPI version to device-tree
 - (a few other random things)

* tag 'wireless-next-2026-05-21' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next:
  ARM: dts: omap2: add stlc4560 spi-wireless node
  p54spi: convert to devicetree
  dt-bindings: net: add st,stlc4560/p54spi binding
  wifi: mac80211: allow cipher change on NAN_DATA interfaces
  wifi: mac80211_hwsim: Do not declare NAN support for Extended Key ID
  wifi: cfg80211: add a function to parse UHR DBE
  wifi: mac80211: don't call ieee80211_handle_reconfig_failure when not needed
  wifi: mac80211: Allow per station GTK for NAN Data interfaces
  wifi: mac80211_hwsim: advertise NPCA capability
  wifi: mac80211_hwsim: reject NAN on multi-radio wiphys
  wifi: plfxlc: use module_usb_driver() macro
  wifi: mac80211: don't recalc min def for S1G chan ctx
  wifi: mac80211: skip NSS and BW init for S1G sta
  wifi: mac80211: check stations are removed before MLD change
  wifi: rt2x00: allocate anchor with rt2x00dev
====================

Link: https://patch.msgid.link/20260521153519.380276-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-05-21 16:00:06 -07:00
commit 1a1f055318
19 changed files with 508 additions and 120 deletions

View File

@ -0,0 +1,61 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/wireless/st,stlc4560.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ST/Intersil/Conexant stlc45xx/p54spi/cx3110x SPI wireless device
maintainers:
- Christian Lamparter <chunkeey@gmail.com>
description:
The SPI variant of the Intersil Prism54 wireless device was sold
under a variety of names, including Conexant CX3110x and
ST Microelectronics STLC5460.
allOf:
- $ref: ieee80211.yaml#
- $ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
oneOf:
- const: st,stlc4560
- items:
- enum:
- cnxt,3110x
- isil,p54spi
- st,stlc4550
- const: st,stlc4560
reg:
maxItems: 1
interrupts:
maxItems: 1
powerdown-gpios:
maxItems: 1
required:
- compatible
- reg
- interrupts
unevaluatedProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
wifi@0 {
compatible = "st,stlc4560";
reg = <0>;
spi-max-frequency = <48000000>;
interrupts-extended = <&gpio 23>;
powerdown-gpios = <&gpio 1>;
};
};

View File

@ -20156,6 +20156,7 @@ M: Christian Lamparter <chunkeey@googlemail.com>
L: linux-wireless@vger.kernel.org
S: Maintained
W: https://wireless.wiki.kernel.org/en/users/Drivers/p54
F: Documentation/devicetree/bindings/net/wireless/st,stlc4560.yaml
F: drivers/net/wireless/intersil/
PACKET SOCKETS

View File

@ -129,6 +129,8 @@ i2c2: i2c@48072000 {
};
mcspi1: spi@48098000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "ti,omap2-mcspi";
ti,hwmods = "mcspi1";
reg = <0x48098000 0x100>;
@ -140,6 +142,8 @@ mcspi1: spi@48098000 {
};
mcspi2: spi@4809a000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "ti,omap2-mcspi";
ti,hwmods = "mcspi2";
reg = <0x4809a000 0x100>;

View File

@ -109,3 +109,15 @@ partition@5 {
};
};
};
&mcspi2 {
status = "okay";
wifi@0 {
reg = <0>;
compatible = "st,stlc4560";
spi-max-frequency = <48000000>;
interrupts-extended = <&gpio3 23 IRQ_TYPE_EDGE_RISING>;
powerdown-gpios = <&gpio4 1 GPIO_ACTIVE_LOW>; /* gpio 97 */
};
};

View File

@ -20,7 +20,6 @@
#include <linux/spi/spi.h>
#include <linux/usb/musb.h>
#include <linux/mmc/host.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/platform_data/mmc-omap.h>
#include <linux/mfd/menelaus.h>
@ -106,21 +105,6 @@ static void __init n8x0_usb_init(void) {}
#endif /*CONFIG_USB_MUSB_TUSB6010 */
static struct omap2_mcspi_device_config p54spi_mcspi_config = {
.turbo_mode = 0,
};
static struct spi_board_info n800_spi_board_info[] __initdata = {
{
.modalias = "p54spi",
.bus_num = 2,
.chip_select = 0,
.max_speed_hz = 48000000,
.controller_data = &p54spi_mcspi_config,
},
};
#if defined(CONFIG_MENELAUS) && IS_ENABLED(CONFIG_MMC_OMAP)
/*
@ -524,7 +508,5 @@ omap_late_initcall(n8x0_late_initcall);
void * __init n8x0_legacy_init(void)
{
board_check_revision();
spi_register_board_info(n800_spi_board_info,
ARRAY_SIZE(n800_spi_board_info));
return &mmc1_data;
}

View File

@ -8,6 +8,7 @@
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
@ -15,7 +16,7 @@
#include <linux/irq.h>
#include <linux/spi/spi.h>
#include <linux/etherdevice.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include "p54spi.h"
@ -30,19 +31,6 @@
MODULE_FIRMWARE("3826.arm");
MODULE_FIRMWARE("3826.eeprom");
/* gpios should be handled in board files and provided via platform data,
* but because it's currently impossible for p54spi to have a header file
* in include/linux, let's use module parameters for now
*/
static int p54spi_gpio_power = 97;
module_param(p54spi_gpio_power, int, 0444);
MODULE_PARM_DESC(p54spi_gpio_power, "gpio number for power line");
static int p54spi_gpio_irq = 87;
module_param(p54spi_gpio_irq, int, 0444);
MODULE_PARM_DESC(p54spi_gpio_irq, "gpio number for irq line");
static void p54spi_spi_read(struct p54s_priv *priv, u8 address,
void *buf, size_t len)
{
@ -262,14 +250,14 @@ static int p54spi_upload_firmware(struct ieee80211_hw *dev)
static void p54spi_power_off(struct p54s_priv *priv)
{
disable_irq(gpio_to_irq(p54spi_gpio_irq));
gpio_set_value(p54spi_gpio_power, 0);
disable_irq(priv->irq);
gpiod_set_value(priv->gpio_powerdown, 1);
}
static void p54spi_power_on(struct p54s_priv *priv)
{
gpio_set_value(p54spi_gpio_power, 1);
enable_irq(gpio_to_irq(p54spi_gpio_irq));
gpiod_set_value(priv->gpio_powerdown, 0);
enable_irq(priv->irq);
/* need to wait a while before device can be accessed, the length
* is just a guess
@ -608,31 +596,20 @@ static int p54spi_probe(struct spi_device *spi)
goto err_free;
}
ret = gpio_request(p54spi_gpio_power, "p54spi power");
if (ret < 0) {
dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret);
priv->gpio_powerdown = gpiod_get_optional(&spi->dev, "powerdown", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpio_powerdown)) {
ret = dev_err_probe(&priv->spi->dev, PTR_ERR(priv->gpio_powerdown),
"powerdown GPIO request failed\n");
goto err_free;
}
ret = gpio_request(p54spi_gpio_irq, "p54spi irq");
ret = request_irq(spi->irq, p54spi_interrupt, IRQF_NO_AUTOEN, "p54spi", priv->spi);
if (ret < 0) {
dev_err(&priv->spi->dev, "irq GPIO request failed: %d", ret);
dev_err(&priv->spi->dev, "request_irq() failed");
goto err_free_gpio_power;
}
gpio_direction_output(p54spi_gpio_power, 0);
gpio_direction_input(p54spi_gpio_irq);
ret = request_irq(gpio_to_irq(p54spi_gpio_irq),
p54spi_interrupt, IRQF_NO_AUTOEN, "p54spi",
priv->spi);
if (ret < 0) {
dev_err(&priv->spi->dev, "request_irq() failed");
goto err_free_gpio_irq;
}
irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING);
priv->irq = spi->irq;
INIT_WORK(&priv->work, p54spi_work);
init_completion(&priv->fw_comp);
INIT_LIST_HEAD(&priv->tx_pending);
@ -659,11 +636,9 @@ static int p54spi_probe(struct spi_device *spi)
err_free_common:
release_firmware(priv->firmware);
free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
err_free_gpio_irq:
gpio_free(p54spi_gpio_irq);
free_irq(priv->irq, spi);
err_free_gpio_power:
gpio_free(p54spi_gpio_power);
gpiod_put(priv->gpio_powerdown);
err_free:
p54_free_common(priv->hw);
return ret;
@ -675,10 +650,8 @@ static void p54spi_remove(struct spi_device *spi)
p54_unregister_common(priv->hw);
free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
gpio_free(p54spi_gpio_power);
gpio_free(p54spi_gpio_irq);
free_irq(priv->irq, spi);
gpiod_put(priv->gpio_powerdown);
release_firmware(priv->firmware);
mutex_destroy(&priv->mutex);
@ -686,10 +659,16 @@ static void p54spi_remove(struct spi_device *spi)
p54_free_common(priv->hw);
}
static const struct of_device_id p54spi_of_ids[] = {
{ .compatible = "st,stlc4560", },
{ },
};
MODULE_DEVICE_TABLE(of, p54spi_of_ids);
static struct spi_driver p54spi_driver = {
.driver = {
.name = "p54spi",
.of_match_table = p54spi_of_ids,
},
.probe = p54spi_probe,

View File

@ -107,6 +107,9 @@ struct p54s_priv {
enum fw_state fw_state;
const struct firmware *firmware;
struct gpio_desc *gpio_powerdown;
int irq;
};
#endif /* P54SPI_H */

View File

@ -851,7 +851,7 @@ static int resume(struct usb_interface *interface)
#endif
static struct usb_driver driver = {
static struct usb_driver usbdriver = {
.name = KBUILD_MODNAME,
.id_table = usb_ids,
.probe = probe,
@ -865,25 +865,7 @@ static struct usb_driver driver = {
.disable_hub_initiated_lpm = 1,
};
static int __init usb_init(void)
{
int r;
r = usb_register(&driver);
if (r) {
pr_err("%s usb_register() failed %d\n", driver.name, r);
return r;
}
pr_debug("Driver initialized :%s\n", driver.name);
return 0;
}
static void __exit usb_exit(void)
{
usb_deregister(&driver);
pr_debug("%s %s\n", driver.name, __func__);
}
module_usb_driver(usbdriver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("USB driver for pureLiFi devices");
@ -891,6 +873,3 @@ MODULE_AUTHOR("pureLiFi");
MODULE_VERSION("1.0");
MODULE_FIRMWARE("plfxlc/lifi-x.bin");
MODULE_DEVICE_TABLE(usb, usb_ids);
module_init(usb_init);
module_exit(usb_exit);

View File

@ -1009,11 +1009,12 @@ struct rt2x00_dev {
/* Extra TX headroom required for alignment purposes. */
unsigned int extra_tx_headroom;
struct usb_anchor *anchor;
unsigned int num_proto_errs;
/* Clock for System On Chip devices. */
struct clk *clk;
struct usb_anchor anchor[];
};
struct rt2x00_bar_list_entry {

View File

@ -804,7 +804,7 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
usb_reset_device(usb_dev);
hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
hw = ieee80211_alloc_hw(struct_size(rt2x00dev, anchor, 1), ops->hw);
if (!hw) {
rt2x00_probe_err("Failed to allocate hardware\n");
return -ENOMEM;
@ -826,13 +826,6 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
if (retval)
goto exit_free_device;
rt2x00dev->anchor = devm_kmalloc(&usb_intf->dev,
sizeof(struct usb_anchor),
GFP_KERNEL);
if (!rt2x00dev->anchor) {
retval = -ENOMEM;
goto exit_free_reg;
}
init_usb_anchor(rt2x00dev->anchor);
retval = rt2x00lib_probe_dev(rt2x00dev);
@ -843,8 +836,6 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
exit_free_anchor:
usb_kill_anchored_urbs(rt2x00dev->anchor);
exit_free_reg:
rt2x00usb_free_reg(rt2x00dev);
exit_free_device:

View File

@ -4554,6 +4554,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
.mac.mac_cap = {
[0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
},
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@ -4667,6 +4670,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
.mac.mac_cap = {
[0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
},
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@ -4841,6 +4847,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
.mac.mac_cap = {
[0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
},
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@ -4971,6 +4980,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
.mac.mac_cap = {
[0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
},
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@ -5169,6 +5181,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
.mac.mac_cap = {
[0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
},
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@ -5320,6 +5335,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
.mac.mac_cap = {
[0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
},
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@ -5416,6 +5434,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
.mac.mac_cap = {
[0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
},
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@ -5714,8 +5735,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hw->wiphy->nan_capa.n_antennas = 0x22;
hw->wiphy->nan_capa.max_channel_switch_time = 0;
hw->wiphy->nan_capa.dev_capabilities =
NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED;
wiphy_ext_feature_set(hw->wiphy,
NL80211_EXT_FEATURE_SECURE_NAN);
@ -6766,9 +6785,15 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
param.p2p_device = true;
}
if (param.nan_device)
if (param.nan_device) {
if (param.multi_radio) {
NL_SET_ERR_MSG(info->extack,
"NAN is not supported on multi-radio wiphys");
return -EINVAL;
}
param.iftypes |= BIT(NL80211_IFTYPE_NAN) |
BIT(NL80211_IFTYPE_NAN_DATA);
}
if (info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]) {
u32 len = nla_len(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]);

View File

@ -1190,6 +1190,16 @@ int cfg80211_chandef_add_npca(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef,
const struct ieee80211_uhr_npca_info *npca);
/**
* cfg80211_chandef_add_dbe - parse and add DBE information to chandef
* @chandef: the chandef to expand
* @dbe: the DBE information, must be size-checked if not %NULL
*
* Returns: 0 for success, a negative error code otherwise
*/
int cfg80211_chandef_add_dbe(struct cfg80211_chan_def *chandef,
const struct ieee80211_uhr_dbe_info *dbe);
/**
* nl80211_send_chandef - sends the channel definition.
* @msg: the msg to send channel definition

View File

@ -751,8 +751,14 @@ _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
struct ieee80211_link_data *rsvd_for,
bool check_reserved)
{
u32 changed = __ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for,
check_reserved);
u32 changed;
/* No recalc for S1G chan ctx's */
if (cfg80211_chandef_is_s1g(&ctx->conf.def))
return;
changed = __ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for,
check_reserved);
/* check is BW narrowed */
ieee80211_chan_bw_change(local, ctx, false, true);

View File

@ -6,7 +6,7 @@
* Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015-2017 Intel Deutschland GmbH
* Copyright 2018-2020, 2022-2025 Intel Corporation
* Copyright 2018-2020, 2022-2026 Intel Corporation
*/
#include <crypto/utils.h>
@ -150,11 +150,14 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
sta = key->sta;
/*
* If this is a per-STA GTK, check if it
* is supported; if not, return.
* Allow installation of a per-STA GTK if per-STA GTK is supported
* by the driver or the interface is a NAN Data interface (as
* per-station GTKs are required to be supported if secure NAN is
* supported).
*/
if (sta && !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE) &&
!ieee80211_hw_check(&key->local->hw, SUPPORTS_PER_STA_GTK))
!(ieee80211_hw_check(&key->local->hw, SUPPORTS_PER_STA_GTK) ||
sdata->vif.type == NL80211_IFTYPE_NAN_DATA))
goto out_unsupported;
if (sta && !sta->uploaded)
@ -867,11 +870,16 @@ int ieee80211_key_link(struct ieee80211_key *key,
alt_key = wiphy_dereference(sdata->local->hw.wiphy,
sta->ptk[idx ^ 1]);
/* The rekey code assumes that the old and new key are using
/*
* The rekey code assumes that the old and new key are using
* the same cipher. Enforce the assumption for pairwise keys.
* NAN Data interfaces are exempt: Wi-Fi Aware v4.0 section 7.4
* requires upgrading the ND-TKSA when a new NDP negotiates a
* stronger cipher suite.
*/
if ((alt_key && alt_key->conf.cipher != key->conf.cipher) ||
(old_key && old_key->conf.cipher != key->conf.cipher)) {
if (sdata->vif.type != NL80211_IFTYPE_NAN_DATA &&
((alt_key && alt_key->conf.cipher != key->conf.cipher) ||
(old_key && old_key->conf.cipher != key->conf.cipher))) {
ret = -EOPNOTSUPP;
goto out;
}

View File

@ -2,7 +2,7 @@
/*
* MLO link handling
*
* Copyright (C) 2022-2025 Intel Corporation
* Copyright (C) 2022-2026 Intel Corporation
*/
#include <linux/slab.h>
#include <linux/kernel.h>
@ -307,6 +307,9 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
if (old_links == new_links && dormant_links == sdata->vif.dormant_links)
return 0;
if (!old_links || !new_links)
WARN_ON(sta_info_flush(sdata, -1) > 0);
/* if there were no old links, need to clear the pointers to deflink */
if (!old_links)
rem |= BIT(0);

View File

@ -3516,6 +3516,23 @@ static u8 ieee80211_sta_nss_capability(struct link_sta_info *link_sta)
void ieee80211_sta_init_nss_bw_capa(struct link_sta_info *link_sta,
struct cfg80211_chan_def *chandef)
{
/*
* TODO: The entirety of the STA Tx/Rx bandwidth handling
* assumes 20MHz based widths, so for now don't initialise
* pubsta->bandwidth for S1G bands. Since enum
* ieee80211_sta_rx_bandwidth is ordered, we will probably
* need to introduce ieee80211_s1g_sta_rx_bandwidth with
* S1G widths and associated S1G specific code. Additionally,
* existing S1G hardware is all 1SS, in the future if hardware
* starts supporting >1SS this should be implemented in
* ieee80211_sta_nss_capability().
*/
if (cfg80211_chandef_is_s1g(chandef)) {
link_sta->capa_nss = 1;
link_sta->pub->rx_nss = 1;
return;
}
link_sta->capa_nss = ieee80211_sta_nss_capability(link_sta);
link_sta->pub->rx_nss = link_sta->capa_nss;

View File

@ -2203,11 +2203,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
break;
case NL80211_IFTYPE_NAN:
res = ieee80211_reconfig_nan(sdata);
if (res < 0) {
ieee80211_handle_reconfig_failure(local);
return res;
}
WARN_ON(ieee80211_reconfig_nan(sdata));
break;
case NL80211_IFTYPE_NAN_DATA:
case NL80211_IFTYPE_AP_VLAN:

View File

@ -619,6 +619,136 @@ int cfg80211_chandef_add_npca(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_chandef_add_npca);
int cfg80211_chandef_add_dbe(struct cfg80211_chan_def *chandef,
const struct ieee80211_uhr_dbe_info *dbe)
{
struct cfg80211_chan_def new_chandef = *chandef;
u16 starting_freq, bw_mhz, start_old, start_new;
u8 bw, punct_shift;
int offset, index;
if (!dbe)
return 0;
if (!cfg80211_chandef_valid(chandef))
return -EINVAL;
if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT)
return -EINVAL;
bw = u8_get_bits(dbe->params, IEEE80211_UHR_DBE_OPER_BANDWIDTH);
switch (chandef->chan->band) {
case NL80211_BAND_5GHZ:
if (bw > IEEE80211_UHR_DBE_OPER_BW_160)
return -EINVAL;
if (chandef->chan->center_freq < 5745)
starting_freq = 5180; /* channel 36 */
else
starting_freq = 5745; /* channel 149 */
break;
case NL80211_BAND_6GHZ:
starting_freq = 5955; /* channel 1 center */
break;
default:
return -EINVAL;
}
switch (bw) {
case IEEE80211_UHR_DBE_OPER_BW_320_2:
case IEEE80211_UHR_DBE_OPER_BW_320_1:
if (chandef->width == NL80211_CHAN_WIDTH_160)
break;
fallthrough;
case IEEE80211_UHR_DBE_OPER_BW_160:
if (chandef->width == NL80211_CHAN_WIDTH_80)
break;
fallthrough;
case IEEE80211_UHR_DBE_OPER_BW_80:
if (chandef->width == NL80211_CHAN_WIDTH_40)
break;
fallthrough;
case IEEE80211_UHR_DBE_OPER_BW_40:
if (chandef->width == NL80211_CHAN_WIDTH_20)
break;
fallthrough;
default:
return -EINVAL;
}
switch (bw) {
case IEEE80211_UHR_DBE_OPER_BW_320_2:
/* 320-2 starts shifted by 160 */
starting_freq += 160;
fallthrough;
case IEEE80211_UHR_DBE_OPER_BW_320_1:
new_chandef.width = NL80211_CHAN_WIDTH_320;
bw_mhz = 320;
break;
case IEEE80211_UHR_DBE_OPER_BW_160:
new_chandef.width = NL80211_CHAN_WIDTH_160;
bw_mhz = 160;
break;
case IEEE80211_UHR_DBE_OPER_BW_80:
new_chandef.width = NL80211_CHAN_WIDTH_80;
bw_mhz = 80;
break;
case IEEE80211_UHR_DBE_OPER_BW_40:
new_chandef.width = NL80211_CHAN_WIDTH_40;
bw_mhz = 40;
break;
}
/* this should only happen for 320-2 and misconfigured AP */
if (chandef->chan->center_freq < starting_freq)
return -EINVAL;
offset = chandef->chan->center_freq - starting_freq;
index = offset / bw_mhz;
start_new = starting_freq - 10 + index * bw_mhz;
new_chandef.center_freq1 = start_new + bw_mhz / 2;
start_old = chandef->center_freq1 -
cfg80211_chandef_get_width(chandef) / 2;
/*
* If the DBE channel extends downward below the lower
* edge of the BSS channel, we need to shift puncturing
* bitmaps up to adjust for that.
*/
if (start_new < start_old)
punct_shift = (start_old - start_new) / 20;
else
punct_shift = 0;
new_chandef.punctured <<= punct_shift;
new_chandef.npca_punctured <<= punct_shift;
if (dbe->params & IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES) {
u16 punct_mask = ((1 << (bw_mhz / 40)) - 1) << punct_shift;
u16 punctured = le16_to_cpu(dbe->dis_subch_bmap[0]);
if ((punctured & punct_mask) != (new_chandef.punctured & punct_mask))
return -EINVAL;
new_chandef.punctured = punctured;
}
if (!cfg80211_chandef_valid(&new_chandef))
return -EINVAL;
/*
* If e.g. a 40 MHz BSS channel (erroneously) occupies the center of the
* DBE 80 MHz channel, they would be incompatible; check and reject.
*/
if (!cfg80211_chandef_compatible(&new_chandef, chandef))
return -EINVAL;
*chandef = new_chandef;
return 0;
}
EXPORT_SYMBOL(cfg80211_chandef_add_dbe);
static const struct cfg80211_chan_def *
check_chandef_primary_compat(const struct cfg80211_chan_def *c1,
const struct cfg80211_chan_def *c2,

View File

@ -2,13 +2,18 @@
/*
* KUnit tests for channel helper functions
*
* Copyright (C) 2023-2024 Intel Corporation
* Copyright (C) 2023-2024, 2026 Intel Corporation
*/
#include <net/cfg80211.h>
#include <kunit/test.h>
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static struct ieee80211_channel chan_2ghz_1 = {
.band = NL80211_BAND_2GHZ,
.center_freq = 2412,
};
static struct ieee80211_channel chan_6ghz_1 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 5955,
@ -215,14 +220,189 @@ static void test_chandef_compat(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, ret, expect);
}
static struct kunit_case chandef_compat_test_cases[] = {
static const struct chandef_dbe_case {
const char *desc;
struct cfg80211_chan_def c;
u8 dbe[3];
bool fails;
u16 cf1;
} chandef_dbe_cases[] = {
{
.desc = "non-HT failure",
.c = {
.width = NL80211_CHAN_WIDTH_20_NOHT,
.chan = &chan_6ghz_1,
.center_freq1 = 5955,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_40,
.fails = true,
},
{
.desc = "2.4 GHz fails",
.c = {
.width = NL80211_CHAN_WIDTH_20,
.chan = &chan_2ghz_1,
.center_freq1 = 2412,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_40,
.fails = true,
},
{
.desc = "DBE narrower",
.c = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_1,
.center_freq1 = 5955 + 10 + 20 + 40 + 80,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_160,
.fails = true,
},
{
.desc = "DBE to 320-1",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1,
.cf1 = 6425,
},
{
.desc = "DBE to 320-2",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2,
.cf1 = 6585,
},
{
.desc = "bad disabled subchannel bitmap - not enough in BSS (1)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0 */
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - too much in BSS (1)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x0300 */
.dbe[2] = 0x03,
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - not enough in BSS (2)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0 */
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - too much in BSS (2)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x03 */
.dbe[1] = 0x03,
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - bad bitmap",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x1100 */
.dbe[2] = 0x11,
.fails = true,
},
{
.desc = "good disabled subchannel bitmap (1)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0003,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x0300 */
.dbe[2] = 0x03,
.cf1 = 6425,
},
{
.desc = "good disabled subchannel bitmap (2)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0003,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x0003 */
.dbe[1] = 0x03,
.cf1 = 6585,
},
};
KUNIT_ARRAY_PARAM_DESC(chandef_dbe, chandef_dbe_cases, desc)
static void test_chandef_dbe(struct kunit *test)
{
const struct chandef_dbe_case *params = test->param_value;
struct cfg80211_chan_def c = params->c;
int ret;
KUNIT_EXPECT_EQ(test, cfg80211_chandef_valid(&params->c), true);
ret = cfg80211_chandef_add_dbe(&c, (void *)params->dbe);
KUNIT_EXPECT_EQ(test, ret != 0, params->fails);
if (params->fails)
return;
KUNIT_EXPECT_EQ(test, c.center_freq1, params->cf1);
}
static struct kunit_case chandef_test_cases[] = {
KUNIT_CASE_PARAM(test_chandef_compat, chandef_compat_gen_params),
KUNIT_CASE_PARAM(test_chandef_dbe, chandef_dbe_gen_params),
{}
};
static struct kunit_suite chandef_compat = {
.name = "cfg80211-chandef-compat",
.test_cases = chandef_compat_test_cases,
static struct kunit_suite chandef = {
.name = "cfg80211-chandef",
.test_cases = chandef_test_cases,
};
kunit_test_suite(chandef_compat);
kunit_test_suite(chandef);