162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * HT handling
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
662306a36Sopenharmony_ci * Copyright 2002-2005, Instant802 Networks, Inc.
762306a36Sopenharmony_ci * Copyright 2005-2006, Devicescape Software, Inc.
862306a36Sopenharmony_ci * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
962306a36Sopenharmony_ci * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
1062306a36Sopenharmony_ci * Copyright 2007-2010, Intel Corporation
1162306a36Sopenharmony_ci * Copyright(c) 2015-2017 Intel Deutschland GmbH
1262306a36Sopenharmony_ci * Copyright (C) 2018-2022 Intel Corporation
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/**
1662306a36Sopenharmony_ci * DOC: RX A-MPDU aggregation
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Aggregation on the RX side requires only implementing the
1962306a36Sopenharmony_ci * @ampdu_action callback that is invoked to start/stop any
2062306a36Sopenharmony_ci * block-ack sessions for RX aggregation.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * When RX aggregation is started by the peer, the driver is
2362306a36Sopenharmony_ci * notified via @ampdu_action function, with the
2462306a36Sopenharmony_ci * %IEEE80211_AMPDU_RX_START action, and may reject the request
2562306a36Sopenharmony_ci * in which case a negative response is sent to the peer, if it
2662306a36Sopenharmony_ci * accepts it a positive response is sent.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * While the session is active, the device/driver are required
2962306a36Sopenharmony_ci * to de-aggregate frames and pass them up one by one to mac80211,
3062306a36Sopenharmony_ci * which will handle the reorder buffer.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * When the aggregation session is stopped again by the peer or
3362306a36Sopenharmony_ci * ourselves, the driver's @ampdu_action function will be called
3462306a36Sopenharmony_ci * with the action %IEEE80211_AMPDU_RX_STOP. In this case, the
3562306a36Sopenharmony_ci * call must not fail.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <linux/ieee80211.h>
3962306a36Sopenharmony_ci#include <linux/slab.h>
4062306a36Sopenharmony_ci#include <linux/export.h>
4162306a36Sopenharmony_ci#include <net/mac80211.h>
4262306a36Sopenharmony_ci#include "ieee80211_i.h"
4362306a36Sopenharmony_ci#include "driver-ops.h"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void ieee80211_free_tid_rx(struct rcu_head *h)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct tid_ampdu_rx *tid_rx =
4862306a36Sopenharmony_ci		container_of(h, struct tid_ampdu_rx, rcu_head);
4962306a36Sopenharmony_ci	int i;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	for (i = 0; i < tid_rx->buf_size; i++)
5262306a36Sopenharmony_ci		__skb_queue_purge(&tid_rx->reorder_buf[i]);
5362306a36Sopenharmony_ci	kfree(tid_rx->reorder_buf);
5462306a36Sopenharmony_ci	kfree(tid_rx->reorder_time);
5562306a36Sopenharmony_ci	kfree(tid_rx);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_civoid ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
5962306a36Sopenharmony_ci				     u16 initiator, u16 reason, bool tx)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct ieee80211_local *local = sta->local;
6262306a36Sopenharmony_ci	struct tid_ampdu_rx *tid_rx;
6362306a36Sopenharmony_ci	struct ieee80211_ampdu_params params = {
6462306a36Sopenharmony_ci		.sta = &sta->sta,
6562306a36Sopenharmony_ci		.action = IEEE80211_AMPDU_RX_STOP,
6662306a36Sopenharmony_ci		.tid = tid,
6762306a36Sopenharmony_ci		.amsdu = false,
6862306a36Sopenharmony_ci		.timeout = 0,
6962306a36Sopenharmony_ci		.ssn = 0,
7062306a36Sopenharmony_ci	};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	lockdep_assert_held(&sta->ampdu_mlme.mtx);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid],
7562306a36Sopenharmony_ci					lockdep_is_held(&sta->ampdu_mlme.mtx));
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!test_bit(tid, sta->ampdu_mlme.agg_session_valid))
7862306a36Sopenharmony_ci		return;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL);
8162306a36Sopenharmony_ci	__clear_bit(tid, sta->ampdu_mlme.agg_session_valid);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	ht_dbg(sta->sdata,
8462306a36Sopenharmony_ci	       "Rx BA session stop requested for %pM tid %u %s reason: %d\n",
8562306a36Sopenharmony_ci	       sta->sta.addr, tid,
8662306a36Sopenharmony_ci	       initiator == WLAN_BACK_RECIPIENT ? "recipient" : "initiator",
8762306a36Sopenharmony_ci	       (int)reason);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (drv_ampdu_action(local, sta->sdata, &params))
9062306a36Sopenharmony_ci		sdata_info(sta->sdata,
9162306a36Sopenharmony_ci			   "HW problem - can not stop rx aggregation for %pM tid %d\n",
9262306a36Sopenharmony_ci			   sta->sta.addr, tid);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* check if this is a self generated aggregation halt */
9562306a36Sopenharmony_ci	if (initiator == WLAN_BACK_RECIPIENT && tx)
9662306a36Sopenharmony_ci		ieee80211_send_delba(sta->sdata, sta->sta.addr,
9762306a36Sopenharmony_ci				     tid, WLAN_BACK_RECIPIENT, reason);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/*
10062306a36Sopenharmony_ci	 * return here in case tid_rx is not assigned - which will happen if
10162306a36Sopenharmony_ci	 * IEEE80211_HW_SUPPORTS_REORDERING_BUFFER is set.
10262306a36Sopenharmony_ci	 */
10362306a36Sopenharmony_ci	if (!tid_rx)
10462306a36Sopenharmony_ci		return;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	del_timer_sync(&tid_rx->session_timer);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
10962306a36Sopenharmony_ci	spin_lock_bh(&tid_rx->reorder_lock);
11062306a36Sopenharmony_ci	tid_rx->removed = true;
11162306a36Sopenharmony_ci	spin_unlock_bh(&tid_rx->reorder_lock);
11262306a36Sopenharmony_ci	del_timer_sync(&tid_rx->reorder_timer);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_civoid __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
11862306a36Sopenharmony_ci				    u16 initiator, u16 reason, bool tx)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	mutex_lock(&sta->ampdu_mlme.mtx);
12162306a36Sopenharmony_ci	___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason, tx);
12262306a36Sopenharmony_ci	mutex_unlock(&sta->ampdu_mlme.mtx);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_civoid ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
12662306a36Sopenharmony_ci				  const u8 *addr)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
12962306a36Sopenharmony_ci	struct sta_info *sta;
13062306a36Sopenharmony_ci	int i;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	rcu_read_lock();
13362306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, addr);
13462306a36Sopenharmony_ci	if (!sta) {
13562306a36Sopenharmony_ci		rcu_read_unlock();
13662306a36Sopenharmony_ci		return;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	for (i = 0; i < IEEE80211_NUM_TIDS; i++)
14062306a36Sopenharmony_ci		if (ba_rx_bitmap & BIT(i))
14162306a36Sopenharmony_ci			set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
14462306a36Sopenharmony_ci	rcu_read_unlock();
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_stop_rx_ba_session);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/*
14962306a36Sopenharmony_ci * After accepting the AddBA Request we activated a timer,
15062306a36Sopenharmony_ci * resetting it after each frame that arrives from the originator.
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistatic void sta_rx_agg_session_timer_expired(struct timer_list *t)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct tid_ampdu_rx *tid_rx = from_timer(tid_rx, t, session_timer);
15562306a36Sopenharmony_ci	struct sta_info *sta = tid_rx->sta;
15662306a36Sopenharmony_ci	u8 tid = tid_rx->tid;
15762306a36Sopenharmony_ci	unsigned long timeout;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	timeout = tid_rx->last_rx + TU_TO_JIFFIES(tid_rx->timeout);
16062306a36Sopenharmony_ci	if (time_is_after_jiffies(timeout)) {
16162306a36Sopenharmony_ci		mod_timer(&tid_rx->session_timer, timeout);
16262306a36Sopenharmony_ci		return;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n",
16662306a36Sopenharmony_ci	       sta->sta.addr, tid);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired);
16962306a36Sopenharmony_ci	ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void sta_rx_agg_reorder_timer_expired(struct timer_list *t)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct tid_ampdu_rx *tid_rx = from_timer(tid_rx, t, reorder_timer);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	rcu_read_lock();
17762306a36Sopenharmony_ci	ieee80211_release_reorder_timeout(tid_rx->sta, tid_rx->tid);
17862306a36Sopenharmony_ci	rcu_read_unlock();
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void ieee80211_add_addbaext(struct ieee80211_sub_if_data *sdata,
18262306a36Sopenharmony_ci				   struct sk_buff *skb,
18362306a36Sopenharmony_ci				   const struct ieee80211_addba_ext_ie *req,
18462306a36Sopenharmony_ci				   u16 buf_size)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct ieee80211_addba_ext_ie *resp;
18762306a36Sopenharmony_ci	u8 *pos;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	pos = skb_put_zero(skb, 2 + sizeof(struct ieee80211_addba_ext_ie));
19062306a36Sopenharmony_ci	*pos++ = WLAN_EID_ADDBA_EXT;
19162306a36Sopenharmony_ci	*pos++ = sizeof(struct ieee80211_addba_ext_ie);
19262306a36Sopenharmony_ci	resp = (struct ieee80211_addba_ext_ie *)pos;
19362306a36Sopenharmony_ci	resp->data = req->data & IEEE80211_ADDBA_EXT_NO_FRAG;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	resp->data |= u8_encode_bits(buf_size >> IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT,
19662306a36Sopenharmony_ci				     IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid,
20062306a36Sopenharmony_ci				      u8 dialog_token, u16 status, u16 policy,
20162306a36Sopenharmony_ci				      u16 buf_size, u16 timeout,
20262306a36Sopenharmony_ci				      const struct ieee80211_addba_ext_ie *addbaext)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = sta->sdata;
20562306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
20662306a36Sopenharmony_ci	struct sk_buff *skb;
20762306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
20862306a36Sopenharmony_ci	bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
20962306a36Sopenharmony_ci	u16 capab;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	skb = dev_alloc_skb(sizeof(*mgmt) +
21262306a36Sopenharmony_ci		    2 + sizeof(struct ieee80211_addba_ext_ie) +
21362306a36Sopenharmony_ci		    local->hw.extra_tx_headroom);
21462306a36Sopenharmony_ci	if (!skb)
21562306a36Sopenharmony_ci		return;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	skb_reserve(skb, local->hw.extra_tx_headroom);
21862306a36Sopenharmony_ci	mgmt = skb_put_zero(skb, 24);
21962306a36Sopenharmony_ci	memcpy(mgmt->da, da, ETH_ALEN);
22062306a36Sopenharmony_ci	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
22162306a36Sopenharmony_ci	if (sdata->vif.type == NL80211_IFTYPE_AP ||
22262306a36Sopenharmony_ci	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
22362306a36Sopenharmony_ci	    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
22462306a36Sopenharmony_ci		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
22562306a36Sopenharmony_ci	else if (sdata->vif.type == NL80211_IFTYPE_STATION)
22662306a36Sopenharmony_ci		memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN);
22762306a36Sopenharmony_ci	else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
22862306a36Sopenharmony_ci		memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
23162306a36Sopenharmony_ci					  IEEE80211_STYPE_ACTION);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
23462306a36Sopenharmony_ci	mgmt->u.action.category = WLAN_CATEGORY_BACK;
23562306a36Sopenharmony_ci	mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
23662306a36Sopenharmony_ci	mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	capab = u16_encode_bits(amsdu, IEEE80211_ADDBA_PARAM_AMSDU_MASK);
23962306a36Sopenharmony_ci	capab |= u16_encode_bits(policy, IEEE80211_ADDBA_PARAM_POLICY_MASK);
24062306a36Sopenharmony_ci	capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK);
24162306a36Sopenharmony_ci	capab |= u16_encode_bits(buf_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
24462306a36Sopenharmony_ci	mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
24562306a36Sopenharmony_ci	mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (sta->sta.deflink.he_cap.has_he && addbaext)
24862306a36Sopenharmony_ci		ieee80211_add_addbaext(sdata, skb, addbaext, buf_size);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	ieee80211_tx_skb(sdata, skb);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_civoid ___ieee80211_start_rx_ba_session(struct sta_info *sta,
25462306a36Sopenharmony_ci				      u8 dialog_token, u16 timeout,
25562306a36Sopenharmony_ci				      u16 start_seq_num, u16 ba_policy, u16 tid,
25662306a36Sopenharmony_ci				      u16 buf_size, bool tx, bool auto_seq,
25762306a36Sopenharmony_ci				      const struct ieee80211_addba_ext_ie *addbaext)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct ieee80211_local *local = sta->sdata->local;
26062306a36Sopenharmony_ci	struct tid_ampdu_rx *tid_agg_rx;
26162306a36Sopenharmony_ci	struct ieee80211_ampdu_params params = {
26262306a36Sopenharmony_ci		.sta = &sta->sta,
26362306a36Sopenharmony_ci		.action = IEEE80211_AMPDU_RX_START,
26462306a36Sopenharmony_ci		.tid = tid,
26562306a36Sopenharmony_ci		.amsdu = false,
26662306a36Sopenharmony_ci		.timeout = timeout,
26762306a36Sopenharmony_ci		.ssn = start_seq_num,
26862306a36Sopenharmony_ci	};
26962306a36Sopenharmony_ci	int i, ret = -EOPNOTSUPP;
27062306a36Sopenharmony_ci	u16 status = WLAN_STATUS_REQUEST_DECLINED;
27162306a36Sopenharmony_ci	u16 max_buf_size;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (tid >= IEEE80211_FIRST_TSPEC_TSID) {
27462306a36Sopenharmony_ci		ht_dbg(sta->sdata,
27562306a36Sopenharmony_ci		       "STA %pM requests BA session on unsupported tid %d\n",
27662306a36Sopenharmony_ci		       sta->sta.addr, tid);
27762306a36Sopenharmony_ci		goto end;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (!sta->sta.deflink.ht_cap.ht_supported &&
28162306a36Sopenharmony_ci	    !sta->sta.deflink.he_cap.has_he) {
28262306a36Sopenharmony_ci		ht_dbg(sta->sdata,
28362306a36Sopenharmony_ci		       "STA %pM erroneously requests BA session on tid %d w/o HT\n",
28462306a36Sopenharmony_ci		       sta->sta.addr, tid);
28562306a36Sopenharmony_ci		/* send a response anyway, it's an error case if we get here */
28662306a36Sopenharmony_ci		goto end;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
29062306a36Sopenharmony_ci		ht_dbg(sta->sdata,
29162306a36Sopenharmony_ci		       "Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
29262306a36Sopenharmony_ci		       sta->sta.addr, tid);
29362306a36Sopenharmony_ci		goto end;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (sta->sta.deflink.eht_cap.has_eht)
29762306a36Sopenharmony_ci		max_buf_size = IEEE80211_MAX_AMPDU_BUF_EHT;
29862306a36Sopenharmony_ci	else if (sta->sta.deflink.he_cap.has_he)
29962306a36Sopenharmony_ci		max_buf_size = IEEE80211_MAX_AMPDU_BUF_HE;
30062306a36Sopenharmony_ci	else
30162306a36Sopenharmony_ci		max_buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* sanity check for incoming parameters:
30462306a36Sopenharmony_ci	 * check if configuration can support the BA policy
30562306a36Sopenharmony_ci	 * and if buffer size does not exceeds max value */
30662306a36Sopenharmony_ci	/* XXX: check own ht delayed BA capability?? */
30762306a36Sopenharmony_ci	if (((ba_policy != 1) &&
30862306a36Sopenharmony_ci	     (!(sta->sta.deflink.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) ||
30962306a36Sopenharmony_ci	    (buf_size > max_buf_size)) {
31062306a36Sopenharmony_ci		status = WLAN_STATUS_INVALID_QOS_PARAM;
31162306a36Sopenharmony_ci		ht_dbg_ratelimited(sta->sdata,
31262306a36Sopenharmony_ci				   "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
31362306a36Sopenharmony_ci				   sta->sta.addr, tid, ba_policy, buf_size);
31462306a36Sopenharmony_ci		goto end;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci	/* determine default buffer size */
31762306a36Sopenharmony_ci	if (buf_size == 0)
31862306a36Sopenharmony_ci		buf_size = max_buf_size;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* make sure the size doesn't exceed the maximum supported by the hw */
32162306a36Sopenharmony_ci	if (buf_size > sta->sta.max_rx_aggregation_subframes)
32262306a36Sopenharmony_ci		buf_size = sta->sta.max_rx_aggregation_subframes;
32362306a36Sopenharmony_ci	params.buf_size = buf_size;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ht_dbg(sta->sdata, "AddBA Req buf_size=%d for %pM\n",
32662306a36Sopenharmony_ci	       buf_size, sta->sta.addr);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* examine state machine */
32962306a36Sopenharmony_ci	lockdep_assert_held(&sta->ampdu_mlme.mtx);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) {
33262306a36Sopenharmony_ci		if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) {
33362306a36Sopenharmony_ci			struct tid_ampdu_rx *tid_rx;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci			ht_dbg_ratelimited(sta->sdata,
33662306a36Sopenharmony_ci					   "updated AddBA Req from %pM on tid %u\n",
33762306a36Sopenharmony_ci					   sta->sta.addr, tid);
33862306a36Sopenharmony_ci			/* We have no API to update the timeout value in the
33962306a36Sopenharmony_ci			 * driver so reject the timeout update if the timeout
34062306a36Sopenharmony_ci			 * changed. If it did not change, i.e., no real update,
34162306a36Sopenharmony_ci			 * just reply with success.
34262306a36Sopenharmony_ci			 */
34362306a36Sopenharmony_ci			rcu_read_lock();
34462306a36Sopenharmony_ci			tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
34562306a36Sopenharmony_ci			if (tid_rx && tid_rx->timeout == timeout)
34662306a36Sopenharmony_ci				status = WLAN_STATUS_SUCCESS;
34762306a36Sopenharmony_ci			else
34862306a36Sopenharmony_ci				status = WLAN_STATUS_REQUEST_DECLINED;
34962306a36Sopenharmony_ci			rcu_read_unlock();
35062306a36Sopenharmony_ci			goto end;
35162306a36Sopenharmony_ci		}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		ht_dbg_ratelimited(sta->sdata,
35462306a36Sopenharmony_ci				   "unexpected AddBA Req from %pM on tid %u\n",
35562306a36Sopenharmony_ci				   sta->sta.addr, tid);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci		/* delete existing Rx BA session on the same tid */
35862306a36Sopenharmony_ci		___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
35962306a36Sopenharmony_ci						WLAN_STATUS_UNSPECIFIED_QOS,
36062306a36Sopenharmony_ci						false);
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER)) {
36462306a36Sopenharmony_ci		ret = drv_ampdu_action(local, sta->sdata, &params);
36562306a36Sopenharmony_ci		ht_dbg(sta->sdata,
36662306a36Sopenharmony_ci		       "Rx A-MPDU request on %pM tid %d result %d\n",
36762306a36Sopenharmony_ci		       sta->sta.addr, tid, ret);
36862306a36Sopenharmony_ci		if (!ret)
36962306a36Sopenharmony_ci			status = WLAN_STATUS_SUCCESS;
37062306a36Sopenharmony_ci		goto end;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* prepare A-MPDU MLME for Rx aggregation */
37462306a36Sopenharmony_ci	tid_agg_rx = kzalloc(sizeof(*tid_agg_rx), GFP_KERNEL);
37562306a36Sopenharmony_ci	if (!tid_agg_rx)
37662306a36Sopenharmony_ci		goto end;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	spin_lock_init(&tid_agg_rx->reorder_lock);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* rx timer */
38162306a36Sopenharmony_ci	timer_setup(&tid_agg_rx->session_timer,
38262306a36Sopenharmony_ci		    sta_rx_agg_session_timer_expired, TIMER_DEFERRABLE);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/* rx reorder timer */
38562306a36Sopenharmony_ci	timer_setup(&tid_agg_rx->reorder_timer,
38662306a36Sopenharmony_ci		    sta_rx_agg_reorder_timer_expired, 0);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* prepare reordering buffer */
38962306a36Sopenharmony_ci	tid_agg_rx->reorder_buf =
39062306a36Sopenharmony_ci		kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
39162306a36Sopenharmony_ci	tid_agg_rx->reorder_time =
39262306a36Sopenharmony_ci		kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
39362306a36Sopenharmony_ci	if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
39462306a36Sopenharmony_ci		kfree(tid_agg_rx->reorder_buf);
39562306a36Sopenharmony_ci		kfree(tid_agg_rx->reorder_time);
39662306a36Sopenharmony_ci		kfree(tid_agg_rx);
39762306a36Sopenharmony_ci		goto end;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	for (i = 0; i < buf_size; i++)
40162306a36Sopenharmony_ci		__skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	ret = drv_ampdu_action(local, sta->sdata, &params);
40462306a36Sopenharmony_ci	ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
40562306a36Sopenharmony_ci	       sta->sta.addr, tid, ret);
40662306a36Sopenharmony_ci	if (ret) {
40762306a36Sopenharmony_ci		kfree(tid_agg_rx->reorder_buf);
40862306a36Sopenharmony_ci		kfree(tid_agg_rx->reorder_time);
40962306a36Sopenharmony_ci		kfree(tid_agg_rx);
41062306a36Sopenharmony_ci		goto end;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* update data */
41462306a36Sopenharmony_ci	tid_agg_rx->ssn = start_seq_num;
41562306a36Sopenharmony_ci	tid_agg_rx->head_seq_num = start_seq_num;
41662306a36Sopenharmony_ci	tid_agg_rx->buf_size = buf_size;
41762306a36Sopenharmony_ci	tid_agg_rx->timeout = timeout;
41862306a36Sopenharmony_ci	tid_agg_rx->stored_mpdu_num = 0;
41962306a36Sopenharmony_ci	tid_agg_rx->auto_seq = auto_seq;
42062306a36Sopenharmony_ci	tid_agg_rx->started = false;
42162306a36Sopenharmony_ci	tid_agg_rx->reorder_buf_filtered = 0;
42262306a36Sopenharmony_ci	tid_agg_rx->tid = tid;
42362306a36Sopenharmony_ci	tid_agg_rx->sta = sta;
42462306a36Sopenharmony_ci	status = WLAN_STATUS_SUCCESS;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* activate it for RX */
42762306a36Sopenharmony_ci	rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (timeout) {
43062306a36Sopenharmony_ci		mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout));
43162306a36Sopenharmony_ci		tid_agg_rx->last_rx = jiffies;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ciend:
43562306a36Sopenharmony_ci	if (status == WLAN_STATUS_SUCCESS) {
43662306a36Sopenharmony_ci		__set_bit(tid, sta->ampdu_mlme.agg_session_valid);
43762306a36Sopenharmony_ci		__clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
43862306a36Sopenharmony_ci		sta->ampdu_mlme.tid_rx_token[tid] = dialog_token;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (tx)
44262306a36Sopenharmony_ci		ieee80211_send_addba_resp(sta, sta->sta.addr, tid,
44362306a36Sopenharmony_ci					  dialog_token, status, 1, buf_size,
44462306a36Sopenharmony_ci					  timeout, addbaext);
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic void __ieee80211_start_rx_ba_session(struct sta_info *sta,
44862306a36Sopenharmony_ci					    u8 dialog_token, u16 timeout,
44962306a36Sopenharmony_ci					    u16 start_seq_num, u16 ba_policy,
45062306a36Sopenharmony_ci					    u16 tid, u16 buf_size, bool tx,
45162306a36Sopenharmony_ci					    bool auto_seq,
45262306a36Sopenharmony_ci					    const struct ieee80211_addba_ext_ie *addbaext)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	mutex_lock(&sta->ampdu_mlme.mtx);
45562306a36Sopenharmony_ci	___ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
45662306a36Sopenharmony_ci					 start_seq_num, ba_policy, tid,
45762306a36Sopenharmony_ci					 buf_size, tx, auto_seq, addbaext);
45862306a36Sopenharmony_ci	mutex_unlock(&sta->ampdu_mlme.mtx);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_civoid ieee80211_process_addba_request(struct ieee80211_local *local,
46262306a36Sopenharmony_ci				     struct sta_info *sta,
46362306a36Sopenharmony_ci				     struct ieee80211_mgmt *mgmt,
46462306a36Sopenharmony_ci				     size_t len)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
46762306a36Sopenharmony_ci	struct ieee802_11_elems *elems = NULL;
46862306a36Sopenharmony_ci	u8 dialog_token;
46962306a36Sopenharmony_ci	int ies_len;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* extract session parameters from addba request frame */
47262306a36Sopenharmony_ci	dialog_token = mgmt->u.action.u.addba_req.dialog_token;
47362306a36Sopenharmony_ci	timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
47462306a36Sopenharmony_ci	start_seq_num =
47562306a36Sopenharmony_ci		le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
47862306a36Sopenharmony_ci	ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
47962306a36Sopenharmony_ci	tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
48062306a36Sopenharmony_ci	buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ies_len = len - offsetof(struct ieee80211_mgmt,
48362306a36Sopenharmony_ci				 u.action.u.addba_req.variable);
48462306a36Sopenharmony_ci	if (ies_len) {
48562306a36Sopenharmony_ci		elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
48662306a36Sopenharmony_ci					       ies_len, true, NULL);
48762306a36Sopenharmony_ci		if (!elems || elems->parse_error)
48862306a36Sopenharmony_ci			goto free;
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (sta->sta.deflink.eht_cap.has_eht && elems && elems->addba_ext_ie) {
49262306a36Sopenharmony_ci		u8 buf_size_1k = u8_get_bits(elems->addba_ext_ie->data,
49362306a36Sopenharmony_ci					     IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		buf_size |= buf_size_1k << IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
49962306a36Sopenharmony_ci					start_seq_num, ba_policy, tid,
50062306a36Sopenharmony_ci					buf_size, true, false,
50162306a36Sopenharmony_ci					elems ? elems->addba_ext_ie : NULL);
50262306a36Sopenharmony_cifree:
50362306a36Sopenharmony_ci	kfree(elems);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_civoid ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif,
50762306a36Sopenharmony_ci				 const u8 *addr, unsigned int tid)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
51062306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
51162306a36Sopenharmony_ci	struct sta_info *sta;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	rcu_read_lock();
51462306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, addr);
51562306a36Sopenharmony_ci	if (!sta)
51662306a36Sopenharmony_ci		goto unlock;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	set_bit(tid, sta->ampdu_mlme.tid_rx_manage_offl);
51962306a36Sopenharmony_ci	ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
52062306a36Sopenharmony_ci unlock:
52162306a36Sopenharmony_ci	rcu_read_unlock();
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_manage_rx_ba_offl);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_civoid ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif,
52662306a36Sopenharmony_ci				   const u8 *addr, unsigned int tid)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
52962306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
53062306a36Sopenharmony_ci	struct sta_info *sta;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	rcu_read_lock();
53362306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, addr);
53462306a36Sopenharmony_ci	if (!sta)
53562306a36Sopenharmony_ci		goto unlock;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired);
53862306a36Sopenharmony_ci	ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci unlock:
54162306a36Sopenharmony_ci	rcu_read_unlock();
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_rx_ba_timer_expired);
544