Subject: Re: [PATCH] net: wlcore: correctness fix fwlog reading

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:37 +0100

From: Russell King (Oracle) <[email-masked]>

Fix the calculation of clear_offset, which may overflow the end of
the buffer. However, this is harmless if it does because in that case
it will be recalculated when we copy the chunk of messages at the
start of the buffer.

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/event.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index 2499dc908305..6c3a8ea9613e 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -83,7 +83,7 @@ int wlcore_event_fw_logger(struct wl1271 *wl)
 	/* Copy initial part up to the end of ring buffer */
 	len = min(actual_len, available_len);
 	wl12xx_copy_fwlog(wl, &buffer[start_loc], len);
-	clear_ptr = addr_ptr + start_loc + actual_len;
+	clear_ptr = addr_ptr + start_loc + len;
 	if (clear_ptr == buff_end_ptr)
 		clear_ptr = buff_start_ptr;

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:38 +0100

wl18xx_tx_immediate_complete() iterates through the completed transmit
descriptors in a circular fashion, and in doing so uses a modulus
operation that is not a power of two. This leads to inefficient code
generation, which can be easily solved by providing a helper to
increment to the next descriptor. Use this more efficient solution.

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wl18xx/tx.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c
index 55d9b0861c53..beef393853ef 100644
--- a/drivers/net/wireless/ti/wl18xx/tx.c
+++ b/drivers/net/wireless/ti/wl18xx/tx.c
@@ -129,6 +129,14 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
 	wl1271_free_tx_id(wl, id);
 }
 
+static u8 wl18xx_next_tx_idx(u8 idx)
+{
+	if (++idx >= WL18XX_FW_MAX_TX_STATUS_DESC)
+		idx = 0;
+
+	return idx;
+}
+
 void wl18xx_tx_immediate_complete(struct wl1271 *wl)
 {
 	struct wl18xx_fw_status_priv *status_priv =
@@ -161,9 +169,8 @@ void wl18xx_tx_immediate_complete(struct wl1271 *wl)
 		return;
 	}
 
-	for (i = priv->last_fw_rls_idx;
-	     i != status_priv->fw_release_idx;
-	     i = (i + 1) % WL18XX_FW_MAX_TX_STATUS_DESC) {
+	for (i = priv->last_fw_rls_idx; i != status_priv->fw_release_idx;
+	     i = wl18xx_next_tx_idx(i)) {
 		wl18xx_tx_complete_packet(wl,
 			status_priv->released_tx_desc[i]);

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:38 +0100

Referring to status->counters.tx_lnk_free_pkts[i] multiple times leads
to less efficient code. Cache this value in a local variable. This
also makes the code clearer.

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/main.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 492cd7aef44f..b4cc1bd17dd0 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -412,18 +412,18 @@ static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status)

 	for_each_set_bit(i, wl->links_map, wl->num_links) {
-		u8 diff;
+		u8 diff, tx_lnk_free_pkts;
 		lnk = &wl->links[i];
 
 		/* prevent wrap-around in freed-packets counter */
-		diff = (status->counters.tx_lnk_free_pkts[i] -
-		       lnk->prev_freed_pkts) & 0xff;
+		tx_lnk_free_pkts = status->counters.tx_lnk_free_pkts[i];
+		diff = (tx_lnk_free_pkts - lnk->prev_freed_pkts) & 0xff;
 
 		if (diff == 0)
 			continue;
 
 		lnk->allocated_pkts -= diff;
-		lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[i];
+		lnk->prev_freed_pkts = tx_lnk_free_pkts;
 
 		/* accumulate the prev_freed_pkts counter */
 		lnk->total_freed_pkts += diff;

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:38 +0100

wlcore_fw_status() is passed a pointer to the struct wl_fw_status to
decode the status into, which is always wl->fw_status. Rather than
referencing wl->fw_status within wlcore_fw_status(), use the supplied
argument so that we access this member in a consistent manner.

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index b4cc1bd17dd0..54e98f7592d0 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -392,7 +392,7 @@ static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status)
 	if (ret < 0)
 		return ret;
 
-	wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, wl->fw_status);
+	wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, status);
 
 	wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
 		     "drv_rx_counter = %d, tx_results_counter = %d)",

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:38 +0100

Updates for WL18xx firmware 8.9.1.x.x need to know the AP encryption
key type. Store this when a new key is set. Patch extracted from:

