In S1G, a STA distributes a Response Indication (RI) to protect the frame expected at SIFS after the eliciting frame. Introduce a userspace configurable selector for the expected response frame type (i.e., Block Ack, NDP, or no response) after transmitting certain S1G frames. Signed-off-by: Ria Thomas --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 14 ++++++++++++++ net/wireless/nl80211.c | 24 ++++++++++++++++++++++++ net/wireless/rdev-ops.h | 14 ++++++++++++++ net/wireless/trace.h | 18 ++++++++++++++++++ 5 files changed, 72 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 899f267b7cf9..cd82f1801b2c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5210,6 +5210,8 @@ struct cfg80211_ops { struct cfg80211_ml_reconf_req *req); int (*set_epcs)(struct wiphy *wiphy, struct net_device *dev, bool val); + int (*set_s1g_ri)(struct wiphy *wiphy, struct net_device *dev, + u8 val); }; /* diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 8134f10e4e6c..4ca86c1fd361 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1361,6 +1361,10 @@ * user space that the NAN new cluster has been joined. The cluster ID is * indicated by %NL80211_ATTR_MAC. * + * @NL80211_CMD_SET_S1G_RI: This command is used to enable and set the + * response indication type for an S1G station. The frame type is + * indicated by %NL80211_ATTR_S1G_RI_FRAME_TYPE. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1624,6 +1628,8 @@ enum nl80211_commands { NL80211_CMD_NAN_NEXT_DW_NOTIFICATION, NL80211_CMD_NAN_CLUSTER_JOINED, + NL80211_CMD_SET_S1G_RI, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2973,6 +2979,12 @@ enum nl80211_commands { * primary channel is 2 MHz wide, and the control channel designates * the 1 MHz primary subchannel within that 2 MHz primary. * + * @NL80211_ATTR_S1G_RI_FRAME_TYPE: (u8) Integer attibute used with + * %NL80211_CMD_SET_S1G_RI that represents the RID + * (Response Indication Deferral) information distributed by + * an S1G STA in order to protect the response frame, which is + * expected a SIFS time after the frame that elicits that response. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3541,6 +3553,8 @@ enum nl80211_attrs { NL80211_ATTR_S1G_PRIMARY_2MHZ, + NL80211_ATTR_S1G_RI_FRAME_TYPE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c961cd42a832..315e17997cb6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -932,6 +932,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { NLA_POLICY_NESTED(nl80211_s1g_short_beacon), [NL80211_ATTR_BSS_PARAM] = { .type = NLA_FLAG }, [NL80211_ATTR_S1G_PRIMARY_2MHZ] = { .type = NLA_FLAG }, + [NL80211_ATTR_S1G_RI_FRAME_TYPE] = { .type = NLA_U8 }, }; /* policy for the key attributes */ @@ -17826,6 +17827,23 @@ nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *info) return rdev_set_epcs(rdev, dev, val); } +static int nl80211_set_s1g_ri(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u8 val; + + if (!rdev->ops->set_s1g_ri) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_S1G_RI_FRAME_TYPE]) + return -EINVAL; + + val = nla_get_u8(info->attrs[NL80211_ATTR_S1G_RI_FRAME_TYPE]); + + return rdev_set_s1g_ri(rdev, dev, val); +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -19031,6 +19049,12 @@ static const struct genl_small_ops nl80211_small_ops[] = { .flags = GENL_UNS_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), }, + { + .cmd = NL80211_CMD_SET_S1G_RI, + .doit = nl80211_set_s1g_ri, + .flags = GENL_UNS_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), + }, }; static struct genl_family nl80211_fam __ro_after_init = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index ac6884bacf3f..d29a4d55f860 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1584,4 +1584,18 @@ rdev_set_epcs(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_set_s1g_ri(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 val) +{ + struct wiphy *wiphy = &rdev->wiphy; + int ret = -EOPNOTSUPP; + + trace_rdev_set_s1g_ri(wiphy, dev, val); + ret = rdev->ops->set_s1g_ri(wiphy, dev, val); + trace_rdev_return_int(wiphy, ret); + + return ret; +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 2b71f1d867a0..ec200e94e9a6 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -3122,6 +3122,24 @@ TRACE_EVENT(rdev_set_epcs, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->val) ); +TRACE_EVENT(rdev_set_s1g_ri, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + u8 val), + TP_ARGS(wiphy, netdev, val), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u8, val) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->val = val; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", ri=%u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->val) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ -- 2.25.1 S1G STAs advertise a Response Indication (RI) that signals which immediate response (at SIFS) is expected to the eliciting frame. Updates mac80211 to track this configured response indication throughout the device's operation. It ensures that changes are recognized and communicated to the driver layer via BSS_CHANGED_S1G_RI, allowing firmware/hardware to be configured accordingly while also maintaining correct behaviour during interface restarts and ensuring that non-S1G interfaces are unaffected. enum defined for the RI types per IEEE Std 802.11-2024 Table 10-7. Signed-off-by: Ria Thomas --- include/linux/ieee80211-s1g.h | 14 ++++++++++++++ include/net/mac80211.h | 2 ++ net/mac80211/cfg.c | 18 ++++++++++++++++++ net/mac80211/link.c | 1 + net/mac80211/util.c | 3 +++ 5 files changed, 38 insertions(+) diff --git a/include/linux/ieee80211-s1g.h b/include/linux/ieee80211-s1g.h index 5b9ed2dcc00e..df5e37d07bf5 100644 --- a/include/linux/ieee80211-s1g.h +++ b/include/linux/ieee80211-s1g.h @@ -109,6 +109,20 @@ enum ieee80211_s1g_pri_chanwidth { IEEE80211_S1G_PRI_CHANWIDTH_1MHZ = 1, }; +/** + * enum ieee80211_s1g_ri - S1G Response Indication type + * @IEEE80211_S1G_RI_NO_RESPONSE: No immediate response + * @IEEE80211_S1G_RI_NDP_RESPONSE: NDP Ack/CTS/BlockAck + * @IEEE80211_S1G_RI_NORMAL_RESPONSE: Ack/CTS/BlockAck/TACK + * @IEEE80211_S1G_RI_LONG_RESPONSE: Not an individual control response frame + */ +enum ieee80211_s1g_ri { + IEEE80211_S1G_RI_NO_RESPONSE = 0, + IEEE80211_S1G_RI_NDP_RESPONSE = 1, + IEEE80211_S1G_RI_NORMAL_RESPONSE = 2, + IEEE80211_S1G_RI_LONG_RESPONSE = 3, +}; + /** * struct ieee80211_s1g_bcn_compat_ie - S1G Beacon Compatibility element * @compat_info: Compatibility Information diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c2e49542626c..8aa7640bb49f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -402,6 +402,7 @@ enum ieee80211_bss_change { BSS_CHANGED_MLD_VALID_LINKS = BIT_ULL(33), BSS_CHANGED_MLD_TTLM = BIT_ULL(34), BSS_CHANGED_TPE = BIT_ULL(35), + BSS_CHANGED_S1G_RI = BIT_ULL(36), /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -861,6 +862,7 @@ struct ieee80211_bss_conf { u8 bss_param_ch_cnt_link_id; u8 s1g_long_beacon_period; + enum ieee80211_s1g_ri s1g_ri; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b51c2c8584ae..ae47ae70d36a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -5533,6 +5533,23 @@ ieee80211_set_epcs(struct wiphy *wiphy, struct net_device *dev, bool enable) return ieee80211_mgd_set_epcs(sdata, enable); } +static int +ieee80211_set_s1g_ri(struct wiphy *wiphy, struct net_device *dev, u8 ri) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!sdata->vif.cfg.s1g) + return -EOPNOTSUPP; + + sdata->vif.bss_conf.s1g_ri = ri; + + if (ieee80211_sdata_running(sdata)) + ieee80211_link_info_change_notify(sdata, &sdata->deflink, + BSS_CHANGED_S1G_RI); + + return 0; +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -5649,4 +5666,5 @@ const struct cfg80211_ops mac80211_config_ops = { .get_radio_mask = ieee80211_get_radio_mask, .assoc_ml_reconf = ieee80211_assoc_ml_reconf, .set_epcs = ieee80211_set_epcs, + .set_s1g_ri = ieee80211_set_s1g_ri, }; diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 1e05845872af..4bf8155b4935 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -109,6 +109,7 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata, link->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; link->user_power_level = sdata->local->user_power_level; link_conf->txpower = INT_MIN; + link_conf->s1g_ri = IEEE80211_S1G_RI_NDP_RESPONSE; wiphy_work_init(&link->csa.finalize_work, ieee80211_csa_finalize_work); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0c46009a3d63..309a3a3b9441 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1960,6 +1960,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) ieee80211_assign_chanctx(local, sdata, link); } + if (sdata->vif.cfg.s1g) + changed |= BSS_CHANGED_S1G_RI; + switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: -- 2.25.1 S1G defines use of NDP Block Ack (BA) for aggregation, requiring negotiation of NDP ADDBA/DELBA action frames. When an S1G STA supports HT-immediate BA, it may reject legacy ADDBA requests, indicating preference for NDP Block Ack. 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. The selected response indication (RI) is updated upon successful negotiation and NDP preference is maintained throughout session setup, management and teardown on both TX and RX paths Signed-off-by: Ria Thomas --- include/linux/ieee80211-ht.h | 3 +++ include/linux/ieee80211.h | 2 ++ include/net/mac80211.h | 4 ++++ net/mac80211/agg-rx.c | 37 +++++++++++++++++++++++++++++++++--- net/mac80211/agg-tx.c | 22 +++++++++++++++++---- net/mac80211/debugfs.c | 1 + net/mac80211/ht.c | 8 +++++--- net/mac80211/ieee80211_i.h | 9 ++++++++- net/mac80211/iface.c | 3 +++ net/mac80211/rx.c | 11 +++++++++-- net/mac80211/s1g.c | 23 ++++++++++++++++++++++ net/mac80211/sta_info.h | 2 ++ 12 files changed, 112 insertions(+), 13 deletions(-) diff --git a/include/linux/ieee80211-ht.h b/include/linux/ieee80211-ht.h index 21bbf470540f..7612b72f9c7c 100644 --- a/include/linux/ieee80211-ht.h +++ b/include/linux/ieee80211-ht.h @@ -281,6 +281,9 @@ enum ieee80211_back_actioncode { 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 */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 96439de55f07..b6a1492cdb4d 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1492,6 +1492,8 @@ enum ieee80211_statuscode { 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, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8aa7640bb49f..efe9af471ea5 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2871,6 +2871,9 @@ struct ieee80211_txq { * 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 { @@ -2931,6 +2934,7 @@ 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 diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 7da909d78c68..65ae8bc1d96a 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -94,7 +94,8 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, /* 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 @@ -240,6 +241,7 @@ static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid, 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) + @@ -253,7 +255,8 @@ static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid, skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp)); mgmt->u.action.category = WLAN_CATEGORY_BACK; - mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP; + mgmt->u.action.u.addba_resp.action_code = use_ndp ? + WLAN_ACTION_NDP_ADDBA_RESP : WLAN_ACTION_ADDBA_RESP; mgmt->u.action.u.addba_resp.dialog_token = dialog_token; capab = u16_encode_bits(amsdu, IEEE80211_ADDBA_PARAM_AMSDU_MASK); @@ -275,6 +278,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, 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; @@ -300,6 +304,18 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, 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 && @@ -457,6 +473,18 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, end: if (status == WLAN_STATUS_SUCCESS) { + /* + * Keep user-forced NO_RESPONSE; otherwise + * track negotiated BA type + */ + if (sta->sdata->vif.bss_conf.s1g_ri != + IEEE80211_S1G_RI_NO_RESPONSE) { + u8 ri = req_ndp ? IEEE80211_S1G_RI_NDP_RESPONSE : + IEEE80211_S1G_RI_NORMAL_RESPONSE; + + ieee80211_s1g_update_ri(sta->sdata, + &sta->sdata->deflink, ri); + } __set_bit(tid, sta->ampdu_mlme.agg_session_valid); __clear_bit(tid, sta->ampdu_mlme.unexpected_agg); sta->ampdu_mlme.tid_rx_token[tid] = dialog_token; @@ -473,6 +501,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, struct ieee80211_mgmt *mgmt, size_t len) { + bool req_ndp = mgmt->u.action.u.addba_req.action_code == + WLAN_ACTION_NDP_ADDBA_REQ; u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num; u8 dialog_token, addba_ext_data; @@ -497,7 +527,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, __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, diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index d981b0fc57bf..17d9c047134d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -60,7 +60,7 @@ 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; @@ -80,7 +80,8 @@ static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid, skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req)); mgmt->u.action.category = WLAN_CATEGORY_BACK; - mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ; + mgmt->u.action.u.addba_req.action_code = ndp ? + WLAN_ACTION_NDP_ADDBA_REQ : WLAN_ACTION_ADDBA_REQ; mgmt->u.action.u.addba_req.dialog_token = dialog_token; capab = IEEE80211_ADDBA_PARAM_AMSDU_MASK; @@ -484,7 +485,8 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta, /* 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)); } @@ -521,6 +523,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) */ 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; @@ -940,7 +943,9 @@ void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid, 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, @@ -973,6 +978,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, { struct tid_ampdu_tx *tid_tx; struct ieee80211_txq *txq; + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_link_data *link = &sdata->deflink; u16 capab, tid, buf_size; bool amsdu; @@ -1054,6 +1061,13 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, tid_tx->last_tx = jiffies; } + if (sdata->vif.bss_conf.s1g_ri != + IEEE80211_S1G_RI_NO_RESPONSE) { + u8 ri = tid_tx->ndp ? IEEE80211_S1G_RI_NDP_RESPONSE : + IEEE80211_S1G_RI_NORMAL_RESPONSE; + + ieee80211_s1g_update_ri(sdata, link, ri); + } } else { __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_DECLINED); } diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index d02f07368c51..e8d0a8b71d59 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -490,6 +490,7 @@ static const char *hw_flag_names[] = { FLAG(DISALLOW_PUNCTURING), FLAG(HANDLES_QUIET_CSA), FLAG(STRICT), + FLAG(SUPPORTS_NDP_BLOCKACK), #undef FLAG }; diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 1c82a28b03de..ac67bf55f983 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -379,7 +379,7 @@ void ieee80211_ba_session_work(struct wiphy *wiphy, struct wiphy_work *work) 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)) @@ -455,7 +455,8 @@ void ieee80211_ba_session_work(struct wiphy *wiphy, struct wiphy_work *work) 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; @@ -472,7 +473,8 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba)); mgmt->u.action.category = WLAN_CATEGORY_BACK; - mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; + mgmt->u.action.u.delba.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 */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9d9313eee59f..265b41d07243 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2185,7 +2185,8 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, 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); @@ -2201,6 +2202,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, 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); @@ -2326,6 +2328,11 @@ void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata, 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); +void ieee80211_s1g_update_ri(struct ieee80211_sub_if_data *sdata, + struct ieee80211_link_data *link, + enum ieee80211_s1g_ri ri); /* Spectrum management */ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 4f04d95c19d4..1ef11a8e742d 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1577,14 +1577,17 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, if (sta) { switch (mgmt->u.action.u.addba_req.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; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6a1899512d07..d4607057b2e1 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1475,7 +1475,9 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx, !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; } @@ -3364,7 +3366,9 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames) !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) @@ -3743,16 +3747,19 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) switch (mgmt->u.action.u.addba_req.action_code) { case WLAN_ACTION_ADDBA_REQ: + case WLAN_ACTION_NDP_ADDBA_REQ: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.addba_req))) goto invalid; break; case WLAN_ACTION_ADDBA_RESP: + case WLAN_ACTION_NDP_ADDBA_RESP: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.addba_resp))) goto invalid; break; case WLAN_ACTION_DELBA: + case WLAN_ACTION_NDP_DELBA: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.delba))) goto invalid; diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c index 1f68df6e8067..fa26322d979a 100644 --- a/net/mac80211/s1g.c +++ b/net/mac80211/s1g.c @@ -220,3 +220,26 @@ void ieee80211_s1g_cap_to_sta_s1g_cap(struct ieee80211_sub_if_data *sdata, 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) +{ + if (!sdata->vif.cfg.s1g || + !ieee80211_hw_check(&sdata->local->hw, SUPPORTS_NDP_BLOCKACK)) + return false; + + return sdata->vif.bss_conf.s1g_ri == IEEE80211_S1G_RI_NDP_RESPONSE || + (sta && sta->sta.deflink.s1g_cap.s1g); +} + +void ieee80211_s1g_update_ri(struct ieee80211_sub_if_data *sdata, + struct ieee80211_link_data *link, + enum ieee80211_s1g_ri ri) +{ + if (!sdata->vif.cfg.s1g || + sdata->vif.bss_conf.s1g_ri == ri) + return; + + sdata->vif.bss_conf.s1g_ri = ri; + ieee80211_link_info_change_notify(sdata, link, BSS_CHANGED_S1G_RI); +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5288d5286651..870fbf4baba6 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -171,6 +171,7 @@ struct sta_info; * @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. @@ -199,6 +200,7 @@ struct tid_ampdu_tx { u16 failed_bar_ssn; bool bar_pending; bool amsdu; + bool ndp; u8 tid; }; -- 2.25.1