From 6970034ae136822827df6af9bf86162935c94d6e Mon Sep 17 00:00:00 2001
From: "Lin Ian (CSS ICW ENG WFS SW WFSW 1)" <ian.lin@infineon.com>
Date: Fri, 23 May 2025 07:03:12 +0000
Subject: [PATCH 4/4] non-upstream: add IW vendor cmds to config mchan
 algorithm

Add three configurations for mchan mcc scenario.
Set mchan iovar after dongle enter apsta mcc condition.
The flow will only process if fwcap contains "mchan_config".
Usage: iw dev wlan0 vendor send 0x000319 0x1f [0x0, 0x1, 0x2]
 - config 0: default, mchan_algo=2, 25ms:25ms
 - config 1: vedio, mchan_algo=5, 29ms:21ms
 - config 2: audio, mchan_algo=1 & mchan_bw=32, 68ms:32ms

Fixes SWLINUX-5212
Signed-off-by: Ian Lin <ian.lin@infineon.com>


(cherry picked from commit fb93a544f5178fd4e4ddba6cd8691b7d6554115d)

Co-authored-by: Ian Lin <ian.lin@infineon.com>
---
 .../broadcom/brcm80211/brcmfmac/cfg80211.c    | 76 +++++++++++++++++++
 .../broadcom/brcm80211/brcmfmac/cfg80211.h    |  1 +
 .../broadcom/brcm80211/brcmfmac/feature.c     |  1 +
 .../broadcom/brcm80211/brcmfmac/feature.h     |  3 +-
 .../broadcom/brcm80211/brcmfmac/fwil_types.h  | 13 ++++
 .../broadcom/brcm80211/brcmfmac/vendor.c      |  8 ++
 .../broadcom/brcm80211/brcmfmac/vendor_ifx.c  | 14 ++++
 .../broadcom/brcm80211/brcmfmac/vendor_ifx.h  |  6 +-
 8 files changed, 120 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 49526f181ac5..6b8fdb14c2ac 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -8012,6 +8012,79 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
 	return 0;
 }
 
+s32 brcmf_mchan_config(struct brcmf_cfg80211_info *cfg)
+{
+	struct brcmf_pub *drvr = cfg->pub;
+	struct brcmf_if *ifp;
+	int ifidx = 0;
+	s32 err = 0;
+	struct brcmf_assoclist_le assoclist = {0};
+	u8 mac_addr[ETH_ALEN] = {0}, mac_null[ETH_ALEN] = {0};
+	u32 mchan_arg = 0;
+	bool staif_connected = false, apif_connected = false;
+
+	if (!brcmf_feat_is_enabled(drvr->iflist[0], BRCMF_FEAT_MCHAN_CONFIG))
+		return 0;
+
+	for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
+		ifp = drvr->iflist[ifidx];
+		if (ifp && ifp->vif) {
+			if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) {
+				err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, &mac_addr,
+							     sizeof(mac_addr));
+				if (err)
+					bphy_err(drvr, "BRCMF_C_GET_BSSID fail (%d)\n", err);
+				else if (memcmp(mac_null, mac_addr, ETH_ALEN))
+					staif_connected = true;
+			} else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
+				memset(&assoclist, 0, sizeof(assoclist));
+				assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
+				err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
+							     &assoclist,
+							     sizeof(assoclist));
+				if (err)
+					bphy_err(drvr, "BRCMF_C_GET_ASSOCLIST fail (%d)\n", err);
+				else if (assoclist.count > 0)
+					apif_connected = true;
+			}
+		}
+	}
+
+	if (staif_connected && apif_connected) {
+		brcmf_dbg(TRACE, "apsta case, set mchan config %d\n", cfg->mchan_conf);
+		switch (cfg->mchan_conf) {
+		case BRCMF_MCHAN_CONF_DEFAULT:
+			mchan_arg = BRCMF_MCHAN_SI_ALGO;
+			err = brcmf_fil_iovar_data_set(drvr->iflist[0], "mchan_algo",
+						       &mchan_arg, sizeof(mchan_arg));
+			break;
+		case BRCMF_MCHAN_CONF_VEDIO:
+			mchan_arg = BRCMF_MCHAN_ASYMMETRIC_SI_ALGO;
+			err = brcmf_fil_iovar_data_set(drvr->iflist[0], "mchan_algo",
+						       &mchan_arg, sizeof(mchan_arg));
+			break;
+		case BRCMF_MCHAN_CONF_AUDIO:
+			mchan_arg = BRCMF_MCHAN_BANDWIDTH_ALGO;
+			err = brcmf_fil_iovar_data_set(drvr->iflist[0], "mchan_algo",
+						       &mchan_arg, sizeof(mchan_arg));
+			if (err)
+				goto cmd_fail;
+			mchan_arg = BRCMF_MCHAN_BANDWIDTH_VAL;
+			err = brcmf_fil_iovar_data_set(drvr->iflist[0], "mchan_bw",
+						       &mchan_arg, sizeof(mchan_arg));
+			break;
+		default:
+			brcmf_dbg(TRACE, "unexpected mchan conf %d\n", cfg->mchan_conf);
+			break;
+		}
+cmd_fail:
+		if (err)
+			bphy_err(drvr, "cmd fail while set mchan config (%d)\n", err);
+	}
+
+	return 0;
+}
+
 static s32
 brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
 			       struct net_device *ndev,