https://git.ti.com/cgit/wilink8-wlan/build-utilites/tree/patches/kernel_patches/4.19.38/0023-wlcore-Fixing-PN-drift-on-encrypted-link-after-recov.patch?h=r8.9&id=a2ee50aa5190ed3b334373d6cd09b1bff56ffcf7

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/main.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 54e98f7592d0..f455e7248fa6 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -3537,6 +3537,10 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
 			return ret;
 		}
 
+		/* Store AP encryption key type */
+		if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+			wlvif->encryption_type = key_type;
+
 		/*
 		 * reconfiguring arp response if the unicast (or common)
 		 * encryption key type was changed

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:38 +0100

TI Wl18xx firmware adds a "pn16" field for AES and TKIP keys as per
their patch:

Add support for this, but rather than requiring the field to be
present (which would break existing firmwares), make it optional.

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/cmd.c      |  9 ++++
 drivers/net/wireless/ti/wlcore/main.c     | 89 +++++++++++++++++++++++++++++--
 drivers/net/wireless/ti/wlcore/wlcore_i.h |  4 ++
 3 files changed, 97 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index 92fc2d456c2c..cd8ad0fe59cc 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -332,6 +332,14 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
 			wl->fw_status->counters.tx_lnk_free_pkts[link];
 	wl->links[link].wlvif = wlvif;
 
+	/*
+	 * Take the last sec_pn16 value from the current FW status. On recovery,
+	 * we might not have fw_status yet, and tx_lnk_sec_pn16[] will be NULL.
+	 */
+	if (wl->fw_status->counters.tx_lnk_sec_pn16)
+		wl->links[link].prev_sec_pn16 =
+			le16_to_cpu(wl->fw_status->counters.tx_lnk_sec_pn16[link]);
+
 	/*
 	 * Take saved value for total freed packets from wlvif, in case this is
 	 * recovery/resume
@@ -360,6 +368,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
 
 	wl->links[*hlid].allocated_pkts = 0;
 	wl->links[*hlid].prev_freed_pkts = 0;
+	wl->links[*hlid].prev_sec_pn16 = 0;
 	wl->links[*hlid].ba_bitmap = 0;
 	eth_zero_addr(wl->links[*hlid].addr);
 
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index f455e7248fa6..28d88060430b 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -379,6 +379,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
 
 static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status)
 {
+	struct wl12xx_vif *wlvifsta;
+	struct wl12xx_vif *wlvifap;
 	struct wl12xx_vif *wlvif;
 	u32 old_tx_blk_count = wl->tx_blocks_available;
 	int avail, freed_blocks;
@@ -410,23 +412,100 @@ static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status)
 		wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i];
 	}
 
+	/* Find an authorized STA vif */
+	wlvifsta = NULL;
+	wl12xx_for_each_wlvif_sta(wl, wlvif) {
+		if (wlvif->sta.hlid != WL12XX_INVALID_LINK_ID &&
+		    test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags)) {
+			wlvifsta = wlvif;
+			break;
+		}
+	}
+
+	/* Find a started AP vif */
+	wlvifap = NULL;
+	wl12xx_for_each_wlvif(wl, wlvif) {
+		if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+		    wlvif->inconn_count == 0 &&
+		    test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
+			wlvifap = wlvif;
+			break;
+		}
+	}
 
 	for_each_set_bit(i, wl->links_map, wl->num_links) {
+		u16 diff16, sec_pn16;
 		u8 diff, tx_lnk_free_pkts;
+
 		lnk = &wl->links[i];
 
 		/* prevent wrap-around in freed-packets counter */
 		tx_lnk_free_pkts = status->counters.tx_lnk_free_pkts[i];
 		diff = (tx_lnk_free_pkts - lnk->prev_freed_pkts) & 0xff;
 
-		if (diff == 0)
+		if (diff) {
+			lnk->allocated_pkts -= diff;
+			lnk->prev_freed_pkts = tx_lnk_free_pkts;
+		}
+
+		/* Get the current sec_pn16 value if present */
+		if (status->counters.tx_lnk_sec_pn16)
+			sec_pn16 = __le16_to_cpu(status->counters.tx_lnk_sec_pn16[i]);
+		else
+			sec_pn16 = 0;
+		/* prevent wrap-around in pn16 counter */
+		diff16 = (sec_pn16 - lnk->prev_sec_pn16) & 0xffff;
+
+		/* FIXME: since free_pkts is a 8-bit counter of packets that
+		 * rolls over, it can become zero. If it is zero, then we
+		 * omit processing below. Is that really correct?
+		 */
+		if (tx_lnk_free_pkts <= 0)
 			continue;
 
-		lnk->allocated_pkts -= diff;
-		lnk->prev_freed_pkts = tx_lnk_free_pkts;
+		/* For a station that has an authorized link: */
+		if (wlvifsta && wlvifsta->sta.hlid == i) {
+			if (wlvifsta->encryption_type == KEY_TKIP ||
+			    wlvifsta->encryption_type == KEY_AES) {
+				if (diff16) {
+					lnk->prev_sec_pn16 = sec_pn16;
+					/* accumulate the prev_freed_pkts
+					 * counter according to the PN from
+					 * firmware
+					 */
+					lnk->total_freed_pkts += diff16;
+				}
+			} else {
+				if (diff)
+					/* accumulate the prev_freed_pkts
+					 * counter according to the free packets
+					 * count from firmware
+					 */
+					lnk->total_freed_pkts += diff;
+			}
+		}
 
-		/* accumulate the prev_freed_pkts counter */
-		lnk->total_freed_pkts += diff;
+		/* For an AP that has been started */
+		if (wlvifap && test_bit(i, wlvifap->ap.sta_hlid_map)) {
+			if (wlvifap->encryption_type == KEY_TKIP ||
+			    wlvifap->encryption_type == KEY_AES) {
+				if (diff16) {
+					lnk->prev_sec_pn16 = sec_pn16;
+					/* accumulate the prev_freed_pkts
+					 * counter according to the PN from
+					 * firmware
+					 */
+					lnk->total_freed_pkts += diff16;
+				}
+			} else {
+				if (diff)
+					/* accumulate the prev_freed_pkts
+					 * counter according to the free packets
+					 * count from firmware
+					 */
+					lnk->total_freed_pkts += diff;
+			}
+		}
 	}
 
 	/* prevent wrap-around in total blocks counter */
diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
index 817a8a61cac6..5bdcb341629c 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
@@ -151,6 +151,9 @@ struct wl_fw_status {
 		 */
 		u8 *tx_lnk_free_pkts;
 
+		/* PN16 of last TKIP/AES seq-num per HLID */
+		__le16 *tx_lnk_sec_pn16;
+
 		/* Cumulative counter of released Voice memory blocks */
 		u8 tx_voice_released_blks;
 
@@ -259,6 +262,7 @@ struct wl1271_link {
 	/* accounting for allocated / freed packets in FW */
 	u8 allocated_pkts;
 	u8 prev_freed_pkts;
+	u16 prev_sec_pn16;
 
 	u8 addr[ETH_ALEN];

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:38 +0100

Add the necessary code to read the 8.9.1 firmware status into the
driver private status structure, augmenting the 8.9.0 firmware
status code. New structure layout taken from:

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wl18xx/main.c   | 71 ++++++++++++++++++++++++++++++++-
 drivers/net/wireless/ti/wl18xx/wl18xx.h | 60 ++++++++++++++++++++++++++++
 2 files changed, 129 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 2ccac1cdec01..39d8eebb9b6e 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -1177,8 +1177,49 @@ static int wl18xx_hw_init(struct wl1271 *wl)
 	return ret;
 }
 
-static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
-				     struct wl_fw_status *fw_status)
+static void wl18xx_convert_fw_status_8_9_1(struct wl1271 *wl,
+					   void *raw_fw_status,
+					   struct wl_fw_status *fw_status)
+{
+	struct wl18xx_fw_status_8_9_1 *int_fw_status = raw_fw_status;
+
+	fw_status->intr = le32_to_cpu(int_fw_status->intr);
+	fw_status->fw_rx_counter = int_fw_status->fw_rx_counter;
+	fw_status->drv_rx_counter = int_fw_status->drv_rx_counter;
+	fw_status->tx_results_counter = int_fw_status->tx_results_counter;
+	fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs;
+
+	fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime);
+	fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap);
+	fw_status->link_fast_bitmap =
+			le32_to_cpu(int_fw_status->link_fast_bitmap);
+	fw_status->total_released_blks =
+			le32_to_cpu(int_fw_status->total_released_blks);
+	fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total);
+
+	fw_status->counters.tx_released_pkts =
+			int_fw_status->counters.tx_released_pkts;
+	fw_status->counters.tx_lnk_free_pkts =
+			int_fw_status->counters.tx_lnk_free_pkts;
+	fw_status->counters.tx_lnk_sec_pn16 =
+			int_fw_status->counters.tx_lnk_sec_pn16;
+	fw_status->counters.tx_voice_released_blks =
+			int_fw_status->counters.tx_voice_released_blks;
+	fw_status->counters.tx_last_rate =
+			int_fw_status->counters.tx_last_rate;
+	fw_status->counters.tx_last_rate_mbps =
+			int_fw_status->counters.tx_last_rate_mbps;
+	fw_status->counters.hlid =
+			int_fw_status->counters.hlid;
+
+	fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr);
+
+	fw_status->priv = &int_fw_status->priv;
+}
+
+static void wl18xx_convert_fw_status_8_9_0(struct wl1271 *wl,
+					   void *raw_fw_status,
+					   struct wl_fw_status *fw_status)
 {
 	struct wl18xx_fw_status *int_fw_status = raw_fw_status;
 
@@ -1214,6 +1255,15 @@ static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
 	fw_status->priv = &int_fw_status->priv;
 }
 
