public inbox for linux-wireless@vger.kernel.org
 help / color / mirror / Atom feed
From: Lachlan Hodges <lachlan.hodges@morsemicro.com>
To: johannes@sipsolutions.net,
	Lachlan Hodges <lachlan.hodges@morsemicro.com>,
	Dan Callaghan <dan.callaghan@morsemicro.com>,
	Arien Judge <arien.judge@morsemicro.com>
Cc: ayman.grais@morsemicro.com, linux-wireless@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH wireless-next 21/35] wifi: mm81x: add rc.c
Date: Fri, 27 Feb 2026 15:10:31 +1100	[thread overview]
Message-ID: <20260227041108.66508-22-lachlan.hodges@morsemicro.com> (raw)
In-Reply-To: <20260227041108.66508-1-lachlan.hodges@morsemicro.com>

(Patches split per file for review, see cover letter for more
information)

Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
 drivers/net/wireless/morsemicro/mm81x/rc.c | 559 +++++++++++++++++++++
 1 file changed, 559 insertions(+)
 create mode 100644 drivers/net/wireless/morsemicro/mm81x/rc.c

diff --git a/drivers/net/wireless/morsemicro/mm81x/rc.c b/drivers/net/wireless/morsemicro/mm81x/rc.c
new file mode 100644
index 000000000000..83954c3e5a4e
--- /dev/null
+++ b/drivers/net/wireless/morsemicro/mm81x/rc.c
@@ -0,0 +1,559 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2026 Morse Micro
+ */
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include "core.h"
+#include "mac.h"
+#include "bus.h"
+#include "debug.h"
+#include "rc.h"
+
+#define MM81X_RC_BW_TO_MMRC_BW(X)    \
+	(((X) == 1) ? MMRC_BW_1MHZ : \
+	 ((X) == 2) ? MMRC_BW_2MHZ : \
+	 ((X) == 4) ? MMRC_BW_4MHZ : \
+	 ((X) == 8) ? MMRC_BW_8MHZ : \
+		      MMRC_BW_2MHZ)
+
+static void mm81x_rc_work(struct work_struct *work)
+{
+	struct mm81x_rc *mrc = container_of(work, struct mm81x_rc, work);
+	struct list_head *pos;
+
+	spin_lock_bh(&mrc->lock);
+
+	list_for_each(pos, &mrc->stas) {
+		struct mm81x_rc_sta *mrc_sta =
+			container_of(pos, struct mm81x_rc_sta, list);
+		unsigned long now = jiffies;
+
+		mrc_sta->last_update = now;
+
+		mmrc_update(mrc_sta->tb);
+	}
+
+	spin_unlock_bh(&mrc->lock);
+
+	mod_timer(&mrc->timer, jiffies + msecs_to_jiffies(100));
+}
+
+static void mm81x_rc_timer(struct timer_list *t)
+{
+	struct mm81x_rc *mrc = timer_container_of(mrc, t, timer);
+	struct mm81x *mm = mrc->mm;
+
+	queue_work(mm->net_wq, &mm->mrc.work);
+}
+
+void mm81x_rc_init(struct mm81x *mm)
+{
+	INIT_LIST_HEAD(&mm->mrc.stas);
+	spin_lock_init(&mm->mrc.lock);
+
+	INIT_WORK(&mm->mrc.work, mm81x_rc_work);
+	timer_setup(&mm->mrc.timer, mm81x_rc_timer, 0);
+
+	mm->mrc.mm = mm;
+	mod_timer(&mm->mrc.timer, jiffies + msecs_to_jiffies(100));
+}
+
+void mm81x_rc_deinit(struct mm81x *mm)
+{
+	cancel_work_sync(&mm->mrc.work);
+	timer_delete_sync_try(&mm->mrc.timer);
+}
+
+static void mm81x_rc_sta_config_guard_per_bw(struct ieee80211_sta *sta,
+					     struct mmrc_sta_capabilities *caps)
+{
+	caps->guard = MMRC_MASK(MMRC_GUARD_LONG);
+
+	if (caps->bandwidth & MMRC_MASK(MMRC_BW_1MHZ)) {
+		caps->sgi_per_bw |= SGI_PER_BW(MMRC_BW_1MHZ);
+		caps->guard |= MMRC_MASK(MMRC_GUARD_SHORT);
+	}
+
+	if (caps->bandwidth & MMRC_MASK(MMRC_BW_2MHZ)) {
+		caps->sgi_per_bw |= SGI_PER_BW(MMRC_BW_2MHZ);
+		caps->guard |= MMRC_MASK(MMRC_GUARD_SHORT);
+	}
+
+	if (caps->bandwidth & MMRC_MASK(MMRC_BW_4MHZ)) {
+		caps->sgi_per_bw |= SGI_PER_BW(MMRC_BW_4MHZ);
+		caps->guard |= MMRC_MASK(MMRC_GUARD_SHORT);
+	}
+
+	if (caps->bandwidth & MMRC_MASK(MMRC_BW_8MHZ)) {
+		caps->sgi_per_bw |= SGI_PER_BW(MMRC_BW_8MHZ);
+		caps->guard |= MMRC_MASK(MMRC_GUARD_SHORT);
+	}
+}
+
+static void mm81x_rc_sta_add_s1g_sta_caps(struct mm81x *mm,
+					  struct mmrc_sta_capabilities *caps,
+					  struct ieee80211_sta_s1g_cap *s1g_cap)
+{
+	int nss_idx = 0;
+	u8 rx_mcs = s1g_cap->nss_mcs[0] & 0x3; /* 1SS */
+	u8 tx_mcs = (s1g_cap->nss_mcs[2] >> 1) & 0x3; /* 1SS */
+	u8 mcs = min(rx_mcs, tx_mcs);
+
+	switch (mcs) {
+	case IEEE80211_VHT_MCS_SUPPORT_0_9: /* VHT 9 ->  S1G 9 */
+		caps->rates |= MMRC_MASK(MMRC_MCS9) | MMRC_MASK(MMRC_MCS8);
+		fallthrough;
+	case IEEE80211_VHT_MCS_SUPPORT_0_8: /* VHT 8 -> S1G 7 */
+		caps->rates |= MMRC_MASK(MMRC_MCS7) | MMRC_MASK(MMRC_MCS6) |
+			       MMRC_MASK(MMRC_MCS5) | MMRC_MASK(MMRC_MCS4) |
+			       MMRC_MASK(MMRC_MCS3);
+		fallthrough;
+	case IEEE80211_VHT_MCS_SUPPORT_0_7: /* VHT 7 -> S1G 2 */
+		caps->rates |= MMRC_MASK(MMRC_MCS2) | MMRC_MASK(MMRC_MCS1) |
+			       MMRC_MASK(MMRC_MCS0) | MMRC_MASK(MMRC_MCS10);
+		caps->spatial_streams |= (MMRC_MASK(nss_idx) & 0x0F);
+		break;
+
+	default:
+		mm81x_warn(mm, "Invalid MCS encoding 0x%02x for stream %d", mcs,
+			   nss_idx);
+	}
+}
+
+int mm81x_rc_sta_add(struct mm81x *mm, struct ieee80211_vif *vif,
+		     struct ieee80211_sta *sta)
+{
+	struct ieee80211_sta_s1g_cap *s1g_cap = &sta->deflink.s1g_cap;
+	struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
+	struct mmrc_sta_capabilities caps;
+	int oper_bw_mhz = cfg80211_chandef_get_width(&mm->chandef);
+	size_t table_mem_size;
+	struct mmrc_table *tb;
+
+	memset(&caps, 0, sizeof(caps));
+
+	mm81x_rc_sta_add_s1g_sta_caps(mm, &caps, s1g_cap);
+
+	/* Configure STA for support up to 8MHZ */
+	while (oper_bw_mhz > 0) {
+		caps.bandwidth |=
+			MMRC_MASK(MM81X_RC_BW_TO_MMRC_BW(oper_bw_mhz));
+		oper_bw_mhz >>= 1;
+	}
+
+	/* Configure STA for short and long guard */
+	mm81x_rc_sta_config_guard_per_bw(sta, &caps);
+
+	/* Set max rates */
+	if (mm->hw->max_rates > 0 && mm->hw->max_rates < IEEE80211_TX_MAX_RATES)
+		caps.max_rates = mm->hw->max_rates;
+	else
+		caps.max_rates = IEEE80211_TX_MAX_RATES;
+
+	/* Set max reties */
+	if (mm->hw->max_rate_tries >= MMRC_MIN_CHAIN_ATTEMPTS &&
+	    mm->hw->max_rate_tries < MMRC_MAX_CHAIN_ATTEMPTS)
+		caps.max_retries = mm->hw->max_rate_tries;
+	else
+		caps.max_retries = MMRC_MAX_CHAIN_ATTEMPTS;
+
+	WARN_ON(msta->rc.tb);
+	table_mem_size = mmrc_memory_required_for_caps(&caps);
+	tb = kzalloc(table_mem_size, GFP_KERNEL);
+	if (!tb)
+		return -ENOMEM;
+
+	/* Initialise the STA rate control table */
+	mmrc_sta_init(tb, &caps, msta->avg_rssi);
+
+	spin_lock_bh(&mm->mrc.lock);
+	kfree(msta->rc.tb);
+	msta->rc.tb = tb;
+	list_add(&msta->rc.list, &mm->mrc.stas);
+	msta->rc.last_update = jiffies;
+	spin_unlock_bh(&mm->mrc.lock);
+
+	return 0;
+}
+
+static void mm81x_rc_reinit_sta_iter(void *data, struct ieee80211_sta *sta)
+{
+	struct ieee80211_vif *vif = (struct ieee80211_vif *)data;
+	struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
+	struct mm81x_vif *mm_vif = ieee80211_vif_to_mm_vif(vif);
+	struct mm81x *mm = mm81x_vif_to_mm(mm_vif);
+	int oper_bw_mhz = cfg80211_chandef_get_width(&mm->chandef);
+
+	if (!msta || msta->vif != vif)
+		return;
+
+	mm81x_dbg(mm, MM81X_DBG_ANY,
+		  "Reinitialize sta %pM with new op_bw=%d, ts=%ld", sta->addr,
+		  oper_bw_mhz, jiffies);
+
+	mm81x_rc_sta_remove(mm, sta);
+	mm81x_rc_sta_add(mm, vif, sta);
+}
+
+void mm81x_rc_reinit_stas(struct mm81x *mm, struct ieee80211_vif *vif)
+{
+	ieee80211_iterate_stations_atomic(mm->hw, mm81x_rc_reinit_sta_iter,
+					  vif);
+}
+
+bool _mm81x_rc_set_fixed_rate(struct mm81x *mm, struct ieee80211_sta *sta,
+			      int mcs, int bw, int ss, int guard,
+			      const char *caller)
+{
+	struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
+	struct list_head *pos;
+	struct mmrc_rate fixed_rate;
+	bool ret_val = true;
+
+	fixed_rate.rate = mcs;
+	fixed_rate.bw = bw;
+	/*
+	 * Code spatial streams is zero based while user starts at 1, like the
+	 * real spatial streams.
+	 */
+	fixed_rate.ss = (ss - 1);
+	fixed_rate.guard = guard;
+
+	spin_lock_bh(&mm->mrc.lock);
+	list_for_each(pos, &mm->mrc.stas) {
+		struct mm81x_rc_sta *mrc_sta =
+			list_entry(pos, struct mm81x_rc_sta, list);
+
+		if (&msta->rc == mrc_sta) {
+			ret_val = mmrc_set_fixed_rate(msta->rc.tb, fixed_rate);
+			break;
+		}
+	}
+	spin_unlock_bh(&mm->mrc.lock);
+
+	if (!ret_val)
+		mm81x_err(mm, "failed, caller %s ss %d bw %d mcs %d guard %d",
+			  caller, ss, bw, mcs, guard);
+
+	return ret_val;
+}
+
+void mm81x_rc_sta_remove(struct mm81x *mm, struct ieee80211_sta *sta)
+{
+	struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
+
+	spin_lock_bh(&mm->mrc.lock);
+	if (msta->rc.tb) {
+		list_del_init(&msta->rc.list);
+		kfree(msta->rc.tb);
+		msta->rc.tb = NULL;
+	}
+	spin_unlock_bh(&mm->mrc.lock);
+}
+
+static void mm81x_rc_sta_fill_basic_rates(struct mm81x_skb_tx_info *tx_info,
+					  struct ieee80211_tx_info *info,
+					  int tx_bw)
+{
+	int i;
+	enum dot11_bandwidth bw_idx = mm81x_ratecode_bw_mhz_to_bw_index(tx_bw);
+	enum mm81x_rate_preamble pream = MM81X_RATE_PREAMBLE_S1G_SHORT;
+
+	mm81x_ratecode_mcs_index_set(&tx_info->rates[0].mm81x_ratecode, 0);
+	mm81x_ratecode_nss_index_set(&tx_info->rates[0].mm81x_ratecode,
+				     NSS_TO_NSS_IDX(1));
+	mm81x_ratecode_bw_index_set(&tx_info->rates[0].mm81x_ratecode, bw_idx);
+	if (bw_idx == DOT11_BANDWIDTH_1MHZ)
+		pream = MM81X_RATE_PREAMBLE_S1G_1M;
+	mm81x_ratecode_preamble_set(&tx_info->rates[0].mm81x_ratecode, pream);
+	tx_info->rates[0].count = 4;
+
+	for (i = 1; i < IEEE80211_TX_MAX_RATES; i++)
+		tx_info->rates[i].count = 0;
+
+	info->control.rates[0].idx = 0;
+	info->control.rates[0].count = tx_info->rates[0].count;
+	info->control.rates[0].flags = 0;
+	info->control.rates[1].idx = -1;
+}
+
+static int mm81x_rc_sta_get_rates(struct mm81x *mm, struct mm81x_sta *msta,
+				  struct mmrc_rate_table *rates, size_t size)
+{
+	int ret = -ENOENT;
+	struct list_head *pos;
+
+	spin_lock_bh(&mm->mrc.lock);
+	list_for_each(pos, &mm->mrc.stas) {
+		struct mm81x_rc_sta *mrc_sta =
+			list_entry(pos, struct mm81x_rc_sta, list);
+
+		if (&msta->rc == mrc_sta) {
+			ret = 0;
+			mmrc_get_rates(msta->rc.tb, rates, size);
+			break;
+		}
+	}
+	spin_unlock_bh(&mm->mrc.lock);
+
+	return ret;
+}
+
+static bool mm81x_rc_use_basic_rates(struct ieee80211_sta *sta,
+				     struct sk_buff *skb,
+				     struct ieee80211_hdr *hdr)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+	if (!sta)
+		return true;
+
+	if (ieee80211_is_qos_nullfunc(hdr->frame_control) ||
+	    ieee80211_is_nullfunc(hdr->frame_control))
+		return true;
+
+	if (!ieee80211_is_data_qos(hdr->frame_control))
+		return true;
+
+	/* Use basic rates for EAPOL exchanges or when instructed */
+	if (unlikely((skb->protocol == cpu_to_be16(ETH_P_PAE) ||
+		      info->flags & IEEE80211_TX_CTL_USE_MINRATE)))
+		return true;
+
+	return false;
+}
+
+void mm81x_rc_sta_fill_tx_rates(struct mm81x *mm,
+				struct mm81x_skb_tx_info *tx_info,
+				struct sk_buff *skb, struct ieee80211_sta *sta,
+				int tx_bw, bool rts_allowed)
+{
+	int ret, i;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct mm81x_sta *msta;
+	struct mmrc_rate_table rates;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+	BUILD_BUG_ON((MMRC_BW_1MHZ != (enum mmrc_bw)DOT11_BANDWIDTH_1MHZ ||
+		      MMRC_BW_2MHZ != (enum mmrc_bw)DOT11_BANDWIDTH_2MHZ ||
+		      MMRC_BW_4MHZ != (enum mmrc_bw)DOT11_BANDWIDTH_4MHZ ||
+		      MMRC_BW_16MHZ != (enum mmrc_bw)DOT11_BANDWIDTH_16MHZ));
+
+	memset(&info->control.rates, 0, sizeof(info->control.rates));
+	memset(&info->status.rates, 0, sizeof(info->status.rates));
+	mm81x_rc_sta_fill_basic_rates(tx_info, info, tx_bw);
+
+	/* Use basic rates for non data packets */
+	if (mm81x_rc_use_basic_rates(sta, skb, hdr))
+		return;
+
+	msta = (struct mm81x_sta *)sta->drv_priv;
+	if (!msta)
+		return;
+
+	ret = mm81x_rc_sta_get_rates(mm, msta, &rates, skb->len);
+	if (ret != 0)
+		return;
+
+	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+		info->control.rates[i].flags = 0;
+		if (rates.rates[i].rate != MMRC_MCS_UNUSED) {
+			u8 mcs = rates.rates[i].rate;
+			u8 nss_index = rates.rates[i].ss;
+			enum dot11_bandwidth bw_idx =
+				(enum dot11_bandwidth)rates.rates[i].bw;
+			enum mm81x_rate_preamble pream =
+				MM81X_RATE_PREAMBLE_S1G_SHORT;
+
+			mm81x_ratecode_bw_index_set(
+				&tx_info->rates[i].mm81x_ratecode, bw_idx);
+			mm81x_ratecode_mcs_index_set(
+				&tx_info->rates[i].mm81x_ratecode, mcs);
+			mm81x_ratecode_nss_index_set(
+				&tx_info->rates[i].mm81x_ratecode, nss_index);
+			if (bw_idx == DOT11_BANDWIDTH_1MHZ)
+				pream = MM81X_RATE_PREAMBLE_S1G_1M;
+			mm81x_ratecode_preamble_set(
+				&tx_info->rates[i].mm81x_ratecode, pream);
+			tx_info->rates[i].count = rates.rates[i].attempts;
+
+			if (rts_allowed &&
+			    (rates.rates[i].flags & BIT(MMRC_FLAGS_CTS_RTS))) {
+				mm81x_ratecode_enable_rts(
+					&tx_info->rates[i].mm81x_ratecode);
+				info->control.rates[i].flags |=
+					IEEE80211_TX_RC_USE_RTS_CTS;
+			}
+
+			if (rates.rates[i].guard == MMRC_GUARD_SHORT) {
+				mm81x_ratecode_enable_sgi(
+					&tx_info->rates[i].mm81x_ratecode);
+				info->control.rates[i].flags |=
+					IEEE80211_TX_RC_SHORT_GI;
+			}
+
+			/* Update skb tx_info */
+			info->control.rates[i].idx = rates.rates[i].rate;
+			info->control.rates[i].count = rates.rates[i].attempts;
+		} else {
+			info->control.rates[i].idx = -1;
+			info->control.rates[i].count = 0;
+			tx_info->rates[i].count = 0;
+		}
+	}
+}
+
+static void mm81x_rc_sta_set_rates(struct mm81x *mm, struct mm81x_sta *msta,
+				   struct mmrc_rate_table *rates, int attempts,
+				   bool was_aggregated)
+{
+	struct list_head *pos;
+
+	spin_lock_bh(&mm->mrc.lock);
+	list_for_each(pos, &mm->mrc.stas) {
+		struct mm81x_rc_sta *mrc_sta =
+			list_entry(pos, struct mm81x_rc_sta, list);
+
+		if (&msta->rc == mrc_sta) {
+			mmrc_feedback(msta->rc.tb, rates, attempts,
+				      was_aggregated);
+			break;
+		}
+	}
+	spin_unlock_bh(&mm->mrc.lock);
+}
+
+void mm81x_rc_sta_feedback_rates(struct mm81x *mm, struct sk_buff *skb,
+				 struct ieee80211_sta *sta,
+				 struct mm81x_skb_tx_status *tx_sts,
+				 int attempts)
+{
+	int i;
+	u32 tx_airtime = 0;
+	struct mmrc_rate_table rates;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
+	struct ieee80211_tx_rate *r = &txi->status.rates[0];
+	int count = min_t(int, MM81X_SKB_MAX_RATES, IEEE80211_TX_MAX_RATES);
+	struct mm81x_sta *msta = msta = (struct mm81x_sta *)sta->drv_priv;
+
+	/* Don't update rate info if basic rates were used */
+	if (mm81x_rc_use_basic_rates(sta, skb, hdr))
+		goto exit;
+
+	if (attempts <= 0)
+		/* Did we really send the packet? */
+		goto exit;
+
+	for (i = 0; i < count; i++) {
+		rates.rates[i].rate = mm81x_ratecode_mcs_index_get(
+			tx_sts->rates[i].mm81x_ratecode);
+		rates.rates[i].ss = mm81x_ratecode_nss_index_get(
+			tx_sts->rates[i].mm81x_ratecode);
+		rates.rates[i].guard =
+			mm81x_ratecode_sgi_get(tx_sts->rates[i].mm81x_ratecode);
+		rates.rates[i].bw = mm81x_ratecode_bw_index_get(
+			tx_sts->rates[i].mm81x_ratecode);
+		rates.rates[i].flags =
+			mm81x_ratecode_rts_get(tx_sts->rates[i].mm81x_ratecode);
+		rates.rates[i].attempts = tx_sts->rates[i].count;
+
+		tx_airtime +=
+			mmrc_calculate_rate_tx_time(&rates.rates[i], skb->len);
+	}
+
+	if (msta) {
+		/*
+		 * Save the rate information. This will be used to update
+		 * station's tx rate stats
+		 */
+		msta->last_sta_tx_rate.bw = rates.rates[0].bw;
+		msta->last_sta_tx_rate.rate = rates.rates[0].rate;
+		msta->last_sta_tx_rate.ss = rates.rates[0].ss;
+		msta->last_sta_tx_rate.guard = rates.rates[0].guard;
+	}
+
+	mm81x_rc_sta_set_rates(mm, msta, &rates, attempts,
+			       !!(le32_to_cpu(tx_sts->flags) &
+				  MM81X_TX_STATUS_WAS_AGGREGATED));
+
+	ieee80211_sta_register_airtime(sta, tx_sts->tid, tx_airtime, 0);
+
+exit:
+	ieee80211_tx_info_clear_status(txi);
+
+	if (!(le32_to_cpu(tx_sts->flags) & MM81X_TX_STATUS_FLAGS_NO_ACK) &&
+	    !(txi->flags & IEEE80211_TX_CTL_NO_ACK))
+		txi->flags |= IEEE80211_TX_STAT_ACK;
+
+	if (le32_to_cpu(tx_sts->flags) & MM81X_TX_STATUS_FLAGS_PS_FILTERED) {
+		txi->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+
+		/*
+		 * Clear TX CTL AMPDU flag so that this frame gets rescheduled
+		 * in ieee80211_handle_filtered_frame(). This flag will get set
+		 * again by mac80211's tx path on rescheduling.
+		 */
+		txi->flags &= ~IEEE80211_TX_CTL_AMPDU;
+		if (msta) {
+			if (!msta->tx_ps_filter_en)
+				mm81x_dbg(mm, MM81X_DBG_ANY,
+					  "TX ps filter set sta[%pM]",
+					  msta->addr);
+			msta->tx_ps_filter_en = true;
+		}
+	}
+
+	for (i = 0; i < count; i++) {
+		if (tx_sts->rates[i].count > 0) {
+			r[i].count = tx_sts->rates[i].count;
+			r[i].flags |= IEEE80211_TX_RC_MCS;
+		} else {
+			r[i].idx = -1;
+		}
+	}
+
+	/* single packet per A-MPDU (for now) */
+	if (txi->flags & IEEE80211_TX_CTL_AMPDU) {
+		txi->flags |= IEEE80211_TX_STAT_AMPDU;
+		txi->status.ampdu_len = 1;
+		txi->status.ampdu_ack_len =
+			txi->flags & IEEE80211_TX_STAT_ACK ? 1 : 0;
+	}
+
+	/*
+	 * Inform mac80211 that the SP (elicited by a PS-Poll or u-APSD) is
+	 * over
+	 */
+	if (sta && (txi->flags & IEEE80211_TX_STATUS_EOSP)) {
+		txi->flags &= ~IEEE80211_TX_STATUS_EOSP;
+		ieee80211_sta_eosp(sta);
+	}
+}
+
+void mm81x_rc_sta_state_check(struct mm81x *mm, struct ieee80211_vif *vif,
+			      struct ieee80211_sta *sta,
+			      enum ieee80211_sta_state old_state,
+			      enum ieee80211_sta_state new_state)
+{
+	struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
+
+	/* Add to Morse RC STA list */
+	if (old_state < new_state && new_state == IEEE80211_STA_ASSOC) {
+		/* Newly associated, add to RC */
+		mm81x_rc_sta_add(mm, vif, sta);
+	} else if (old_state > new_state && (old_state == IEEE80211_STA_ASSOC ||
+					     old_state == IEEE80211_STA_AUTH)) {
+		/* Lost or failed association; remove from list */
+		mm81x_rc_sta_remove(mm, sta);
+	} else if (old_state < new_state && old_state == IEEE80211_STA_NONE &&
+		   msta->rc.list.prev) {
+		/*
+		 * Special case for driver warning issue causing a sta to be
+		 * left on the list
+		 */
+		mm81x_dbg(mm, MM81X_DBG_ANY, "Remove stale sta from rc list");
+		mm81x_rc_sta_remove(mm, sta);
+	}
+}
-- 
2.43.0


  parent reply	other threads:[~2026-02-27  4:13 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-27  4:10 [PATCH wireless-next 00/35] wifi: mm81x: add mm81x driver Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 01/35] wifi: mm81x: add bus.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 02/35] wifi: mm81x: add command.c Lachlan Hodges