@@ -8085,6 +8158,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
 			brcmf_del_sta(ifp, e->addr);
 		}
 		err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
+		brcmf_mchan_config(cfg);
 	} else if (brcmf_is_linkup(ifp->vif, e)) {
 		brcmf_dbg(CONN, "Linkup\n");
 		if (brcmf_is_ibssmode(ifp->vif)) {
@@ -8105,6 +8179,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
 			}
 		}
 		brcmf_net_setcarrier(ifp, true);
+		brcmf_mchan_config(cfg);
 	} else if (brcmf_is_linkdown(ifp->vif, e)) {
 		brcmf_dbg(CONN, "Linkdown\n");
 		if (!brcmf_is_ibssmode(ifp->vif) &&
@@ -10717,6 +10792,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
 	cfg->pub = drvr;
 	cfg->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED;
 	cfg->num_softap = 0;
+	cfg->mchan_conf = BRCMF_MCHAN_CONF_DEFAULT;
 	init_vif_event(&cfg->vif_event);
 	INIT_LIST_HEAD(&cfg->vif_list);
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 9ffdce5f5ff3..f02481e9a168 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -446,6 +446,7 @@ struct brcmf_cfg80211_info {
 	u8 pfn_enable;
 	u8 pfn_connection;
 	struct drv_config_pfn_params pfn_data;
+	u8 mchan_conf;
 };
 
 /**
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index 2798bf16ee35..781e3c1d86e3 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -53,6 +53,7 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
 	{ BRCMF_FEAT_PROPTXSTATUS, "proptxstatus" },
 	{ BRCMF_FEAT_OWE, "owe" },
 	{ BRCMF_FEAT_GTKO, "gtko" },
+	{ BRCMF_FEAT_MCHAN_CONFIG, "mchan_config" },
 };
 
 #ifdef DEBUG
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
index 54e0f7878d86..012a19bfca5a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
@@ -74,7 +74,8 @@
 	BRCMF_FEAT_DEF(ULP) \
 	BRCMF_FEAT_DEF(PROPTXSTATUS) \
 	BRCMF_FEAT_DEF(OWE) \
-	BRCMF_FEAT_DEF(GTKO)
+	BRCMF_FEAT_DEF(GTKO) \
+	BRCMF_FEAT_DEF(MCHAN_CONFIG)
 
 /*
  * Quirks:
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index a3c0b29a76ab..49c46f2fc6b7 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -201,6 +201,19 @@ enum {
 #define BRCMF_EXTAUTH_FAIL	3
 #define BRCMF_EXTAUTH_SUCCESS	4
 
+/* mchan configuration (ap timeslot : sta timeslot)*/
+#define BRCMF_MCHAN_CONF_DEFAULT	0 /* mchan_algo=2 (25ms:25ms) */
+#define BRCMF_MCHAN_CONF_VEDIO		1 /* mchan_algo=5 (29ms:21ms) */
+#define BRCMF_MCHAN_CONF_AUDIO		2 /* mchan_algo=1, mchan_bw=32 (68ms:32ms) */
+/* mchan algo in dongle */
+#define BRCMF_MCHAN_DEFAULT_ALGO 0
+#define BRCMF_MCHAN_BANDWIDTH_ALGO 1
+#define BRCMF_MCHAN_SI_ALGO 2
+#define BRCMF_MCHAN_DYNAMIC_BW_ALGO 3
+#define BRCMF_MCHAN_ALTERNATE_SWITCHING 4
+#define BRCMF_MCHAN_ASYMMETRIC_SI_ALGO 5
+#define BRCMF_MCHAN_BANDWIDTH_VAL 32
+
 /* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each
  * ioctl. It is relatively small because firmware has small maximum size input
  * playload restriction for ioctls.
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
index 9bec90f7fc18..63838cc7ff55 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
@@ -429,6 +429,14 @@ const struct wiphy_vendor_command brcmf_vendor_cmds[] = {
 			   ifx_cfg80211_vndr_cmds_ssid_prot),
 		.maxattr = IFX_VENDOR_ATTR_SSID_PROT_MAX
 	},
+	{
+		IFX_SUBCMD(MCHAN_CONFIG,
+			   (WIPHY_VENDOR_CMD_NEED_WDEV |
+			   WIPHY_VENDOR_CMD_NEED_NETDEV),
+			   VENDOR_CMD_RAW_DATA,
+			   ifx_cfg80211_vndr_cmds_mchan_config)
+	},
+
 };
 
 const struct nl80211_vendor_cmd_info brcmf_vendor_events[] = {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c
index 0d2b16dd64f5..de6126e77ae5 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c
@@ -1488,3 +1488,17 @@ int ifx_cfg80211_vndr_cmds_ssid_prot(struct wiphy *wiphy,
 	return ret;
 }
 
+int ifx_cfg80211_vndr_cmds_mchan_config(struct wiphy *wiphy,
+					struct wireless_dev *wdev,
+					const void *data, int len)
+{
+	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+	u8 val = *(u8 *)data;
+
+	brcmf_dbg(TRACE, "enter, config: %d\n", val);
+
+	if (val <= BRCMF_MCHAN_CONF_AUDIO)
+		cfg->mchan_conf = val;
+
+	return 0;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h
index 224e2a979ddb..9ec05522eeda 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h
@@ -150,7 +150,8 @@ enum ifx_nl80211_vendor_subcmds {
 	SCMD(CMDSTR)		= 28,
 	/* Reserved 29 */
 	SCMD(SSID_PROT)		= 30,
-	SCMD(MAX)		= 31
+	SCMD(MCHAN_CONFIG)	= 31,
+	SCMD(MAX)		= 32
 };
 
 /* enum ifx_vendor_evts - IFX nl80211 vendor events
@@ -846,4 +847,7 @@ int ifx_cfg80211_vndr_cmds_get_pfn_status(struct wiphy *wiphy,
 					  struct wireless_dev *wdev, const void  *data, int len);
 int ifx_cfg80211_vndr_cmds_ssid_prot(struct wiphy *wiphy,
 				     struct wireless_dev *wdev, const void *data, int len);
+int ifx_cfg80211_vndr_cmds_mchan_config(struct wiphy *wiphy,
+					struct wireless_dev *wdev, const void *data, int len);
+
 #endif /* IFX_VENDOR_H */
-- 
2.25.0

