mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-28 01:28:59 +00:00
0e0192098a
This fixes potental rx drop issues
Signed-off-by: Felix Fietkau <nbd@nbd.name>
(cherry-picked from commit 68189835ac
)
577 lines
16 KiB
Diff
577 lines
16 KiB
Diff
From: Lorenzo Bianconi <lorenzo@kernel.org>
|
|
Date: Mon, 23 Aug 2021 20:02:39 +0200
|
|
Subject: [PATCH] mac80211: introduce individual TWT support in AP mode
|
|
|
|
Introduce TWT action frames parsing support to mac80211.
|
|
Currently just individual TWT agreement are support in AP mode.
|
|
Whenever the AP receives a TWT action frame from an associated client,
|
|
after performing sanity checks, it will notify the underlay driver with
|
|
requested parameters in order to check if they are supported and if there
|
|
is enough room for a new agreement. The driver is expected to set the
|
|
agreement result and report it to mac80211.
|
|
|
|
Drivers supporting this have two new callbacks:
|
|
- add_twt_setup (mandatory)
|
|
- twt_teardown_request (optional)
|
|
|
|
mac80211 will send an action frame reply according to the result
|
|
reported by the driver.
|
|
|
|
Tested-by: Peter Chiu <chui-hao.chiu@mediatek.com>
|
|
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
|
|
Link: https://lore.kernel.org/r/257512f2e22ba42b9f2624942a128dd8f141de4b.1629741512.git.lorenzo@kernel.org
|
|
[use le16p_replace_bits(), minor cleanups, use (void *) casts,
|
|
fix to use ieee80211_get_he_iftype_cap() correctly]
|
|
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
|
---
|
|
|
|
--- a/include/net/mac80211.h
|
|
+++ b/include/net/mac80211.h
|
|
@@ -4219,6 +4219,11 @@ struct ieee80211_ops {
|
|
void (*sta_set_decap_offload)(struct ieee80211_hw *hw,
|
|
struct ieee80211_vif *vif,
|
|
struct ieee80211_sta *sta, bool enabled);
|
|
+ void (*add_twt_setup)(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_sta *sta,
|
|
+ struct ieee80211_twt_setup *twt);
|
|
+ void (*twt_teardown_request)(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_sta *sta, u8 flowid);
|
|
};
|
|
|
|
/**
|
|
--- a/net/mac80211/driver-ops.h
|
|
+++ b/net/mac80211/driver-ops.h
|
|
@@ -1429,4 +1429,40 @@ static inline void drv_sta_set_decap_off
|
|
trace_drv_return_void(local);
|
|
}
|
|
|
|
+static inline void drv_add_twt_setup(struct ieee80211_local *local,
|
|
+ struct ieee80211_sub_if_data *sdata,
|
|
+ struct ieee80211_sta *sta,
|
|
+ struct ieee80211_twt_setup *twt)
|
|
+{
|
|
+ struct ieee80211_twt_params *twt_agrt;
|
|
+
|
|
+ might_sleep();
|
|
+
|
|
+ if (!check_sdata_in_driver(sdata))
|
|
+ return;
|
|
+
|
|
+ twt_agrt = (void *)twt->params;
|
|
+
|
|
+ trace_drv_add_twt_setup(local, sta, twt, twt_agrt);
|
|
+ local->ops->add_twt_setup(&local->hw, sta, twt);
|
|
+ trace_drv_return_void(local);
|
|
+}
|
|
+
|
|
+static inline void drv_twt_teardown_request(struct ieee80211_local *local,
|
|
+ struct ieee80211_sub_if_data *sdata,
|
|
+ struct ieee80211_sta *sta,
|
|
+ u8 flowid)
|
|
+{
|
|
+ might_sleep();
|
|
+ if (!check_sdata_in_driver(sdata))
|
|
+ return;
|
|
+
|
|
+ if (!local->ops->twt_teardown_request)
|
|
+ return;
|
|
+
|
|
+ trace_drv_twt_teardown_request(local, sta, flowid);
|
|
+ local->ops->twt_teardown_request(&local->hw, sta, flowid);
|
|
+ trace_drv_return_void(local);
|
|
+}
|
|
+
|
|
#endif /* __MAC80211_DRIVER_OPS */
|
|
--- a/net/mac80211/ieee80211_i.h
|
|
+++ b/net/mac80211/ieee80211_i.h
|
|
@@ -954,6 +954,7 @@ struct ieee80211_sub_if_data {
|
|
|
|
struct work_struct work;
|
|
struct sk_buff_head skb_queue;
|
|
+ struct sk_buff_head status_queue;
|
|
|
|
u8 needed_rx_chains;
|
|
enum ieee80211_smps_mode smps_mode;
|
|
@@ -2093,6 +2094,11 @@ ieee80211_he_op_ie_to_bss_conf(struct ie
|
|
|
|
/* S1G */
|
|
void ieee80211_s1g_sta_rate_init(struct sta_info *sta);
|
|
+bool ieee80211_s1g_is_twt_setup(struct sk_buff *skb);
|
|
+void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata,
|
|
+ struct sk_buff *skb);
|
|
+void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata,
|
|
+ struct sk_buff *skb);
|
|
|
|
/* Spectrum management */
|
|
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
|
--- a/net/mac80211/iface.c
|
|
+++ b/net/mac80211/iface.c
|
|
@@ -563,6 +563,7 @@ static void ieee80211_do_stop(struct iee
|
|
*/
|
|
ieee80211_free_keys(sdata, true);
|
|
skb_queue_purge(&sdata->skb_queue);
|
|
+ skb_queue_purge(&sdata->status_queue);
|
|
}
|
|
|
|
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
|
@@ -1070,6 +1071,7 @@ int ieee80211_add_virtual_monitor(struct
|
|
}
|
|
|
|
skb_queue_head_init(&sdata->skb_queue);
|
|
+ skb_queue_head_init(&sdata->status_queue);
|
|
INIT_WORK(&sdata->work, ieee80211_iface_work);
|
|
|
|
return 0;
|
|
@@ -1442,6 +1444,24 @@ static void ieee80211_if_setup_no_queue(
|
|
#endif
|
|
}
|
|
|
|
+static void ieee80211_iface_process_status(struct ieee80211_sub_if_data *sdata,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
|
|
+
|
|
+ if (ieee80211_is_action(mgmt->frame_control) &&
|
|
+ mgmt->u.action.category == WLAN_CATEGORY_S1G) {
|
|
+ switch (mgmt->u.action.u.s1g.action_code) {
|
|
+ case WLAN_S1G_TWT_TEARDOWN:
|
|
+ case WLAN_S1G_TWT_SETUP:
|
|
+ ieee80211_s1g_status_twt_action(sdata, skb);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
static void ieee80211_iface_work(struct work_struct *work)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata =
|
|
@@ -1519,6 +1539,16 @@ static void ieee80211_iface_work(struct
|
|
WARN_ON(1);
|
|
break;
|
|
}
|
|
+ } else if (ieee80211_is_action(mgmt->frame_control) &&
|
|
+ mgmt->u.action.category == WLAN_CATEGORY_S1G) {
|
|
+ switch (mgmt->u.action.u.s1g.action_code) {
|
|
+ case WLAN_S1G_TWT_TEARDOWN:
|
|
+ case WLAN_S1G_TWT_SETUP:
|
|
+ ieee80211_s1g_rx_twt_action(sdata, skb);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
} else if (ieee80211_is_ext(mgmt->frame_control)) {
|
|
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
|
ieee80211_sta_rx_queued_ext(sdata, skb);
|
|
@@ -1574,6 +1604,12 @@ static void ieee80211_iface_work(struct
|
|
kfree_skb(skb);
|
|
}
|
|
|
|
+ /* process status queue */
|
|
+ while ((skb = skb_dequeue(&sdata->status_queue))) {
|
|
+ ieee80211_iface_process_status(sdata, skb);
|
|
+ kfree_skb(skb);
|
|
+ }
|
|
+
|
|
/* then other type-dependent work */
|
|
switch (sdata->vif.type) {
|
|
case NL80211_IFTYPE_STATION:
|
|
@@ -1637,6 +1673,7 @@ static void ieee80211_setup_sdata(struct
|
|
}
|
|
|
|
skb_queue_head_init(&sdata->skb_queue);
|
|
+ skb_queue_head_init(&sdata->status_queue);
|
|
INIT_WORK(&sdata->work, ieee80211_iface_work);
|
|
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
|
|
INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
|
|
--- a/net/mac80211/rx.c
|
|
+++ b/net/mac80211/rx.c
|
|
@@ -3210,6 +3210,68 @@ ieee80211_rx_h_mgmt_check(struct ieee802
|
|
return RX_CONTINUE;
|
|
}
|
|
|
|
+static bool
|
|
+ieee80211_process_rx_twt_action(struct ieee80211_rx_data *rx)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)rx->skb->data;
|
|
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
|
|
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
|
|
+ const struct ieee80211_sta_he_cap *hecap;
|
|
+ struct ieee80211_supported_band *sband;
|
|
+
|
|
+ /* TWT actions are only supported in AP for the moment */
|
|
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
|
|
+ return false;
|
|
+
|
|
+ if (!rx->local->ops->add_twt_setup)
|
|
+ return false;
|
|
+
|
|
+ sband = rx->local->hw.wiphy->bands[status->band];
|
|
+ hecap = ieee80211_get_he_iftype_cap(sband,
|
|
+ ieee80211_vif_type_p2p(&sdata->vif));
|
|
+ if (!hecap)
|
|
+ return false;
|
|
+
|
|
+ if (!(hecap->he_cap_elem.mac_cap_info[0] &
|
|
+ IEEE80211_HE_MAC_CAP0_TWT_RES))
|
|
+ return false;
|
|
+
|
|
+ if (!rx->sta)
|
|
+ return false;
|
|
+
|
|
+ switch (mgmt->u.action.u.s1g.action_code) {
|
|
+ case WLAN_S1G_TWT_SETUP: {
|
|
+ struct ieee80211_twt_setup *twt;
|
|
+
|
|
+ if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE +
|
|
+ 1 + /* action code */
|
|
+ sizeof(struct ieee80211_twt_setup) +
|
|
+ 2 /* TWT req_type agrt */)
|
|
+ break;
|
|
+
|
|
+ twt = (void *)mgmt->u.action.u.s1g.variable;
|
|
+ if (twt->element_id != WLAN_EID_S1G_TWT)
|
|
+ break;
|
|
+
|
|
+ if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE +
|
|
+ 4 + /* action code + token + tlv */
|
|
+ twt->length)
|
|
+ break;
|
|
+
|
|
+ return true; /* queue the frame */
|
|
+ }
|
|
+ case WLAN_S1G_TWT_TEARDOWN:
|
|
+ if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE + 2)
|
|
+ break;
|
|
+
|
|
+ return true; /* queue the frame */
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
static ieee80211_rx_result debug_noinline
|
|
ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
|
{
|
|
@@ -3489,6 +3551,17 @@ ieee80211_rx_h_action(struct ieee80211_r
|
|
!mesh_path_sel_is_hwmp(sdata))
|
|
break;
|
|
goto queue;
|
|
+ case WLAN_CATEGORY_S1G:
|
|
+ switch (mgmt->u.action.u.s1g.action_code) {
|
|
+ case WLAN_S1G_TWT_SETUP:
|
|
+ case WLAN_S1G_TWT_TEARDOWN:
|
|
+ if (ieee80211_process_rx_twt_action(rx))
|
|
+ goto queue;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
}
|
|
|
|
return RX_CONTINUE;
|
|
--- a/net/mac80211/s1g.c
|
|
+++ b/net/mac80211/s1g.c
|
|
@@ -6,6 +6,7 @@
|
|
#include <linux/ieee80211.h>
|
|
#include <net/mac80211.h>
|
|
#include "ieee80211_i.h"
|
|
+#include "driver-ops.h"
|
|
|
|
void ieee80211_s1g_sta_rate_init(struct sta_info *sta)
|
|
{
|
|
@@ -14,3 +15,182 @@ void ieee80211_s1g_sta_rate_init(struct
|
|
sta->rx_stats.last_rate =
|
|
STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_S1G);
|
|
}
|
|
+
|
|
+bool ieee80211_s1g_is_twt_setup(struct sk_buff *skb)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
|
|
+
|
|
+ if (likely(!ieee80211_is_action(mgmt->frame_control)))
|
|
+ return false;
|
|
+
|
|
+ if (likely(mgmt->u.action.category != WLAN_CATEGORY_S1G))
|
|
+ return false;
|
|
+
|
|
+ return mgmt->u.action.u.s1g.action_code == WLAN_S1G_TWT_SETUP;
|
|
+}
|
|
+
|
|
+static void
|
|
+ieee80211_s1g_send_twt_setup(struct ieee80211_sub_if_data *sdata, const u8 *da,
|
|
+ const u8 *bssid, struct ieee80211_twt_setup *twt)
|
|
+{
|
|
+ int len = IEEE80211_MIN_ACTION_SIZE + 4 + twt->length;
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
+ struct ieee80211_mgmt *mgmt;
|
|
+ struct sk_buff *skb;
|
|
+
|
|
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
|
|
+ if (!skb)
|
|
+ return;
|
|
+
|
|
+ skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
+ mgmt = skb_put_zero(skb, len);
|
|
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
+ IEEE80211_STYPE_ACTION);
|
|
+ memcpy(mgmt->da, da, ETH_ALEN);
|
|
+ memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
|
+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
|
|
+
|
|
+ mgmt->u.action.category = WLAN_CATEGORY_S1G;
|
|
+ mgmt->u.action.u.s1g.action_code = WLAN_S1G_TWT_SETUP;
|
|
+ memcpy(mgmt->u.action.u.s1g.variable, twt, 3 + twt->length);
|
|
+
|
|
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
|
|
+ IEEE80211_TX_INTFL_MLME_CONN_TX |
|
|
+ IEEE80211_TX_CTL_REQ_TX_STATUS;
|
|
+ ieee80211_tx_skb(sdata, skb);
|
|
+}
|
|
+
|
|
+static void
|
|
+ieee80211_s1g_send_twt_teardown(struct ieee80211_sub_if_data *sdata,
|
|
+ const u8 *da, const u8 *bssid, u8 flowid)
|
|
+{
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
+ struct ieee80211_mgmt *mgmt;
|
|
+ struct sk_buff *skb;
|
|
+ u8 *id;
|
|
+
|
|
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
|
+ IEEE80211_MIN_ACTION_SIZE + 2);
|
|
+ if (!skb)
|
|
+ return;
|
|
+
|
|
+ skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
+ mgmt = skb_put_zero(skb, IEEE80211_MIN_ACTION_SIZE + 2);
|
|
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
+ IEEE80211_STYPE_ACTION);
|
|
+ memcpy(mgmt->da, da, ETH_ALEN);
|
|
+ memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
|
+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
|
|
+
|
|
+ mgmt->u.action.category = WLAN_CATEGORY_S1G;
|
|
+ mgmt->u.action.u.s1g.action_code = WLAN_S1G_TWT_TEARDOWN;
|
|
+ id = (u8 *)mgmt->u.action.u.s1g.variable;
|
|
+ *id = flowid;
|
|
+
|
|
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
|
|
+ IEEE80211_TX_CTL_REQ_TX_STATUS;
|
|
+ ieee80211_tx_skb(sdata, skb);
|
|
+}
|
|
+
|
|
+static void
|
|
+ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
|
|
+ struct sta_info *sta, struct sk_buff *skb)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
|
|
+ struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
|
|
+ struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
|
|
+
|
|
+ twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
|
|
+
|
|
+ /* broadcast TWT not supported yet */
|
|
+ if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) {
|
|
+ le16p_replace_bits(&twt_agrt->req_type,
|
|
+ TWT_SETUP_CMD_REJECT,
|
|
+ IEEE80211_TWT_REQTYPE_SETUP_CMD);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ drv_add_twt_setup(sdata->local, sdata, &sta->sta, twt);
|
|
+out:
|
|
+ ieee80211_s1g_send_twt_setup(sdata, mgmt->sa, sdata->vif.addr, twt);
|
|
+}
|
|
+
|
|
+static void
|
|
+ieee80211_s1g_rx_twt_teardown(struct ieee80211_sub_if_data *sdata,
|
|
+ struct sta_info *sta, struct sk_buff *skb)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
|
|
+
|
|
+ drv_twt_teardown_request(sdata->local, sdata, &sta->sta,
|
|
+ mgmt->u.action.u.s1g.variable[0]);
|
|
+}
|
|
+
|
|
+static void
|
|
+ieee80211_s1g_tx_twt_setup_fail(struct ieee80211_sub_if_data *sdata,
|
|
+ struct sta_info *sta, struct sk_buff *skb)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
|
|
+ struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
|
|
+ struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
|
|
+ u8 flowid = le16_get_bits(twt_agrt->req_type,
|
|
+ IEEE80211_TWT_REQTYPE_FLOWID);
|
|
+
|
|
+ drv_twt_teardown_request(sdata->local, sdata, &sta->sta, flowid);
|
|
+
|
|
+ ieee80211_s1g_send_twt_teardown(sdata, mgmt->sa, sdata->vif.addr,
|
|
+ flowid);
|
|
+}
|
|
+
|
|
+void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
+ struct sta_info *sta;
|
|
+
|
|
+ mutex_lock(&local->sta_mtx);
|
|
+
|
|
+ sta = sta_info_get_bss(sdata, mgmt->sa);
|
|
+ if (!sta)
|
|
+ goto out;
|
|
+
|
|
+ switch (mgmt->u.action.u.s1g.action_code) {
|
|
+ case WLAN_S1G_TWT_SETUP:
|
|
+ ieee80211_s1g_rx_twt_setup(sdata, sta, skb);
|
|
+ break;
|
|
+ case WLAN_S1G_TWT_TEARDOWN:
|
|
+ ieee80211_s1g_rx_twt_teardown(sdata, sta, skb);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ mutex_unlock(&local->sta_mtx);
|
|
+}
|
|
+
|
|
+void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
+ struct sta_info *sta;
|
|
+
|
|
+ mutex_lock(&local->sta_mtx);
|
|
+
|
|
+ sta = sta_info_get_bss(sdata, mgmt->da);
|
|
+ if (!sta)
|
|
+ goto out;
|
|
+
|
|
+ switch (mgmt->u.action.u.s1g.action_code) {
|
|
+ case WLAN_S1G_TWT_SETUP:
|
|
+ /* process failed twt setup frames */
|
|
+ ieee80211_s1g_tx_twt_setup_fail(sdata, sta, skb);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ mutex_unlock(&local->sta_mtx);
|
|
+}
|
|
--- a/net/mac80211/status.c
|
|
+++ b/net/mac80211/status.c
|
|
@@ -705,13 +705,26 @@ static void ieee80211_report_used_skb(st
|
|
/* Check to see if packet is a TDLS teardown packet */
|
|
if (ieee80211_is_data(hdr->frame_control) &&
|
|
(ieee80211_get_tdls_action(skb, hdr_size) ==
|
|
- WLAN_TDLS_TEARDOWN))
|
|
+ WLAN_TDLS_TEARDOWN)) {
|
|
ieee80211_tdls_td_tx_handle(local, sdata, skb,
|
|
info->flags);
|
|
- else
|
|
+ } else if (ieee80211_s1g_is_twt_setup(skb)) {
|
|
+ if (!acked) {
|
|
+ struct sk_buff *qskb;
|
|
+
|
|
+ qskb = skb_clone(skb, GFP_ATOMIC);
|
|
+ if (qskb) {
|
|
+ skb_queue_tail(&sdata->status_queue,
|
|
+ qskb);
|
|
+ ieee80211_queue_work(&local->hw,
|
|
+ &sdata->work);
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
ieee80211_mgd_conn_tx_status(sdata,
|
|
hdr->frame_control,
|
|
acked);
|
|
+ }
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
--- a/net/mac80211/trace.h
|
|
+++ b/net/mac80211/trace.h
|
|
@@ -2804,6 +2804,73 @@ DEFINE_EVENT(sta_flag_evt, drv_sta_set_d
|
|
TP_ARGS(local, sdata, sta, enabled)
|
|
);
|
|
|
|
+TRACE_EVENT(drv_add_twt_setup,
|
|
+ TP_PROTO(struct ieee80211_local *local,
|
|
+ struct ieee80211_sta *sta,
|
|
+ struct ieee80211_twt_setup *twt,
|
|
+ struct ieee80211_twt_params *twt_agrt),
|
|
+
|
|
+ TP_ARGS(local, sta, twt, twt_agrt),
|
|
+
|
|
+ TP_STRUCT__entry(
|
|
+ LOCAL_ENTRY
|
|
+ STA_ENTRY
|
|
+ __field(u8, dialog_token)
|
|
+ __field(u8, control)
|
|
+ __field(__le16, req_type)
|
|
+ __field(__le64, twt)
|
|
+ __field(u8, duration)
|
|
+ __field(__le16, mantissa)
|
|
+ __field(u8, channel)
|
|
+ ),
|
|
+
|
|
+ TP_fast_assign(
|
|
+ LOCAL_ASSIGN;
|
|
+ STA_ASSIGN;
|
|
+ __entry->dialog_token = twt->dialog_token;
|
|
+ __entry->control = twt->control;
|
|
+ __entry->req_type = twt_agrt->req_type;
|
|
+ __entry->twt = twt_agrt->twt;
|
|
+ __entry->duration = twt_agrt->min_twt_dur;
|
|
+ __entry->mantissa = twt_agrt->mantissa;
|
|
+ __entry->channel = twt_agrt->channel;
|
|
+ ),
|
|
+
|
|
+ TP_printk(
|
|
+ LOCAL_PR_FMT STA_PR_FMT
|
|
+ " token:%d control:0x%02x req_type:0x%04x"
|
|
+ " twt:%llu duration:%d mantissa:%d channel:%d",
|
|
+ LOCAL_PR_ARG, STA_PR_ARG, __entry->dialog_token,
|
|
+ __entry->control, le16_to_cpu(__entry->req_type),
|
|
+ le64_to_cpu(__entry->twt), __entry->duration,
|
|
+ le16_to_cpu(__entry->mantissa), __entry->channel
|
|
+ )
|
|
+);
|
|
+
|
|
+TRACE_EVENT(drv_twt_teardown_request,
|
|
+ TP_PROTO(struct ieee80211_local *local,
|
|
+ struct ieee80211_sta *sta, u8 flowid),
|
|
+
|
|
+ TP_ARGS(local, sta, flowid),
|
|
+
|
|
+ TP_STRUCT__entry(
|
|
+ LOCAL_ENTRY
|
|
+ STA_ENTRY
|
|
+ __field(u8, flowid)
|
|
+ ),
|
|
+
|
|
+ TP_fast_assign(
|
|
+ LOCAL_ASSIGN;
|
|
+ STA_ASSIGN;
|
|
+ __entry->flowid = flowid;
|
|
+ ),
|
|
+
|
|
+ TP_printk(
|
|
+ LOCAL_PR_FMT STA_PR_FMT " flowid:%d",
|
|
+ LOCAL_PR_ARG, STA_PR_ARG, __entry->flowid
|
|
+ )
|
|
+);
|
|
+
|
|
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
|
|
|
|
#undef TRACE_INCLUDE_PATH
|