blob: 973d6650021f3bb3a15e67cfc77ff7fc4e2b2cf1 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From: Lorenzo Bianconi <lorenzo@kernel.org>
2Date: Mon, 23 Aug 2021 20:02:39 +0200
3Subject: [PATCH] mac80211: introduce individual TWT support in AP mode
4
5Introduce TWT action frames parsing support to mac80211.
6Currently just individual TWT agreement are support in AP mode.
7Whenever the AP receives a TWT action frame from an associated client,
8after performing sanity checks, it will notify the underlay driver with
9requested parameters in order to check if they are supported and if there
10is enough room for a new agreement. The driver is expected to set the
11agreement result and report it to mac80211.
12
13Drivers supporting this have two new callbacks:
14 - add_twt_setup (mandatory)
15 - twt_teardown_request (optional)
16
17mac80211 will send an action frame reply according to the result
18reported by the driver.
19
20Tested-by: Peter Chiu <chui-hao.chiu@mediatek.com>
21Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
22Link: https://lore.kernel.org/r/257512f2e22ba42b9f2624942a128dd8f141de4b.1629741512.git.lorenzo@kernel.org
23[use le16p_replace_bits(), minor cleanups, use (void *) casts,
24 fix to use ieee80211_get_he_iftype_cap() correctly]
25Signed-off-by: Johannes Berg <johannes.berg@intel.com>
26---
27
28--- a/include/net/mac80211.h
29+++ b/include/net/mac80211.h
30@@ -4219,6 +4219,11 @@ struct ieee80211_ops {
31 void (*sta_set_decap_offload)(struct ieee80211_hw *hw,
32 struct ieee80211_vif *vif,
33 struct ieee80211_sta *sta, bool enabled);
34+ void (*add_twt_setup)(struct ieee80211_hw *hw,
35+ struct ieee80211_sta *sta,
36+ struct ieee80211_twt_setup *twt);
37+ void (*twt_teardown_request)(struct ieee80211_hw *hw,
38+ struct ieee80211_sta *sta, u8 flowid);
39 };
40
41 /**
42--- a/net/mac80211/driver-ops.h
43+++ b/net/mac80211/driver-ops.h
44@@ -1432,4 +1432,40 @@ static inline void drv_sta_set_decap_off
45 trace_drv_return_void(local);
46 }
47
48+static inline void drv_add_twt_setup(struct ieee80211_local *local,
49+ struct ieee80211_sub_if_data *sdata,
50+ struct ieee80211_sta *sta,
51+ struct ieee80211_twt_setup *twt)
52+{
53+ struct ieee80211_twt_params *twt_agrt;
54+
55+ might_sleep();
56+
57+ if (!check_sdata_in_driver(sdata))
58+ return;
59+
60+ twt_agrt = (void *)twt->params;
61+
62+ trace_drv_add_twt_setup(local, sta, twt, twt_agrt);
63+ local->ops->add_twt_setup(&local->hw, sta, twt);
64+ trace_drv_return_void(local);
65+}
66+
67+static inline void drv_twt_teardown_request(struct ieee80211_local *local,
68+ struct ieee80211_sub_if_data *sdata,
69+ struct ieee80211_sta *sta,
70+ u8 flowid)
71+{
72+ might_sleep();
73+ if (!check_sdata_in_driver(sdata))
74+ return;
75+
76+ if (!local->ops->twt_teardown_request)
77+ return;
78+
79+ trace_drv_twt_teardown_request(local, sta, flowid);
80+ local->ops->twt_teardown_request(&local->hw, sta, flowid);
81+ trace_drv_return_void(local);
82+}
83+
84 #endif /* __MAC80211_DRIVER_OPS */
85--- a/net/mac80211/ieee80211_i.h
86+++ b/net/mac80211/ieee80211_i.h
87@@ -954,6 +954,7 @@ struct ieee80211_sub_if_data {
88
89 struct work_struct work;
90 struct sk_buff_head skb_queue;
91+ struct sk_buff_head status_queue;
92
93 u8 needed_rx_chains;
94 enum ieee80211_smps_mode smps_mode;
95@@ -2098,6 +2099,11 @@ ieee80211_he_op_ie_to_bss_conf(struct ie
96
97 /* S1G */
98 void ieee80211_s1g_sta_rate_init(struct sta_info *sta);
99+bool ieee80211_s1g_is_twt_setup(struct sk_buff *skb);
100+void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata,
101+ struct sk_buff *skb);
102+void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata,
103+ struct sk_buff *skb);
104
105 /* Spectrum management */
106 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
107--- a/net/mac80211/iface.c
108+++ b/net/mac80211/iface.c
109@@ -563,6 +563,7 @@ static void ieee80211_do_stop(struct iee
110 */
111 ieee80211_free_keys(sdata, true);
112 skb_queue_purge(&sdata->skb_queue);
113+ skb_queue_purge(&sdata->status_queue);
114 }
115
116 spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
117@@ -1070,6 +1071,7 @@ int ieee80211_add_virtual_monitor(struct
118 }
119
120 skb_queue_head_init(&sdata->skb_queue);
121+ skb_queue_head_init(&sdata->status_queue);
122 INIT_WORK(&sdata->work, ieee80211_iface_work);
123
124 return 0;
125@@ -1442,6 +1444,24 @@ static void ieee80211_if_setup_no_queue(
126 #endif
127 }
128
129+static void ieee80211_iface_process_status(struct ieee80211_sub_if_data *sdata,
130+ struct sk_buff *skb)
131+{
132+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
133+
134+ if (ieee80211_is_action(mgmt->frame_control) &&
135+ mgmt->u.action.category == WLAN_CATEGORY_S1G) {
136+ switch (mgmt->u.action.u.s1g.action_code) {
137+ case WLAN_S1G_TWT_TEARDOWN:
138+ case WLAN_S1G_TWT_SETUP:
139+ ieee80211_s1g_status_twt_action(sdata, skb);
140+ break;
141+ default:
142+ break;
143+ }
144+ }
145+}
146+
147 static void ieee80211_iface_work(struct work_struct *work)
148 {
149 struct ieee80211_sub_if_data *sdata =
150@@ -1519,6 +1539,16 @@ static void ieee80211_iface_work(struct
151 WARN_ON(1);
152 break;
153 }
154+ } else if (ieee80211_is_action(mgmt->frame_control) &&
155+ mgmt->u.action.category == WLAN_CATEGORY_S1G) {
156+ switch (mgmt->u.action.u.s1g.action_code) {
157+ case WLAN_S1G_TWT_TEARDOWN:
158+ case WLAN_S1G_TWT_SETUP:
159+ ieee80211_s1g_rx_twt_action(sdata, skb);
160+ break;
161+ default:
162+ break;
163+ }
164 } else if (ieee80211_is_ext(mgmt->frame_control)) {
165 if (sdata->vif.type == NL80211_IFTYPE_STATION)
166 ieee80211_sta_rx_queued_ext(sdata, skb);
167@@ -1574,6 +1604,12 @@ static void ieee80211_iface_work(struct
168 kfree_skb(skb);
169 }
170
171+ /* process status queue */
172+ while ((skb = skb_dequeue(&sdata->status_queue))) {
173+ ieee80211_iface_process_status(sdata, skb);
174+ kfree_skb(skb);
175+ }
176+
177 /* then other type-dependent work */
178 switch (sdata->vif.type) {
179 case NL80211_IFTYPE_STATION:
180@@ -1637,6 +1673,7 @@ static void ieee80211_setup_sdata(struct
181 }
182
183 skb_queue_head_init(&sdata->skb_queue);
184+ skb_queue_head_init(&sdata->status_queue);
185 INIT_WORK(&sdata->work, ieee80211_iface_work);
186 INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
187 INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
188--- a/net/mac80211/rx.c
189+++ b/net/mac80211/rx.c
190@@ -3211,6 +3211,68 @@ ieee80211_rx_h_mgmt_check(struct ieee802
191 return RX_CONTINUE;
192 }
193
194+static bool
195+ieee80211_process_rx_twt_action(struct ieee80211_rx_data *rx)
196+{
197+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)rx->skb->data;
198+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
199+ struct ieee80211_sub_if_data *sdata = rx->sdata;
200+ const struct ieee80211_sta_he_cap *hecap;
201+ struct ieee80211_supported_band *sband;
202+
203+ /* TWT actions are only supported in AP for the moment */
204+ if (sdata->vif.type != NL80211_IFTYPE_AP)
205+ return false;
206+
207+ if (!rx->local->ops->add_twt_setup)
208+ return false;
209+
210+ sband = rx->local->hw.wiphy->bands[status->band];
211+ hecap = ieee80211_get_he_iftype_cap(sband,
212+ ieee80211_vif_type_p2p(&sdata->vif));
213+ if (!hecap)
214+ return false;
215+
216+ if (!(hecap->he_cap_elem.mac_cap_info[0] &
217+ IEEE80211_HE_MAC_CAP0_TWT_RES))
218+ return false;
219+
220+ if (!rx->sta)
221+ return false;
222+
223+ switch (mgmt->u.action.u.s1g.action_code) {
224+ case WLAN_S1G_TWT_SETUP: {
225+ struct ieee80211_twt_setup *twt;
226+
227+ if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE +
228+ 1 + /* action code */
229+ sizeof(struct ieee80211_twt_setup) +
230+ 2 /* TWT req_type agrt */)
231+ break;
232+
233+ twt = (void *)mgmt->u.action.u.s1g.variable;
234+ if (twt->element_id != WLAN_EID_S1G_TWT)
235+ break;
236+
237+ if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE +
238+ 4 + /* action code + token + tlv */
239+ twt->length)
240+ break;
241+
242+ return true; /* queue the frame */
243+ }
244+ case WLAN_S1G_TWT_TEARDOWN:
245+ if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE + 2)
246+ break;
247+
248+ return true; /* queue the frame */
249+ default:
250+ break;
251+ }
252+
253+ return false;
254+}
255+
256 static ieee80211_rx_result debug_noinline
257 ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
258 {
259@@ -3490,6 +3552,17 @@ ieee80211_rx_h_action(struct ieee80211_r
260 !mesh_path_sel_is_hwmp(sdata))
261 break;
262 goto queue;
263+ case WLAN_CATEGORY_S1G:
264+ switch (mgmt->u.action.u.s1g.action_code) {
265+ case WLAN_S1G_TWT_SETUP:
266+ case WLAN_S1G_TWT_TEARDOWN:
267+ if (ieee80211_process_rx_twt_action(rx))
268+ goto queue;
269+ break;
270+ default:
271+ break;
272+ }
273+ break;
274 }
275
276 return RX_CONTINUE;
277--- a/net/mac80211/s1g.c
278+++ b/net/mac80211/s1g.c
279@@ -6,6 +6,7 @@
280 #include <linux/ieee80211.h>
281 #include <net/mac80211.h>
282 #include "ieee80211_i.h"
283+#include "driver-ops.h"
284
285 void ieee80211_s1g_sta_rate_init(struct sta_info *sta)
286 {
287@@ -14,3 +15,182 @@ void ieee80211_s1g_sta_rate_init(struct
288 sta->rx_stats.last_rate =
289 STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_S1G);
290 }
291+
292+bool ieee80211_s1g_is_twt_setup(struct sk_buff *skb)
293+{
294+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
295+
296+ if (likely(!ieee80211_is_action(mgmt->frame_control)))
297+ return false;
298+
299+ if (likely(mgmt->u.action.category != WLAN_CATEGORY_S1G))
300+ return false;
301+
302+ return mgmt->u.action.u.s1g.action_code == WLAN_S1G_TWT_SETUP;
303+}
304+
305+static void
306+ieee80211_s1g_send_twt_setup(struct ieee80211_sub_if_data *sdata, const u8 *da,
307+ const u8 *bssid, struct ieee80211_twt_setup *twt)
308+{
309+ int len = IEEE80211_MIN_ACTION_SIZE + 4 + twt->length;
310+ struct ieee80211_local *local = sdata->local;
311+ struct ieee80211_mgmt *mgmt;
312+ struct sk_buff *skb;
313+
314+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
315+ if (!skb)
316+ return;
317+
318+ skb_reserve(skb, local->hw.extra_tx_headroom);
319+ mgmt = skb_put_zero(skb, len);
320+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
321+ IEEE80211_STYPE_ACTION);
322+ memcpy(mgmt->da, da, ETH_ALEN);
323+ memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
324+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
325+
326+ mgmt->u.action.category = WLAN_CATEGORY_S1G;
327+ mgmt->u.action.u.s1g.action_code = WLAN_S1G_TWT_SETUP;
328+ memcpy(mgmt->u.action.u.s1g.variable, twt, 3 + twt->length);
329+
330+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
331+ IEEE80211_TX_INTFL_MLME_CONN_TX |
332+ IEEE80211_TX_CTL_REQ_TX_STATUS;
333+ ieee80211_tx_skb(sdata, skb);
334+}
335+
336+static void
337+ieee80211_s1g_send_twt_teardown(struct ieee80211_sub_if_data *sdata,
338+ const u8 *da, const u8 *bssid, u8 flowid)
339+{
340+ struct ieee80211_local *local = sdata->local;
341+ struct ieee80211_mgmt *mgmt;
342+ struct sk_buff *skb;
343+ u8 *id;
344+
345+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
346+ IEEE80211_MIN_ACTION_SIZE + 2);
347+ if (!skb)
348+ return;
349+
350+ skb_reserve(skb, local->hw.extra_tx_headroom);
351+ mgmt = skb_put_zero(skb, IEEE80211_MIN_ACTION_SIZE + 2);
352+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
353+ IEEE80211_STYPE_ACTION);
354+ memcpy(mgmt->da, da, ETH_ALEN);
355+ memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
356+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
357+
358+ mgmt->u.action.category = WLAN_CATEGORY_S1G;
359+ mgmt->u.action.u.s1g.action_code = WLAN_S1G_TWT_TEARDOWN;
360+ id = (u8 *)mgmt->u.action.u.s1g.variable;
361+ *id = flowid;
362+
363+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
364+ IEEE80211_TX_CTL_REQ_TX_STATUS;
365+ ieee80211_tx_skb(sdata, skb);
366+}
367+
368+static void
369+ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
370+ struct sta_info *sta, struct sk_buff *skb)
371+{
372+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
373+ struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
374+ struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
375+
376+ twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
377+
378+ /* broadcast TWT not supported yet */
379+ if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) {
380+ le16p_replace_bits(&twt_agrt->req_type,
381+ TWT_SETUP_CMD_REJECT,
382+ IEEE80211_TWT_REQTYPE_SETUP_CMD);
383+ goto out;
384+ }
385+
386+ drv_add_twt_setup(sdata->local, sdata, &sta->sta, twt);
387+out:
388+ ieee80211_s1g_send_twt_setup(sdata, mgmt->sa, sdata->vif.addr, twt);
389+}
390+
391+static void
392+ieee80211_s1g_rx_twt_teardown(struct ieee80211_sub_if_data *sdata,
393+ struct sta_info *sta, struct sk_buff *skb)
394+{
395+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
396+
397+ drv_twt_teardown_request(sdata->local, sdata, &sta->sta,
398+ mgmt->u.action.u.s1g.variable[0]);
399+}
400+
401+static void
402+ieee80211_s1g_tx_twt_setup_fail(struct ieee80211_sub_if_data *sdata,
403+ struct sta_info *sta, struct sk_buff *skb)
404+{
405+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
406+ struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
407+ struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
408+ u8 flowid = le16_get_bits(twt_agrt->req_type,
409+ IEEE80211_TWT_REQTYPE_FLOWID);
410+
411+ drv_twt_teardown_request(sdata->local, sdata, &sta->sta, flowid);
412+
413+ ieee80211_s1g_send_twt_teardown(sdata, mgmt->sa, sdata->vif.addr,
414+ flowid);
415+}
416+
417+void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata,
418+ struct sk_buff *skb)
419+{
420+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
421+ struct ieee80211_local *local = sdata->local;
422+ struct sta_info *sta;
423+
424+ mutex_lock(&local->sta_mtx);
425+
426+ sta = sta_info_get_bss(sdata, mgmt->sa);
427+ if (!sta)
428+ goto out;
429+
430+ switch (mgmt->u.action.u.s1g.action_code) {
431+ case WLAN_S1G_TWT_SETUP:
432+ ieee80211_s1g_rx_twt_setup(sdata, sta, skb);
433+ break;
434+ case WLAN_S1G_TWT_TEARDOWN:
435+ ieee80211_s1g_rx_twt_teardown(sdata, sta, skb);
436+ break;
437+ default:
438+ break;
439+ }
440+
441+out:
442+ mutex_unlock(&local->sta_mtx);
443+}
444+
445+void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata,
446+ struct sk_buff *skb)
447+{
448+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
449+ struct ieee80211_local *local = sdata->local;
450+ struct sta_info *sta;
451+
452+ mutex_lock(&local->sta_mtx);
453+
454+ sta = sta_info_get_bss(sdata, mgmt->da);
455+ if (!sta)
456+ goto out;
457+
458+ switch (mgmt->u.action.u.s1g.action_code) {
459+ case WLAN_S1G_TWT_SETUP:
460+ /* process failed twt setup frames */
461+ ieee80211_s1g_tx_twt_setup_fail(sdata, sta, skb);
462+ break;
463+ default:
464+ break;
465+ }
466+
467+out:
468+ mutex_unlock(&local->sta_mtx);
469+}
470--- a/net/mac80211/status.c
471+++ b/net/mac80211/status.c
472@@ -705,13 +705,26 @@ static void ieee80211_report_used_skb(st
473 /* Check to see if packet is a TDLS teardown packet */
474 if (ieee80211_is_data(hdr->frame_control) &&
475 (ieee80211_get_tdls_action(skb, hdr_size) ==
476- WLAN_TDLS_TEARDOWN))
477+ WLAN_TDLS_TEARDOWN)) {
478 ieee80211_tdls_td_tx_handle(local, sdata, skb,
479 info->flags);
480- else
481+ } else if (ieee80211_s1g_is_twt_setup(skb)) {
482+ if (!acked) {
483+ struct sk_buff *qskb;
484+
485+ qskb = skb_clone(skb, GFP_ATOMIC);
486+ if (qskb) {
487+ skb_queue_tail(&sdata->status_queue,
488+ qskb);
489+ ieee80211_queue_work(&local->hw,
490+ &sdata->work);
491+ }
492+ }
493+ } else {
494 ieee80211_mgd_conn_tx_status(sdata,
495 hdr->frame_control,
496 acked);
497+ }
498 }
499
500 rcu_read_unlock();
501--- a/net/mac80211/trace.h
502+++ b/net/mac80211/trace.h
503@@ -2804,6 +2804,73 @@ DEFINE_EVENT(sta_flag_evt, drv_sta_set_d
504 TP_ARGS(local, sdata, sta, enabled)
505 );
506
507+TRACE_EVENT(drv_add_twt_setup,
508+ TP_PROTO(struct ieee80211_local *local,
509+ struct ieee80211_sta *sta,
510+ struct ieee80211_twt_setup *twt,
511+ struct ieee80211_twt_params *twt_agrt),
512+
513+ TP_ARGS(local, sta, twt, twt_agrt),
514+
515+ TP_STRUCT__entry(
516+ LOCAL_ENTRY
517+ STA_ENTRY
518+ __field(u8, dialog_token)
519+ __field(u8, control)
520+ __field(__le16, req_type)
521+ __field(__le64, twt)
522+ __field(u8, duration)
523+ __field(__le16, mantissa)
524+ __field(u8, channel)
525+ ),
526+
527+ TP_fast_assign(
528+ LOCAL_ASSIGN;
529+ STA_ASSIGN;
530+ __entry->dialog_token = twt->dialog_token;
531+ __entry->control = twt->control;
532+ __entry->req_type = twt_agrt->req_type;
533+ __entry->twt = twt_agrt->twt;
534+ __entry->duration = twt_agrt->min_twt_dur;
535+ __entry->mantissa = twt_agrt->mantissa;
536+ __entry->channel = twt_agrt->channel;
537+ ),
538+
539+ TP_printk(
540+ LOCAL_PR_FMT STA_PR_FMT
541+ " token:%d control:0x%02x req_type:0x%04x"
542+ " twt:%llu duration:%d mantissa:%d channel:%d",
543+ LOCAL_PR_ARG, STA_PR_ARG, __entry->dialog_token,
544+ __entry->control, le16_to_cpu(__entry->req_type),
545+ le64_to_cpu(__entry->twt), __entry->duration,
546+ le16_to_cpu(__entry->mantissa), __entry->channel
547+ )
548+);
549+
550+TRACE_EVENT(drv_twt_teardown_request,
551+ TP_PROTO(struct ieee80211_local *local,
552+ struct ieee80211_sta *sta, u8 flowid),
553+
554+ TP_ARGS(local, sta, flowid),
555+
556+ TP_STRUCT__entry(
557+ LOCAL_ENTRY
558+ STA_ENTRY
559+ __field(u8, flowid)
560+ ),
561+
562+ TP_fast_assign(
563+ LOCAL_ASSIGN;
564+ STA_ASSIGN;
565+ __entry->flowid = flowid;
566+ ),
567+
568+ TP_printk(
569+ LOCAL_PR_FMT STA_PR_FMT " flowid:%d",
570+ LOCAL_PR_ARG, STA_PR_ARG, __entry->flowid
571+ )
572+);
573+
574 #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
575
576 #undef TRACE_INCLUDE_PATH