From: Jeff Chen Add support for the NXP TX power limit vendor command. The command provides userspace access to firmware Transmit Rate-based Power Control (TRPC) configuration through NL80211_CMD_VENDOR. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/Makefile | 1 + drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 5 +- drivers/net/wireless/nxp/nxpwifi/main.h | 1 + drivers/net/wireless/nxp/nxpwifi/vendor.c | 152 ++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/vendor.h | 14 ++ 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/nxp/nxpwifi/vendor.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/vendor.h diff --git a/drivers/net/wireless/nxp/nxpwifi/Makefile b/drivers/net/wireless/nxp/nxpwifi/Makefile index 8f581429f28d..f82435253a72 100644 --- a/drivers/net/wireless/nxp/nxpwifi/Makefile +++ b/drivers/net/wireless/nxp/nxpwifi/Makefile @@ -28,6 +28,7 @@ nxpwifi-y += sta_tx.o nxpwifi-y += sta_rx.o nxpwifi-y += uap_txrx.o nxpwifi-y += cfg80211.o +nxpwifi-y += vendor.o nxpwifi-y += ethtool.o nxpwifi-y += 11h.o nxpwifi-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c index 8805439c0b4b..16b970227e9c 100644 --- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c +++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c @@ -10,6 +10,7 @@ #include "cmdevt.h" #include "11n.h" #include "wmm.h" +#include "vendor.h" static const struct ieee80211_iface_limit nxpwifi_ap_sta_limits[] = { { @@ -91,7 +92,7 @@ u8 nxpwifi_get_chan_type(struct nxpwifi_private *priv) } /* Retrieve the driver private data from the wiphy */ -static void *nxpwifi_cfg80211_get_adapter(struct wiphy *wiphy) +void *nxpwifi_cfg80211_get_adapter(struct wiphy *wiphy) { return (void *)(*(unsigned long *)wiphy_priv(wiphy)); } @@ -3834,6 +3835,8 @@ int nxpwifi_register_cfg80211(struct nxpwifi_adapter *adapter) wiphy->cipher_suites = nxpwifi_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(nxpwifi_cipher_suites); + nxpwifi_set_vendor_commands(wiphy); + if (adapter->regd) { wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | REGULATORY_DISABLE_BEACON_HINTS | diff --git a/drivers/net/wireless/nxp/nxpwifi/main.h b/drivers/net/wireless/nxp/nxpwifi/main.h index 9ae017bdc6ca..bbfc4266c48d 100644 --- a/drivers/net/wireless/nxp/nxpwifi/main.h +++ b/drivers/net/wireless/nxp/nxpwifi/main.h @@ -1435,4 +1435,5 @@ void nxpwifi_dev_debugfs_remove(struct nxpwifi_private *priv); int nxpwifi_reinit_sw(struct nxpwifi_adapter *adapter); void nxpwifi_shutdown_sw(struct nxpwifi_adapter *adapter); bool nxpwifi_is_valid_region_code(enum nxpwifi_region_code code); +void *nxpwifi_cfg80211_get_adapter(struct wiphy *wiphy); #endif /* !_NXPWIFI_MAIN_H_ */ diff --git a/drivers/net/wireless/nxp/nxpwifi/vendor.c b/drivers/net/wireless/nxp/nxpwifi/vendor.c new file mode 100644 index 000000000000..ac31cfd38941 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/vendor.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: vendor command interface + * + * Copyright 2026 NXP + */ + +#include +#include +#include "vendor.h" +#include "main.h" + +/* Attributes for NXP_VENDOR_CMD_TXPOWER_LIMIT. */ +static const struct nla_policy +nxpwifi_txpwr_policy[NXP_ATTR_TXPWR_MAX + 1] = { + [NXP_ATTR_TXPWR_ACTION] = NLA_POLICY_MAX(NLA_U8, + NXP_TXPWR_ACTION_SET), + [NXP_ATTR_TXPWR_SUBBAND] = { .type = NLA_U8 }, + [NXP_ATTR_TXPWR_DATA] = { .type = NLA_BINARY, + .len = NXPWIFI_MAX_TRPC_BUF }, +}; + +static bool nxpwifi_txpwr_subband_valid(u8 subband) +{ + switch (subband) { + case NXP_TXPWR_SUBBAND_2G: + case NXP_TXPWR_SUBBAND_5G_0: + case NXP_TXPWR_SUBBAND_5G_1: + case NXP_TXPWR_SUBBAND_5G_2: + case NXP_TXPWR_SUBBAND_5G_3: + return true; + default: + return false; + } +} + +static int nxpwifi_vendor_txpower_limit(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nlattr *tb[NXP_ATTR_TXPWR_MAX + 1]; + struct nxpwifi_ds_chan_trpc_cfg *ch_trpc; + struct nxpwifi_private *priv; + struct sk_buff *resp; + u8 action, subband = 0; + u16 tlv_len = 0; + int ret; + + ret = nla_parse(tb, NXP_ATTR_TXPWR_MAX, data, data_len, + nxpwifi_txpwr_policy, NULL); + if (ret) + return ret; + + if (!tb[NXP_ATTR_TXPWR_ACTION]) + return -EINVAL; + action = nla_get_u8(tb[NXP_ATTR_TXPWR_ACTION]); + + if (tb[NXP_ATTR_TXPWR_SUBBAND]) { + subband = nla_get_u8(tb[NXP_ATTR_TXPWR_SUBBAND]); + if (!nxpwifi_txpwr_subband_valid(subband)) + return -EINVAL; + } else if (action == NXP_TXPWR_ACTION_GET) { + /* A subband selector is required for GET operations. */ + return -EINVAL; + } + + if (action == NXP_TXPWR_ACTION_SET) { + if (!tb[NXP_ATTR_TXPWR_DATA]) + return -EINVAL; + tlv_len = nla_len(tb[NXP_ATTR_TXPWR_DATA]); + } + + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA); + if (!priv) + return -ENODEV; + + /* Reserve space for the returned TRPC table on GET requests. */ + ch_trpc = kzalloc_flex(*ch_trpc, tlvbuffer, + (action == NXP_TXPWR_ACTION_SET) ? + tlv_len : NXPWIFI_MAX_TRPC_BUF, GFP_KERNEL); + if (!ch_trpc) + return -ENOMEM; + + ch_trpc->action = (action == NXP_TXPWR_ACTION_SET) ? + HOST_ACT_GEN_SET : HOST_ACT_GEN_GET; + ch_trpc->subband = subband; + + if (tlv_len) + memcpy(ch_trpc->tlvbuffer, + nla_data(tb[NXP_ATTR_TXPWR_DATA]), tlv_len); + + ch_trpc->len = sizeof(ch_trpc->action) + sizeof(ch_trpc->subband) + + tlv_len; + + ret = nxpwifi_chan_trpc_cfg(priv, NXPWIFI_SYNC_CMD, ch_trpc); + if (ret) + goto done; + + if (action == NXP_TXPWR_ACTION_GET) { + u16 hdr_len = sizeof(ch_trpc->action) + + sizeof(ch_trpc->subband); + u16 resp_len, skb_len; + + if (ch_trpc->len < hdr_len || + ch_trpc->len > hdr_len + NXPWIFI_MAX_TRPC_BUF) { + ret = -EIO; + goto done; + } + + resp_len = ch_trpc->len - hdr_len; + skb_len = nla_total_size(resp_len); + + resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len); + if (!resp) { + ret = -ENOMEM; + goto done; + } + + if (nla_put(resp, NXP_ATTR_TXPWR_DATA, resp_len, + ch_trpc->tlvbuffer)) { + kfree_skb(resp); + ret = -ENOBUFS; + goto done; + } + + ret = cfg80211_vendor_cmd_reply(resp); + } + +done: + kfree(ch_trpc); + return ret; +} + +static const struct wiphy_vendor_command nxpwifi_vendor_commands[] = { + { + .info = { + .vendor_id = NXP_OUI, + .subcmd = NXP_VENDOR_CMD_TXPOWER_LIMIT, + }, + .flags = 0, + .doit = nxpwifi_vendor_txpower_limit, + .policy = nxpwifi_txpwr_policy, + .maxattr = NXP_ATTR_TXPWR_MAX, + }, +}; + +void nxpwifi_set_vendor_commands(struct wiphy *wiphy) +{ + wiphy->vendor_commands = nxpwifi_vendor_commands; + wiphy->n_vendor_commands = ARRAY_SIZE(nxpwifi_vendor_commands); +} diff --git a/drivers/net/wireless/nxp/nxpwifi/vendor.h b/drivers/net/wireless/nxp/nxpwifi/vendor.h new file mode 100644 index 000000000000..fdde8e1a7a40 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/vendor.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: vendor command interface + * + * Copyright 2026 NXP + */ + +#ifndef __NXPWIFI_VENDOR_H__ +#define __NXPWIFI_VENDOR_H__ + +#include + +void nxpwifi_set_vendor_commands(struct wiphy *wiphy); +#endif /* __NXPWIFI_VENDOR_H__ */ -- 2.34.1