+static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+				     struct wl_fw_status *fw_status)
+{
+	if (wl->chip.fw_ver[FW_VER_MAJOR] == 0)
+		wl18xx_convert_fw_status_8_9_0(wl, raw_fw_status, fw_status);
+	else
+		wl18xx_convert_fw_status_8_9_1(wl, raw_fw_status, fw_status);
+}
+
 static void wl18xx_set_tx_desc_csum(struct wl1271 *wl,
 				    struct wl1271_tx_hw_descr *desc,
 				    struct sk_buff *skb)
@@ -1515,12 +1565,29 @@ static int wl18xx_handle_static_data(struct wl1271 *wl,
 {
 	struct wl18xx_static_data_priv *static_data_priv =
 		(struct wl18xx_static_data_priv *) static_data->priv;
+	size_t fw_status_len;
 
 	strscpy(wl->chip.phy_fw_ver_str, static_data_priv->phy_version,
 		sizeof(wl->chip.phy_fw_ver_str));
 
 	wl1271_info("PHY firmware version: %s", static_data_priv->phy_version);
 
+	/* Adjust the firmware status size according to the firmware version */
+	if (wl->chip.fw_ver[FW_VER_MAJOR] == 0)
+		fw_status_len = sizeof(struct wl18xx_fw_status);
+	else
+		fw_status_len = sizeof(struct wl18xx_fw_status_8_9_1);
+
+	if (wl->fw_status_len != fw_status_len) {
+		void *new_status = krealloc(wl->raw_fw_status, fw_status_len,
+					    GFP_KERNEL | __GFP_ZERO);
+		if (!new_status)
+			return -ENOMEM;
+
+		wl->raw_fw_status = new_status;
+		wl->fw_status_len = fw_status_len;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h
index b642e0c437bb..7fed96d71b27 100644
--- a/drivers/net/wireless/ti/wl18xx/wl18xx.h
+++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h
@@ -155,6 +155,66 @@ struct wl18xx_fw_status {
 	struct wl18xx_fw_status_priv priv;
 } __packed;
 
+struct wl18xx_fw_packet_counters_8_9_1 {
+	/* Cumulative counter of released packets per AC */
+	u8 tx_released_pkts[NUM_TX_QUEUES];
+
+	/* Cumulative counter of freed packets per HLID */
+	u8 tx_lnk_free_pkts[WL18XX_MAX_LINKS];
+
+	/* PN16 of last TKIP/AES seq-num per HLID */
+	__le16 tx_lnk_sec_pn16[WL18XX_MAX_LINKS];
+
+	/* Cumulative counter of released Voice memory blocks */
+	u8 tx_voice_released_blks;
+
+	/* Tx rate of the last transmitted packet */
+	u8 tx_last_rate;
+
+	/* Tx rate or Tx rate estimate pre-calculated by fw in mbps units */
+	u8 tx_last_rate_mbps;
+
+	/* hlid for which the rates were reported */
+	u8 hlid;
+} __packed;
+
+/* FW status registers */
+struct wl18xx_fw_status_8_9_1 {
+	__le32 intr;
+	u8  fw_rx_counter;
+	u8  drv_rx_counter;
+	u8  reserved;
+	u8  tx_results_counter;
+	__le32 rx_pkt_descs[WL18XX_NUM_RX_DESCRIPTORS];
+
+	__le32 fw_localtime;
+
+	/*
+	 * A bitmap (where each bit represents a single HLID)
+	 * to indicate if the station is in PS mode.
+	 */
+	__le32 link_ps_bitmap;
+
+	/*
+	 * A bitmap (where each bit represents a single HLID) to indicate
+	 * if the station is in Fast mode
+	 */
+	__le32 link_fast_bitmap;
+
+	/* Cumulative counter of total released mem blocks since FW-reset */
+	__le32 total_released_blks;
+
+	/* Size (in Memory Blocks) of TX pool */
+	__le32 tx_total;
+
+	struct wl18xx_fw_packet_counters_8_9_1 counters;
+
+	__le32 log_start_addr;
+
+	/* Private status to be used by the lower drivers */
+	struct wl18xx_fw_status_priv priv;
+} __packed;
+
 #define WL18XX_PHY_VERSION_MAX_LEN 20
 
 struct wl18xx_static_data_priv {

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:38 +0100

wlcore firmware versions are structured thusly:

	chip.if-type.major.sub-type.minor
e.g.	  8    9       0       0     58

With WL18xx ignoring the major firmware version, looking for a
firmware version that conforms to:

	chip >= 8
	if-type >= 9
	major (don't care)
	sub-type (don't care)
	minor >= 58

Each test is satisfied if the value read from the firmware is greater
than the minimum, but if it is equal (or we don't care about the
field), then the next field is checked.

Thus it doesn't recognise 8.9.1.x.0 as being newer than 8.9.0.x.58
since the major and sub-type numbers are "don't care" and the minor
needs to be greater or equal to 58.

We need to change the major version from "ignore" to "0" for this later
firmware to be correctly detected, and allow the dual-firmware version
support to work.

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wl18xx/wl18xx.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h
index 7fed96d71b27..de6c671c4be6 100644
--- a/drivers/net/wireless/ti/wl18xx/wl18xx.h
+++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h
@@ -13,7 +13,7 @@
 /* minimum FW required for driver */
 #define WL18XX_CHIP_VER		8
 #define WL18XX_IFTYPE_VER	9
-#define WL18XX_MAJOR_VER	WLCORE_FW_VER_IGNORE
+#define WL18XX_MAJOR_VER	0
 #define WL18XX_SUBTYPE_VER	WLCORE_FW_VER_IGNORE
 #define WL18XX_MINOR_VER	58

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:38 +0100

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/main.c | 10 ++++++++
 drivers/net/wireless/ti/wlcore/tx.c   | 47 +++++++++++++++++++++++++++++++++++
 drivers/net/wireless/ti/wlcore/tx.h   |  1 +
 3 files changed, 58 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 28d88060430b..267c2debc039 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -5656,6 +5656,15 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	wl1271_tx_flush(wl);
 }
 
+static void wlcore_op_flush_sta(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				struct ieee80211_sta *sta)
+{
+	struct wl1271 *wl = hw->priv;
+
+	wl1271_tx_flush_sta(wl, sta);
+}
+
 static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
 				       struct ieee80211_vif *vif,
 				       struct ieee80211_channel *chan,
@@ -6044,6 +6053,7 @@ static const struct ieee80211_ops wl1271_ops = {
 	.channel_switch = wl12xx_op_channel_switch,
 	.channel_switch_beacon = wlcore_op_channel_switch_beacon,
 	.flush = wlcore_op_flush,
+	.flush_sta = wlcore_op_flush_sta,
 	.remain_on_channel = wlcore_op_remain_on_channel,
 	.cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
 	.add_chanctx = wlcore_op_add_chanctx,
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 464587d16ab2..9d6b5115db09 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -1181,6 +1181,53 @@ void wl1271_tx_flush(struct wl1271 *wl)
 }
 EXPORT_SYMBOL_GPL(wl1271_tx_flush);
 
+void wl1271_tx_flush_sta(struct wl1271 *wl, struct ieee80211_sta *sta)
+{
+	u8 hlid = wl1271_station(sta)->hlid;
+	bool frames_queued = false;
+	unsigned long timeout;
+	int i;
+
+	mutex_lock(&wl->mutex);
+	/* Flush queued packets for the link associated with this station */
+	wl1271_tx_reset_link_queues(wl, hlid);
+
+	/* Wait for any frames to be transmitted */
+	timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT);
+	while (!time_after(jiffies, timeout)) {
+		frames_queued = false;
+
+		/* Are there any frames for this hlid? */
+		for (i = 0; i < WLCORE_MAX_TX_DESCRIPTORS; i++) {
+			struct wl1271_tx_hw_descr *desc;
+			struct sk_buff *skb;
+
+			skb = wl->tx_frames[i];
+			if (!skb)
+				continue;
+
+			desc = (struct wl1271_tx_hw_descr *)skb->data;
+			if (desc->hlid == hlid) {
+				frames_queued = true;
+				break;
+			}
+		}
+
+		if (!frames_queued)
+			break;
+
+		mutex_unlock(&wl->mutex);
+		msleep(20);
+		mutex_lock(&wl->mutex);
+	}
+
+	if (frames_queued)
+		wl1271_warning("Unable to flush all frames for station %pM\n",
+			       sta->addr);
+
+	mutex_unlock(&wl->mutex);
+}
+
 u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
 {
 	if (WARN_ON(!rate_set))
diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h
index 9069bcf87158..e113ecca1b1d 100644
--- a/drivers/net/wireless/ti/wlcore/tx.h
+++ b/drivers/net/wireless/ti/wlcore/tx.h
@@ -232,6 +232,7 @@ int wlcore_tx_complete(struct wl1271 *wl);
 void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl12xx_tx_reset(struct wl1271 *wl);
 void wl1271_tx_flush(struct wl1271 *wl);
+void wl1271_tx_flush_sta(struct wl1271 *wl, struct ieee80211_sta *sta);
 u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum nl80211_band band);
 u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
 				enum nl80211_band rate_band);

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:38 +0100

Add support for the AES-CMAC cipher to TI Wireless. Based on TI's
patch:

https://git.ti.com/cgit/wilink8-wlan/build-utilites/tree/patches/kernel_patches/4.19.38/0007-Adding-support-to-IGTK-key-AES-CMAC128-in-the-wlcore.patch?h=r8.9&id=a2ee50aa5190ed3b334373d6cd09b1bff56ffcf7

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/cmd.h  | 1 +
 drivers/net/wireless/ti/wlcore/main.c | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index 4c2f2608ef3b..ab6121b92b00 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -456,6 +456,7 @@ enum wl1271_cmd_key_type {
 	KEY_TKIP = 2,
 	KEY_AES  = 3,
 	KEY_GEM  = 4,
+	KEY_IGTK = 5,
 };
 
 struct wl1271_cmd_set_keys {
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 267c2debc039..f714238abcf3 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -3597,6 +3597,9 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
 	case WL1271_CIPHER_SUITE_GEM:
 		key_type = KEY_GEM;
 		break;
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+		key_type = KEY_IGTK;
+		break;
 	default:
 		wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
 
@@ -6238,6 +6241,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
 		WLAN_CIPHER_SUITE_TKIP,
 		WLAN_CIPHER_SUITE_CCMP,
 		WL1271_CIPHER_SUITE_GEM,
+		WLAN_CIPHER_SUITE_AES_CMAC,
 	};
 
 	/* The tx descriptor buffer */

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:39 +0100

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/debugfs.c | 1 +
 drivers/net/wireless/ti/wlcore/main.c    | 1 +
 drivers/net/wireless/ti/wlcore/wlcore.h  | 1 +
 3 files changed, 3 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index eb3d3f0e0b4d..cc5a5411d781 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -457,6 +457,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
 		DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel,
 					   wlvif->p2p ? "P2P-GO" : "AP");
 
+	DRIVER_STATE_PRINT_INT(tx_blocks_total);
 	DRIVER_STATE_PRINT_INT(tx_blocks_available);
 	DRIVER_STATE_PRINT_INT(tx_allocated_blocks);
 	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index f714238abcf3..59f9b147e1ea 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -532,6 +532,7 @@ static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status)
 			cancel_delayed_work(&wl->tx_watchdog_work);
 	}
 
