Commit a5d028d6 authored by Lorenzo Bianconi's avatar Lorenzo Bianconi Committed by Felix Fietkau
Browse files

wifi: mt76: mt7996: add wed rro delete session garbage collector



Introduce the capability to clear WED rro session configured in the hw
according to the event reported by the MCU firmware

Co-developed-by: default avatarSujuan Chen <sujuan.chen@mediatek.com>
Signed-off-by: default avatarSujuan Chen <sujuan.chen@mediatek.com>
Co-developed-by: default avatarBo Jiao <Bo.Jiao@mediatek.com>
Signed-off-by: default avatarBo Jiao <Bo.Jiao@mediatek.com>
Signed-off-by: default avatarLorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent 00d2ced0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -404,6 +404,7 @@ struct mt76_rx_tid {
	spinlock_t lock;
	struct delayed_work reorder_work;

	u16 id;
	u16 head;
	u16 size;
	u16 nframes;
+1 −0
Original line number Diff line number Diff line
@@ -1022,6 +1022,7 @@ enum {
	MCU_UNI_EVENT_ROC = 0x27,
	MCU_UNI_EVENT_TX_DONE = 0x2d,
	MCU_UNI_EVENT_NIC_CAPAB = 0x43,
	MCU_UNI_EVENT_WED_RRO = 0x57,
	MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
	MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
};
+52 −0
Original line number Diff line number Diff line
@@ -641,6 +641,54 @@ static void mt7996_wed_rro_free(struct mt7996_dev *dev)
#endif
}

static void mt7996_wed_rro_work(struct work_struct *work)
{
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
	struct mt7996_dev *dev;
	LIST_HEAD(list);

	dev = (struct mt7996_dev *)container_of(work, struct mt7996_dev,
						wed_rro.work);

	spin_lock_bh(&dev->wed_rro.lock);
	list_splice_init(&dev->wed_rro.poll_list, &list);
	spin_unlock_bh(&dev->wed_rro.lock);

	while (!list_empty(&list)) {
		struct mt7996_wed_rro_session_id *e;
		int i;

		e = list_first_entry(&list, struct mt7996_wed_rro_session_id,
				     list);
		list_del_init(&e->list);

		for (i = 0; i < MT7996_RRO_WINDOW_MAX_LEN; i++) {
			void *ptr = dev->wed_rro.session.ptr;
			struct mt7996_wed_rro_addr *elem;
			u32 idx, elem_id = i;

			if (e->id == MT7996_RRO_MAX_SESSION)
				goto reset;

			idx = e->id / MT7996_RRO_BA_BITMAP_SESSION_SIZE;
			if (idx >= ARRAY_SIZE(dev->wed_rro.addr_elem))
				goto out;

			ptr = dev->wed_rro.addr_elem[idx].ptr;
			elem_id +=
				(e->id % MT7996_RRO_BA_BITMAP_SESSION_SIZE) *
				MT7996_RRO_WINDOW_MAX_LEN;
reset:
			elem = ptr + elem_id * sizeof(*elem);
			elem->signature = 0xff;
		}
		mt7996_mcu_wed_rro_reset_sessions(dev, e->id);
out:
		kfree(e);
	}
#endif
}

static int mt7996_init_hardware(struct mt7996_dev *dev)
{
	int ret, idx;
@@ -648,6 +696,9 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
	mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);

	INIT_WORK(&dev->init_work, mt7996_init_work);
	INIT_WORK(&dev->wed_rro.work, mt7996_wed_rro_work);
	INIT_LIST_HEAD(&dev->wed_rro.poll_list);
	spin_lock_init(&dev->wed_rro.lock);

	dev->dbdc_support = true;
	dev->tbtc_support = true;
@@ -1100,6 +1151,7 @@ int mt7996_register_device(struct mt7996_dev *dev)

void mt7996_unregister_device(struct mt7996_dev *dev)
{
	cancel_work_sync(&dev->wed_rro.work);
	mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
	mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
	mt7996_coredump_unregister(dev);
+3 −0
Original line number Diff line number Diff line
@@ -1822,6 +1822,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
	if (phy3)
		ieee80211_stop_queues(phy3->mt76->hw);

	cancel_work_sync(&dev->wed_rro.work);
	cancel_delayed_work_sync(&dev->mphy.mac_work);
	if (phy2)
		cancel_delayed_work_sync(&phy2->mt76->mac_work);
@@ -1920,6 +1921,8 @@ void mt7996_mac_reset_work(struct work_struct *work)
	set_bit(MT76_RESET, &dev->mphy.state);
	set_bit(MT76_MCU_RESET, &dev->mphy.state);
	wake_up(&dev->mt76.mcu.wait);

	cancel_work_sync(&dev->wed_rro.work);
	cancel_delayed_work_sync(&dev->mphy.mac_work);
	if (phy2) {
		set_bit(MT76_RESET, &phy2->mt76->state);
+89 −0
Original line number Diff line number Diff line
@@ -526,6 +526,73 @@ mt7996_mcu_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
	dev_kfree_skb(skb);
}

static void
mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb)
{
	struct mt7996_mcu_wed_rro_event *event = (void *)skb->data;

	if (!dev->has_rro)
		return;

	skb_pull(skb, sizeof(struct mt7996_mcu_rxd) + 4);

	switch (le16_to_cpu(event->tag)) {
	case UNI_WED_RRO_BA_SESSION_STATUS: {
		struct mt7996_mcu_wed_rro_ba_event *e;

		while (skb->len >= sizeof(*e)) {
			struct mt76_rx_tid *tid;
			struct mt76_wcid *wcid;
			u16 idx;

			e = (void *)skb->data;
			idx = le16_to_cpu(e->wlan_id);
			if (idx >= ARRAY_SIZE(dev->mt76.wcid))
				break;

			wcid = rcu_dereference(dev->mt76.wcid[idx]);
			if (!wcid || !wcid->sta)
				break;

			if (e->tid >= ARRAY_SIZE(wcid->aggr))
				break;

			tid = rcu_dereference(wcid->aggr[e->tid]);
			if (!tid)
				break;

			tid->id = le16_to_cpu(e->id);
			skb_pull(skb, sizeof(*e));
		}
		break;
	}
	case UNI_WED_RRO_BA_SESSION_DELETE: {
		struct mt7996_mcu_wed_rro_ba_delete_event *e;

		while (skb->len >= sizeof(*e)) {
			struct mt7996_wed_rro_session_id *session;

			e = (void *)skb->data;
			session = kzalloc(sizeof(*session), GFP_ATOMIC);
			if (!session)
				break;

			session->id = le16_to_cpu(e->session_id);

			spin_lock_bh(&dev->wed_rro.lock);
			list_add_tail(&session->list, &dev->wed_rro.poll_list);
			spin_unlock_bh(&dev->wed_rro.lock);

			ieee80211_queue_work(mt76_hw(dev), &dev->wed_rro.work);
			skb_pull(skb, sizeof(*e));
		}
		break;
	}
	default:
		break;
	}
}

static void
mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
{
@@ -544,6 +611,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
	case MCU_UNI_EVENT_ALL_STA_INFO:
		mt7996_mcu_rx_all_sta_info_event(dev, skb);
		break;
	case MCU_UNI_EVENT_WED_RRO:
		mt7996_mcu_wed_rro_event(dev, skb);
		break;
	default:
		break;
	}
@@ -4087,3 +4157,22 @@ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ALL_STA_INFO),
				 &req, sizeof(req), false);
}

int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id)
{
	struct {
		u8 __rsv[4];

		__le16 tag;
		__le16 len;
		__le16 session_id;
		u8 pad[4];
	} __packed req = {
		.tag = cpu_to_le16(UNI_RRO_DEL_BA_SESSION),
		.len = cpu_to_le16(sizeof(req) - 4),
		.session_id = cpu_to_le16(id),
	};

	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req,
				 sizeof(req), true);
}
Loading