diff --git a/Documentation/devicetree/bindings/net/wireless/st,stlc4560.yaml b/Documentation/devicetree/bindings/net/wireless/st,stlc4560.yaml new file mode 100644 index 000000000000..a32265c07350 --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/st,stlc4560.yaml @@ -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 + +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>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 34da4c83f0a2..ca6c7425b45f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20156,6 +20156,7 @@ M: Christian Lamparter 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 diff --git a/arch/arm/boot/dts/ti/omap/omap2.dtsi b/arch/arm/boot/dts/ti/omap/omap2.dtsi index afabb36a8ac1..fdc1790adf43 100644 --- a/arch/arm/boot/dts/ti/omap/omap2.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap2.dtsi @@ -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>; diff --git a/arch/arm/boot/dts/ti/omap/omap2420-n8x0-common.dtsi b/arch/arm/boot/dts/ti/omap/omap2420-n8x0-common.dtsi index 63b0b4921e4e..fe9dd8bbfc85 100644 --- a/arch/arm/boot/dts/ti/omap/omap2420-n8x0-common.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap2420-n8x0-common.dtsi @@ -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 */ + }; +}; diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c index 969265d5d5c6..d9acd32c5457 100644 --- a/arch/arm/mach-omap2/board-n8x0.c +++ b/arch/arm/mach-omap2/board-n8x0.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -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; } diff --git a/drivers/net/wireless/intersil/p54/p54spi.c b/drivers/net/wireless/intersil/p54/p54spi.c index 9d66dcae54e0..d18be2545028 100644 --- a/drivers/net/wireless/intersil/p54/p54spi.c +++ b/drivers/net/wireless/intersil/p54/p54spi.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -15,7 +16,7 @@ #include #include #include -#include +#include #include #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, diff --git a/drivers/net/wireless/intersil/p54/p54spi.h b/drivers/net/wireless/intersil/p54/p54spi.h index e5619a13fd61..118785cc635a 100644 --- a/drivers/net/wireless/intersil/p54/p54spi.h +++ b/drivers/net/wireless/intersil/p54/p54spi.h @@ -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 */ diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c index 5724ec173e64..6d24086eb8b7 100644 --- a/drivers/net/wireless/purelifi/plfxlc/usb.c +++ b/drivers/net/wireless/purelifi/plfxlc/usb.c @@ -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); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index 665887e9b118..7d313e86d3f2 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -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 { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c index 174d89b0b1d7..47e427ea8622 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c @@ -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: diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index dc9775cd9f54..67a33e5e7d54 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -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]); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ddcf559430dd..69dc9a978861 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -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 diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index b9d563f927da..c64a99131954 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -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); diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 4b8965633df3..f45e792abede 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -6,7 +6,7 @@ * Copyright 2007-2008 Johannes Berg * 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 @@ -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; } diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 93e290dd783f..e81dd02de12e 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -2,7 +2,7 @@ /* * MLO link handling * - * Copyright (C) 2022-2025 Intel Corporation + * Copyright (C) 2022-2026 Intel Corporation */ #include #include @@ -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); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index aba2fabfe0db..0ea37016cd4f 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -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; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index c3861e414804..255905f971c8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -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: diff --git a/net/wireless/chan.c b/net/wireless/chan.c index ed35b55b1b67..f0811efb5d0f 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -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, diff --git a/net/wireless/tests/chan.c b/net/wireless/tests/chan.c index 7b97b731993c..65eb18c498de 100644 --- a/net/wireless/tests/chan.c +++ b/net/wireless/tests/chan.c @@ -2,13 +2,18 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2023-2024 Intel Corporation + * Copyright (C) 2023-2024, 2026 Intel Corporation */ #include #include 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(¶ms->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);