+	wl->tx_blocks_total = status->tx_total;
 	avail = status->tx_total - wl->tx_allocated_blocks;
 
 	/*
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 1f8511bf9bb3..97ade837c343 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -242,6 +242,7 @@ struct wl1271 {
 	struct wl1271_acx_mem_map *target_mem_map;
 
 	/* Accounting for allocated / available TX blocks on HW */
+	u32 tx_blocks_total;
 	u32 tx_blocks_freed;
 	u32 tx_blocks_available;
 	u32 tx_allocated_blocks;

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:39 +0100

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/debugfs.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index cc5a5411d781..27ac27364fe0 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -493,6 +493,18 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
 	DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
 	DRIVER_STATE_PRINT_INT(recovery_count);
 
+	wl12xx_for_each_wlvif_ap(wl, wlvif) {
+		int hlid;
+		for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, wl->num_links) {
+			res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,
+					 "links[%d].allocated_pkts = %u\n",
+					 hlid, wl->links[hlid].allocated_pkts);
+			res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,
+					 "links[%d].addr = %pM\n",
+					 hlid, wl->links[hlid].addr);
+		}
+	}
+
 #undef DRIVER_STATE_PRINT_INT
 #undef DRIVER_STATE_PRINT_LONG
 #undef DRIVER_STATE_PRINT_HEX

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:39 +0100