2026-03-06  8:38   ` Johannes Berg
2026-02-27  4:10 ` [PATCH wireless-next 03/35] wifi: mm81x: add command_defs.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 04/35] wifi: mm81x: add command.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 05/35] wifi: mm81x: add core.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 06/35] wifi: mm81x: add core.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 07/35] wifi: mm81x: add debug.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 08/35] wifi: mm81x: add debug.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 09/35] wifi: mm81x: add fw.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 10/35] wifi: mm81x: add fw.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 11/35] wifi: mm81x: add hif.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 12/35] wifi: mm81x: add hw.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 13/35] wifi: mm81x: add hw.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 14/35] wifi: mm81x: add mac.c Lachlan Hodges
2026-03-06  9:04   ` Johannes Berg
2026-03-09  4:43     ` Lachlan Hodges
2026-03-09  7:08       ` Johannes Berg
2026-03-09  9:23         ` Lachlan Hodges
2026-03-09  9:37           ` Johannes Berg
2026-03-20  6:39             ` Lachlan Hodges
2026-03-20  7:18               ` Johannes Berg
2026-03-20 10:06                 ` Arien Judge
2026-02-27  4:10 ` [PATCH wireless-next 15/35] wifi: mm81x: add mac.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 16/35] wifi: mm81x: add mmrc.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 17/35] wifi: mm81x: add mmrc.h Lachlan Hodges
2026-03-06  9:07   ` Johannes Berg
2026-02-27  4:10 ` [PATCH wireless-next 18/35] wifi: mm81x: add ps.c Lachlan Hodges
2026-03-06  9:07   ` Johannes Berg
2026-02-27  4:10 ` [PATCH wireless-next 19/35] wifi: mm81x: add ps.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 20/35] wifi: mm81x: add rate_code.h Lachlan Hodges
2026-02-27  4:10 ` Lachlan Hodges [this message]
2026-02-27  4:10 ` [PATCH wireless-next 22/35] wifi: mm81x: add rc.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 23/35] wifi: mm81x: add sdio.c Lachlan Hodges
2026-02-27 11:10   ` Krzysztof Kozlowski
2026-03-02  6:30     ` Lachlan Hodges
2026-03-06  8:20       ` Johannes Berg
2026-02-27  4:10 ` [PATCH wireless-next 24/35] wifi: mm81x: add skbq.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 25/35] wifi: mm81x: add skbq.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 26/35] wifi: mm81x: add usb.c Lachlan Hodges
2026-03-06  9:11   ` Johannes Berg
2026-02-27  4:10 ` [PATCH wireless-next 27/35] wifi: mm81x: add yaps.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 28/35] wifi: mm81x: add yaps.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 29/35] wifi: mm81x: add yaps_hw.c Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 30/35] wifi: mm81x: add yaps_hw.h Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 31/35] dt-bindings: vendor-prefixes: add Morse Micro Lachlan Hodges
2026-02-27 10:50   ` Krzysztof Kozlowski
2026-02-27  4:10 ` [PATCH wireless-next 32/35] dt-bindings: net: wireless: morsemicro: add mm81x family Lachlan Hodges
2026-02-27 10:59   ` Krzysztof Kozlowski
2026-02-27  4:10 ` [PATCH wireless-next 33/35] mmc: sdio: add Morse Micro vendor ids Lachlan Hodges
2026-02-27 10:49   ` Krzysztof Kozlowski
2026-03-04 16:45   ` Ulf Hansson
2026-02-27  4:10 ` [PATCH wireless-next 34/35] wifi: mm81x: add Kconfig and Makefile Lachlan Hodges
2026-02-27  4:10 ` [PATCH wireless-next 35/35] wifi: mm81x: add MAINTAINERS entry Lachlan Hodges

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=20260227041108.66508-22-lachlan.hodges@morsemicro.com \
    --to=lachlan.hodges@morsemicro.com \
    --cc=arien.judge@morsemicro.com \
    --cc=ayman.grais@morsemicro.com \
    --cc=dan.callaghan@morsemicro.com \
    --cc=johannes@sipsolutions.net \
    --cc=linux-kernel@vger.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