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, ¶ms)) 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, ¶ms); 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, ¶ms); 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