Add information about the frames queued to firmware into the debugfs
driver state output.

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/debugfs.c | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index 27ac27364fe0..9432aeb4902e 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -423,7 +423,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
 	char *buf;
 	struct wl12xx_vif *wlvif;
 
-#define DRIVER_STATE_BUF_LEN 1024
+#define DRIVER_STATE_BUF_LEN 2048
 
 	buf = kmalloc(DRIVER_STATE_BUF_LEN, GFP_KERNEL);
 	if (!buf)
@@ -505,6 +505,33 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
 		}
 	}
 
+	if (wl->fw_status) {
+		DRIVER_STATE_PRINT(fw_status->fw_localtime, "%u");
+	}
+
+	{
+		struct wl1271_tx_hw_descr *tx_desc;
+		struct sk_buff *skb;
+		u32 start, end;
+		u16 lt;
+		int id;
+
+		for (id = 0; id < WLCORE_MAX_TX_DESCRIPTORS; id++) {
+			skb = wl->tx_frames[id];
+			if (!skb)
+				continue;
+
+			tx_desc = (struct wl1271_tx_hw_descr *)skb->data;
+			start = le32_to_cpu(tx_desc->start_time);
+			lt = le16_to_cpu(tx_desc->life_time);
+			end = start + 1024 * lt;
+
+			res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,
+					 "tx_frames[%2u] hlid = %2u st %10u lt %u et %10u\n",
+					 id, tx_desc->hlid, start, lt, end);
+		}
+	}
+
 #undef DRIVER_STATE_PRINT_INT
 #undef DRIVER_STATE_PRINT_LONG
 #undef DRIVER_STATE_PRINT_HEX

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:39 +0100

From: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/cmd.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index cd8ad0fe59cc..d65bb63e1a7d 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -1575,6 +1575,9 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 		cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates,
 							wlvif->band));
 
+	pr_info("wl18xx: add_peer: %pM hlid=%u rates=0x%x queues=0x%x\n",
+		sta->addr, hlid, cmd->supported_rates, sta->uapsd_queues);
+
 	wl1271_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x",
 		     cmd->supported_rates, sta->uapsd_queues);

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:39 +0100

Dump some information about the firmware queued packets that have
not flushed.

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/debugfs.c | 11 +++++++---
 drivers/net/wireless/ti/wlcore/tx.c      | 36 +++++++++++++++++++++++++++++---
 2 files changed, 41 insertions(+), 6 deletions(-)

index 9432aeb4902e..effd81e9f6cc 100644
 
-#define DRIVER_STATE_BUF_LEN 2048
+#define DRIVER_STATE_BUF_LEN 4096
 
 	buf = kmalloc(DRIVER_STATE_BUF_LEN, GFP_KERNEL);
 	if (!buf)
@@ -511,6 +511,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
 
 	{
 		struct wl1271_tx_hw_descr *tx_desc;
+		struct ieee80211_hdr *hdr;
 		struct sk_buff *skb;
 		u32 start, end;
 		u16 lt;
@@ -522,13 +523,17 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
 				continue;
 
 			tx_desc = (struct wl1271_tx_hw_descr *)skb->data;
+			hdr = (void *)(tx_desc + 1);
 			start = le32_to_cpu(tx_desc->start_time);
 			lt = le16_to_cpu(tx_desc->life_time);
 			end = start + 1024 * lt;
 
 			res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,
-					 "tx_frames[%2u] hlid = %2u st %10u lt %u et %10u\n",
-					 id, tx_desc->hlid, start, lt, end);
+					 "tx_frames[%2u] hlid %2u st %10u lt %u et %10u SA %pM DA %pM FC %04x\n",
+					 id, tx_desc->hlid, start, lt, end,
+					 ieee80211_get_SA(hdr),
+					 ieee80211_get_DA(hdr),
+					 hdr->frame_control);
 		}
 	}
 
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 9d6b5115db09..3a5b3fad028f 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -1221,9 +1221,39 @@ void wl1271_tx_flush_sta(struct wl1271 *wl, struct ieee80211_sta *sta)
 		mutex_lock(&wl->mutex);
 	}
 
-	if (frames_queued)
-		wl1271_warning("Unable to flush all frames for station %pM\n",
-			       sta->addr);
+	if (frames_queued) {
+		dev_warn(wl->dev,
+			 "Unable to flush all frames for station %pM hlid %u\n",
+			 sta->addr, hlid);
+		dev_warn(wl->dev, "FW time: %u", wl->fw_status->fw_localtime);
+
+		for (i = 0; i < WLCORE_MAX_TX_DESCRIPTORS; i++) {
+			struct wl1271_tx_hw_descr *desc;
+			struct ieee80211_hdr *hdr;
+			struct sk_buff *skb;
+			u32 start;
+			u16 lt;
+
+			skb = wl->tx_frames[i];
+			if (!skb)
+				continue;
+
+			desc = (struct wl1271_tx_hw_descr *)skb->data;
+			if (desc->hlid != hlid)
+				continue;
+
+			/* The IEEE 802.11 header follows the TX descriptor */
+			hdr = (void *)(desc + 1);
+
+			start = le32_to_cpu(desc->start_time);
+			lt = le16_to_cpu(desc->life_time);
+
+			dev_warn(wl->dev,
+				 " Frame %d: expires %u MAC %pM FC 0x%04x\n",
+				 i, start + 1024 * lt,
+				 hdr->addr1, hdr->frame_control);
+		}
+	}
 
 	mutex_unlock(&wl->mutex);
 }

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:39 +0100

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/event.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index 6c3a8ea9613e..4190ce26e587 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -72,7 +72,8 @@ int wlcore_event_fw_logger(struct wl1271 *wl)
 	buff_read_ptr = le32_to_cpu(fw_log.buff_read_ptr);
 	if (buff_read_ptr < buff_start_ptr ||
 	    buff_read_ptr >= buff_end_ptr) {
-		wl1271_error("buffer read pointer out of bounds: %x not in (%x-%x)\n",
+		actual_len = 0;
+		wl1271_error("buffer read pointer out of bounds: %x not in (%x-%x)",
 			     buff_read_ptr, buff_start_ptr, buff_end_ptr);
 		goto free_out;
 	}

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:39 +0100

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/cmd.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index d65bb63e1a7d..281090e005e4 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -1609,6 +1609,8 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 		goto out;
 	}
 
+	pr_info("wl18xx: del_peer: hlid=%u\n", hlid);
+
 	cmd->hlid = hlid;
 	/* We never send a deauth, mac80211 is in charge of this */
 	cmd->reason_opcode = 0;

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:39 +0100

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/acx.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
index e820fe694121..9667d0ae533e 100644
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -1434,7 +1434,8 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
 
 	/* sometimes we can't start the session */
 	if (ret == CMD_STATUS_NO_RX_BA_SESSION) {
-		wl1271_warning("no fw rx ba on tid %d", tid_index);
+		wl1271_warning("no fw rx ba on tid %u hlid %u", tid_index,
+			       peer_hlid);
 		ret = -EBUSY;
 		goto out;
 	}

---

From: Russell King <[email-masked]>
Date: Fri, 18 Oct 2024 11:09:39 +0100

When a peer is removed, it may have been moved out of range with open
BA sessions on the AP. Each open BA session consumes firmware resources
and if allowed to increase, will eventually cause the firmware to run
BA sessions. Not only does this impact firmware, but also the driver's
state tracking as well.

Fix this by ensuring that all BA sessions associated with a STA are
properly removed from the firmware and driver state just before the STA
is removed.

Signed-off-by: Russell King (Oracle) <[email-masked]>
---
 drivers/net/wireless/ti/wlcore/main.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 59f9b147e1ea..25820e6228a5 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -5144,6 +5144,25 @@ static int wl12xx_sta_remove(struct wl1271 *wl,
 	if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
 		return -EINVAL;
 
+	if (wl->links[id].ba_bitmap) {
+		u8 ba = wl->links[id].ba_bitmap;
+		u8 tid;
+
+		pr_info("WARNING: removing peer with active ba 0x%08x, count %u\n",
+			ba, wl->ba_rx_session_count);
+
+		for (tid = 0; tid < 8; tid++)
+			if (ba & BIT(tid))
+				wl12xx_acx_set_ba_receiver_session(wl, tid, 0,
+								   false, id, 0);
+		if (wl->ba_rx_session_count >= hweight8(ba))
+			wl->ba_rx_session_count -= hweight8(ba);
+		else
+			wl->ba_rx_session_count = 0;
+
+		wl->links[id].ba_bitmap = 0;
+	}
+
 	ret = wl12xx_cmd_remove_peer(wl, wlvif, wl_sta->hlid);
 	if (ret < 0)
 		return ret;