Linux wireless drivers development
 help / color / mirror / Atom feed
From: jeff.chen_1@oss.nxp.com
To: linux-wireless@vger.kernel.org
Cc: johannes@sipsolutions.net, francesco@dolcini.it,
	kees+treewide@kernel.org, arend.vanspriel@broadcom.com,
	benjamin.berg@intel.com, Jeff Chen <jeff.chen_1@nxp.com>
Subject: [PATCH nxpwifi-next v3 3/3] wifi: nxpwifi: add netlink vendor command for TX power limits
Date: Mon,  5 Oct 2026 13:44:14 +0800	[thread overview]
Message-ID: <20261005054414.2874415-4-jeff.chen_1@oss.nxp.com> (raw)
In-Reply-To: <20261005054414.2874415-1-jeff.chen_1@oss.nxp.com>

From: Jeff Chen <jeff.chen_1@nxp.com>

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 <jeff.chen_1@nxp.com>
---
 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..3f1834cf8844
--- /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 <net/mac80211.h>
+#include <net/netlink.h>
+#include "vendor.h"
+#include "main.h"
+
+/* Attributes for NXP_VENDOR_CMD_TXPOWER_LIMIT. */
+static const struct nla_policy
+nxpwifi_txpwrlimit_policy[NXP_ATTR_TXPWRLIMIT_MAX + 1] = {
+	[NXP_ATTR_TXPWRLIMIT_ACTION] =
+		NLA_POLICY_MAX(NLA_U8, NXP_TXPWRLIMIT_ACTION_SET),
+	[NXP_ATTR_TXPWRLIMIT_SUBBAND] = { .type = NLA_U8 },
+	[NXP_ATTR_TXPWRLIMIT_DATA] = { .type = NLA_BINARY,
+				       .len  = NXPWIFI_MAX_TRPC_BUF },
+};
+
+static bool nxpwifi_txpwrlimit_subband_valid(u8 subband)
+{
+	switch (subband) {
+	case NXP_TXPWRLIMIT_SUBBAND_2G:
+	case NXP_TXPWRLIMIT_SUBBAND_5G_0:
+	case NXP_TXPWRLIMIT_SUBBAND_5G_1:
+	case NXP_TXPWRLIMIT_SUBBAND_5G_2:
+	case NXP_TXPWRLIMIT_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_TXPWRLIMIT_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_TXPWRLIMIT_MAX, data, data_len,
+			nxpwifi_txpwrlimit_policy, NULL);
+	if (ret)
+		return ret;
+
+	if (!tb[NXP_ATTR_TXPWRLIMIT_ACTION])
+		return -EINVAL;
+	action = nla_get_u8(tb[NXP_ATTR_TXPWRLIMIT_ACTION]);
+
+	if (tb[NXP_ATTR_TXPWRLIMIT_SUBBAND]) {
+		subband = nla_get_u8(tb[NXP_ATTR_TXPWRLIMIT_SUBBAND]);
+		if (!nxpwifi_txpwrlimit_subband_valid(subband))
+			return -EINVAL;
+	} else if (action == NXP_TXPWRLIMIT_ACTION_GET) {
+		/* A subband selector is required for GET operations. */
+		return -EINVAL;
+	}
+
+	if (action == NXP_TXPWRLIMIT_ACTION_SET) {
+		if (!tb[NXP_ATTR_TXPWRLIMIT_DATA])
+			return -EINVAL;
+		tlv_len = nla_len(tb[NXP_ATTR_TXPWRLIMIT_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_TXPWRLIMIT_ACTION_SET) ?
+			       tlv_len : NXPWIFI_MAX_TRPC_BUF, GFP_KERNEL);
+	if (!ch_trpc)
+		return -ENOMEM;
+
+	ch_trpc->action = (action == NXP_TXPWRLIMIT_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_TXPWRLIMIT_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_TXPWRLIMIT_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_TXPWRLIMIT_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_txpwrlimit_policy,
+		.maxattr = NXP_ATTR_TXPWRLIMIT_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 <uapi/linux/nl80211-vnd-nxp.h>
+
+void nxpwifi_set_vendor_commands(struct wiphy *wiphy);
+#endif /* __NXPWIFI_VENDOR_H__ */
-- 
2.34.1


      parent reply	other threads:[~2026-10-05  5:44 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-05  5:44 [PATCH nxpwifi-next v3 0/3] wifi: nxpwifi: add TX power limit configuration support jeff.chen_1
2026-10-05  5:44 ` [PATCH nxpwifi-next v3 1/3] wifi: nl80211: vendor-cmd: add NXP vendor command definitions jeff.chen_1
2026-10-05  5:49   ` Johannes Berg
2026-10-05  8:31   ` jeff.chen_1
2026-10-05  5:44 ` [PATCH nxpwifi-next v3 2/3] wifi: nxpwifi: add firmware TRPC command support jeff.chen_1
2026-10-05  5:44 ` jeff.chen_1 [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261005054414.2874415-4-jeff.chen_1@oss.nxp.com \
    --to=jeff.chen_1@oss.nxp.com \
    --cc=arend.vanspriel@broadcom.com \
    --cc=benjamin.berg@intel.com \
    --cc=francesco@dolcini.it \
    --cc=jeff.chen_1@nxp.com \
    --cc=johannes@sipsolutions.net \
    --cc=kees+treewide@kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox