S1G defines use of NDP Block Ack (BA) for aggregation, requiring negotiation
of NDP ADDBA/DELBA action frames. If the S1G recipient supports HT-immediate
block ack, the sender must send an NDP ADDBA Request indicating it expects
only NDP BlockAck frames for the agreement.
Introduce support for NDP ADDBA and DELBA exchange in mac80211. The
implementation negotiates the BA mechanism during setup based on station
capabilities and driver support (IEEE80211_HW_SUPPORTS_NDP_BLOCKACK).
If negotiation fails due to mismatched expectations, a rejection with status code
WLAN_STATUS_REJECTED_NDP_BLOCK_ACK_SUGGESTED is returned as per IEEE 802.11-2024.
Trace sample:
IEEE 802.11 Wireless Management
Fixed parameters
Category code: Block Ack (3)
Action code: NDP ADDBA Request (0x80)
Dialog token: 0x01
Block Ack Parameters: 0x1003, A-MSDUs, Block Ack Policy
.... .... .... ...1 = A-MSDUs: Permitted in QoS Data MPDUs
.... .... .... ..1. = Block Ack Policy: Immediate Block Ack
.... .... ..00 00.. = Traffic Identifier: 0x0
0001 0000 00.. .... = Number of Buffers (1 Buffer = 2304 Bytes): 64
Block Ack Timeout: 0x0000
Block Ack Starting Sequence Control (SSC): 0x0010
.... .... .... 0000 = Fragment: 0
0000 0000 0001 .... = Starting Sequence Number: 1
IEEE 802.11 Wireless Management
Fixed parameters
Category code: Block Ack (3)
Action code: NDP ADDBA Response (0x81)
Dialog token: 0x02
Status code: BlockAck negotiation refused because, due to buffer constraints and other unspecified reasons, the recipient prefers to generate only NDP BlockAck frames (0x006d)
Block Ack Parameters: 0x1002, Block Ack Policy
.... .... .... ...0 = A-MSDUs: Not Permitted
.... .... .... ..1. = Block Ack Policy: Immediate Block Ack
.... .... ..00 00.. = Traffic Identifier: 0x0
0001 0000 00.. .... = Number of Buffers (1 Buffer = 2304 Bytes): 64
Block Ack Timeout: 0x0000
Signed-off-by: Ria Thomas <ria.thomas@morsemicro.com>
Link: https://patch.msgid.link/20260305091304.310990-1-ria.thomas@morsemicro.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
WLAN_ACTION_ADDBA_REQ = 0,
WLAN_ACTION_ADDBA_RESP = 1,
WLAN_ACTION_DELBA = 2,
+ WLAN_ACTION_NDP_ADDBA_REQ = 128,
+ WLAN_ACTION_NDP_ADDBA_RESP = 129,
+ WLAN_ACTION_NDP_DELBA = 130,
};
/* BACK (block-ack) parties */
WLAN_STATUS_REJECT_DSE_BAND = 96,
WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL = 99,
WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT = 103,
+ /* 802.11ah */
+ WLAN_STATUS_REJECTED_NDP_BLOCK_ACK_SUGGESTED = 109,
/* 802.11ai */
WLAN_STATUS_FILS_AUTHENTICATION_FAILURE = 112,
WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER = 113,
* HW flag so drivers can opt in according to their own control, e.g. in
* testing.
*
+ * @IEEE80211_HW_SUPPORTS_NDP_BLOCKACK: HW can transmit/receive S1G NDP
+ * BlockAck frames.
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
IEEE80211_HW_DISALLOW_PUNCTURING,
IEEE80211_HW_HANDLES_QUIET_CSA,
IEEE80211_HW_STRICT,
+ IEEE80211_HW_SUPPORTS_NDP_BLOCKACK,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
/* check if this is a self generated aggregation halt */
if (initiator == WLAN_BACK_RECIPIENT && tx)
ieee80211_send_delba(sta->sdata, sta->sta.addr,
- tid, WLAN_BACK_RECIPIENT, reason);
+ tid, WLAN_BACK_RECIPIENT, reason,
+ ieee80211_s1g_use_ndp_ba(sta->sdata, sta));
/*
* return here in case tid_rx is not assigned - which will happen if
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
+ bool use_ndp = ieee80211_s1g_use_ndp_ba(sdata, sta);
u16 capab;
skb = dev_alloc_skb(sizeof(*mgmt) +
skb_put(skb, 2 + sizeof(mgmt->u.action.addba_resp));
mgmt->u.action.category = WLAN_CATEGORY_BACK;
- mgmt->u.action.action_code = WLAN_ACTION_ADDBA_RESP;
+ mgmt->u.action.action_code = use_ndp ?
+ WLAN_ACTION_NDP_ADDBA_RESP : WLAN_ACTION_ADDBA_RESP;
mgmt->u.action.addba_resp.dialog_token = dialog_token;
u8 dialog_token, u16 timeout,
u16 start_seq_num, u16 ba_policy, u16 tid,
u16 buf_size, bool tx, bool auto_seq,
+ bool req_ndp,
const u8 addba_ext_data)
{
struct ieee80211_local *local = sta->sdata->local;
goto end;
}
+ if (tx && ieee80211_s1g_use_ndp_ba(sta->sdata, sta) && !req_ndp) {
+ /*
+ * According to IEEE 802.11-2024: Inform S1G originator
+ * ADDBA rejected as NDP BlockAck is preferred
+ */
+ status = WLAN_STATUS_REJECTED_NDP_BLOCK_ACK_SUGGESTED;
+ ht_dbg(sta->sdata,
+ "Rejecting AddBA Req from %pM tid %u - require NDP BlockAck\n",
+ sta->sta.addr, tid);
+ goto end;
+ }
+
if (!sta->sta.valid_links &&
!sta->sta.deflink.ht_cap.ht_supported &&
!sta->sta.deflink.he_cap.has_he &&
struct ieee80211_mgmt *mgmt,
size_t len)
{
+ bool req_ndp = mgmt->u.action.action_code == WLAN_ACTION_NDP_ADDBA_REQ;
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
u8 dialog_token, addba_ext_data;
__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
start_seq_num, ba_policy, tid,
- buf_size, true, false, addba_ext_data);
+ buf_size, true, false,
+ req_ndp, addba_ext_data);
}
void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif,
static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid,
u8 dialog_token, u16 start_seq_num,
- u16 agg_size, u16 timeout)
+ u16 agg_size, u16 timeout, bool ndp)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
skb_put(skb, 2 + sizeof(mgmt->u.action.addba_req));
mgmt->u.action.category = WLAN_CATEGORY_BACK;
- mgmt->u.action.action_code = WLAN_ACTION_ADDBA_REQ;
+ mgmt->u.action.action_code = ndp ?
+ WLAN_ACTION_NDP_ADDBA_REQ : WLAN_ACTION_ADDBA_REQ;
mgmt->u.action.addba_req.dialog_token = dialog_token;
capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK;
/* send AddBA request */
ieee80211_send_addba_request(sta, tid, tid_tx->dialog_token,
- tid_tx->ssn, buf_size, tid_tx->timeout);
+ tid_tx->ssn, buf_size, tid_tx->timeout,
+ tid_tx->ndp);
WARN_ON(test_and_set_bit(HT_AGG_STATE_SENT_ADDBA, &tid_tx->state));
}
*/
synchronize_net();
+ tid_tx->ndp = ieee80211_s1g_use_ndp_ba(sdata, sta);
params.ssn = sta->tid_seq[tid] >> 4;
ret = drv_ampdu_action(local, sdata, ¶ms);
tid_tx->ssn = params.ssn;
if (send_delba)
ieee80211_send_delba(sdata, sta->sta.addr, tid,
- WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
+ WLAN_BACK_INITIATOR,
+ WLAN_REASON_QSTA_NOT_USE,
+ tid_tx->ndp);
}
void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,
FLAG(DISALLOW_PUNCTURING),
FLAG(HANDLES_QUIET_CSA),
FLAG(STRICT),
+ FLAG(SUPPORTS_NDP_BLOCKACK),
#undef FLAG
};
sta->ampdu_mlme.tid_rx_manage_offl))
__ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid,
IEEE80211_MAX_AMPDU_BUF_HT,
- false, true, 0);
+ false, true, false, 0);
if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS,
sta->ampdu_mlme.tid_rx_manage_offl))
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
const u8 *da, u16 tid,
- u16 initiator, u16 reason_code)
+ u16 initiator, u16 reason_code,
+ bool use_ndp)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
skb_put(skb, 2 + sizeof(mgmt->u.action.delba));
mgmt->u.action.category = WLAN_CATEGORY_BACK;
- mgmt->u.action.action_code = WLAN_ACTION_DELBA;
+ mgmt->u.action.action_code = use_ndp ?
+ WLAN_ACTION_NDP_DELBA : WLAN_ACTION_DELBA;
params = (u16)(initiator << 11); /* bit 11 initiator */
params |= (u16)(tid << 12); /* bit 15:12 TID number */
struct link_sta_info *link_sta);
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
const u8 *da, u16 tid,
- u16 initiator, u16 reason_code);
+ u16 initiator, u16 reason_code,
+ bool use_ndp);
int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps, const u8 *da,
const u8 *bssid, int link_id);
u8 dialog_token, u16 timeout,
u16 start_seq_num, u16 ba_policy, u16 tid,
u16 buf_size, bool tx, bool auto_seq,
+ bool req_ndp,
const u8 addba_ext_data);
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
enum ieee80211_agg_stop_reason reason);
void ieee80211_s1g_cap_to_sta_s1g_cap(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_s1g_cap *s1g_cap_ie,
struct link_sta_info *link_sta);
+bool ieee80211_s1g_use_ndp_ba(const struct ieee80211_sub_if_data *sdata,
+ const struct sta_info *sta);
/* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
if (sta) {
switch (mgmt->u.action.action_code) {
case WLAN_ACTION_ADDBA_REQ:
+ case WLAN_ACTION_NDP_ADDBA_REQ:
ieee80211_process_addba_request(local, sta,
mgmt, len);
break;
case WLAN_ACTION_ADDBA_RESP:
+ case WLAN_ACTION_NDP_ADDBA_RESP:
ieee80211_process_addba_resp(local, sta,
mgmt, len);
break;
case WLAN_ACTION_DELBA:
+ case WLAN_ACTION_NDP_DELBA:
ieee80211_process_delba(sdata, sta,
mgmt, len);
break;
!test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
WLAN_BACK_RECIPIENT,
- WLAN_REASON_QSTA_REQUIRE_SETUP);
+ WLAN_REASON_QSTA_REQUIRE_SETUP,
+ ieee80211_s1g_use_ndp_ba(rx->sdata,
+ rx->sta));
goto dont_reorder;
}
!test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
WLAN_BACK_RECIPIENT,
- WLAN_REASON_QSTA_REQUIRE_SETUP);
+ WLAN_REASON_QSTA_REQUIRE_SETUP,
+ ieee80211_s1g_use_ndp_ba(rx->sdata,
+ rx->sta));
tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]);
if (!tid_agg_rx)
switch (mgmt->u.action.action_code) {
case WLAN_ACTION_ADDBA_REQ:
+ case WLAN_ACTION_NDP_ADDBA_REQ:
if (len < IEEE80211_MIN_ACTION_SIZE(addba_req))
goto invalid;
break;
case WLAN_ACTION_ADDBA_RESP:
+ case WLAN_ACTION_NDP_ADDBA_RESP:
if (len < IEEE80211_MIN_ACTION_SIZE(addba_resp))
goto invalid;
break;
case WLAN_ACTION_DELBA:
+ case WLAN_ACTION_NDP_DELBA:
if (len < IEEE80211_MIN_ACTION_SIZE(delba))
goto invalid;
break;
ieee80211_sta_recalc_aggregates(&link_sta->sta->sta);
}
+
+bool ieee80211_s1g_use_ndp_ba(const struct ieee80211_sub_if_data *sdata,
+ const struct sta_info *sta)
+{
+ return sdata->vif.cfg.s1g &&
+ ieee80211_hw_check(&sdata->local->hw, SUPPORTS_NDP_BLOCKACK) &&
+ (sta && sta->sta.deflink.s1g_cap.s1g);
+}
* @bar_pending: BAR needs to be re-sent
* @amsdu: support A-MSDU within A-MDPU
* @ssn: starting sequence number of the session
+ * @ndp: this session is using NDP Block ACKs
*
* This structure's lifetime is managed by RCU, assignments to
* the array holding it must hold the aggregation mutex.
u16 failed_bar_ssn;
bool bar_pending;
bool amsdu;
+ bool ndp;
u8 tid;
};