dbd98b96ccfbf8060807411d1b4b38a9e0b58958
[openwrt/staging/robimarko.git] /
1 From: Lorenzo Bianconi <lorenzo@kernel.org>
2 Date: Mon, 23 Aug 2021 20:02:39 +0200
3 Subject: [PATCH] mac80211: introduce individual TWT support in AP mode
4
5 Introduce TWT action frames parsing support to mac80211.
6 Currently just individual TWT agreement are support in AP mode.
7 Whenever the AP receives a TWT action frame from an associated client,
8 after performing sanity checks, it will notify the underlay driver with
9 requested parameters in order to check if they are supported and if there
10 is enough room for a new agreement. The driver is expected to set the
11 agreement result and report it to mac80211.
12
13 Drivers supporting this have two new callbacks:
14 - add_twt_setup (mandatory)
15 - twt_teardown_request (optional)
16
17 mac80211 will send an action frame reply according to the result
18 reported by the driver.
19
20 Tested-by: Peter Chiu <chui-hao.chiu@mediatek.com>
21 Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
22 Link: 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]
25 Signed-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 @@ -1429,4 +1429,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 @@ -2093,6 +2094,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,16 @@ 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 + kcov_remote_start_common(skb_get_kcov_handle(skb));
174 +
175 + ieee80211_iface_process_status(sdata, skb);
176 + kfree_skb(skb);
177 +
178 + kcov_remote_stop();
179 + }
180 +
181 /* then other type-dependent work */
182 switch (sdata->vif.type) {
183 case NL80211_IFTYPE_STATION:
184 @@ -1637,6 +1677,7 @@ static void ieee80211_setup_sdata(struct
185 }
186
187 skb_queue_head_init(&sdata->skb_queue);
188 + skb_queue_head_init(&sdata->status_queue);
189 INIT_WORK(&sdata->work, ieee80211_iface_work);
190 INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
191 INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
192 --- a/net/mac80211/rx.c
193 +++ b/net/mac80211/rx.c
194 @@ -3208,6 +3208,68 @@ ieee80211_rx_h_mgmt_check(struct ieee802
195 return RX_CONTINUE;
196 }
197
198 +static bool
199 +ieee80211_process_rx_twt_action(struct ieee80211_rx_data *rx)
200 +{
201 + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)rx->skb->data;
202 + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
203 + struct ieee80211_sub_if_data *sdata = rx->sdata;
204 + const struct ieee80211_sta_he_cap *hecap;
205 + struct ieee80211_supported_band *sband;
206 +
207 + /* TWT actions are only supported in AP for the moment */
208 + if (sdata->vif.type != NL80211_IFTYPE_AP)
209 + return false;
210 +
211 + if (!rx->local->ops->add_twt_setup)
212 + return false;
213 +
214 + sband = rx->local->hw.wiphy->bands[status->band];
215 + hecap = ieee80211_get_he_iftype_cap(sband,
216 + ieee80211_vif_type_p2p(&sdata->vif));
217 + if (!hecap)
218 + return false;
219 +
220 + if (!(hecap->he_cap_elem.mac_cap_info[0] &
221 + IEEE80211_HE_MAC_CAP0_TWT_RES))
222 + return false;
223 +
224 + if (!rx->sta)
225 + return false;
226 +
227 + switch (mgmt->u.action.u.s1g.action_code) {
228 + case WLAN_S1G_TWT_SETUP: {
229 + struct ieee80211_twt_setup *twt;
230 +
231 + if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE +
232 + 1 + /* action code */
233 + sizeof(struct ieee80211_twt_setup) +
234 + 2 /* TWT req_type agrt */)
235 + break;
236 +
237 + twt = (void *)mgmt->u.action.u.s1g.variable;
238 + if (twt->element_id != WLAN_EID_S1G_TWT)
239 + break;
240 +
241 + if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE +
242 + 4 + /* action code + token + tlv */
243 + twt->length)
244 + break;
245 +
246 + return true; /* queue the frame */
247 + }
248 + case WLAN_S1G_TWT_TEARDOWN:
249 + if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE + 2)
250 + break;
251 +
252 + return true; /* queue the frame */
253 + default:
254 + break;
255 + }
256 +
257 + return false;
258 +}
259 +
260 static ieee80211_rx_result debug_noinline
261 ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
262 {
263 @@ -3487,6 +3549,17 @@ ieee80211_rx_h_action(struct ieee80211_r
264 !mesh_path_sel_is_hwmp(sdata))
265 break;
266 goto queue;
267 + case WLAN_CATEGORY_S1G:
268 + switch (mgmt->u.action.u.s1g.action_code) {
269 + case WLAN_S1G_TWT_SETUP:
270 + case WLAN_S1G_TWT_TEARDOWN:
271 + if (ieee80211_process_rx_twt_action(rx))
272 + goto queue;
273 + break;
274 + default:
275 + break;
276 + }
277 + break;
278 }
279
280 return RX_CONTINUE;
281 --- a/net/mac80211/s1g.c
282 +++ b/net/mac80211/s1g.c
283 @@ -6,6 +6,7 @@
284 #include <linux/ieee80211.h>
285 #include <net/mac80211.h>
286 #include "ieee80211_i.h"
287 +#include "driver-ops.h"
288
289 void ieee80211_s1g_sta_rate_init(struct sta_info *sta)
290 {
291 @@ -14,3 +15,182 @@ void ieee80211_s1g_sta_rate_init(struct
292 sta->rx_stats.last_rate =
293 STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_S1G);
294 }
295 +
296 +bool ieee80211_s1g_is_twt_setup(struct sk_buff *skb)
297 +{
298 + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
299 +
300 + if (likely(!ieee80211_is_action(mgmt->frame_control)))
301 + return false;
302 +
303 + if (likely(mgmt->u.action.category != WLAN_CATEGORY_S1G))
304 + return false;
305 +
306 + return mgmt->u.action.u.s1g.action_code == WLAN_S1G_TWT_SETUP;
307 +}
308 +
309 +static void
310 +ieee80211_s1g_send_twt_setup(struct ieee80211_sub_if_data *sdata, const u8 *da,
311 + const u8 *bssid, struct ieee80211_twt_setup *twt)
312 +{
313 + int len = IEEE80211_MIN_ACTION_SIZE + 4 + twt->length;
314 + struct ieee80211_local *local = sdata->local;
315 + struct ieee80211_mgmt *mgmt;
316 + struct sk_buff *skb;
317 +
318 + skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
319 + if (!skb)
320 + return;
321 +
322 + skb_reserve(skb, local->hw.extra_tx_headroom);
323 + mgmt = skb_put_zero(skb, len);
324 + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
325 + IEEE80211_STYPE_ACTION);
326 + memcpy(mgmt->da, da, ETH_ALEN);
327 + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
328 + memcpy(mgmt->bssid, bssid, ETH_ALEN);
329 +
330 + mgmt->u.action.category = WLAN_CATEGORY_S1G;
331 + mgmt->u.action.u.s1g.action_code = WLAN_S1G_TWT_SETUP;
332 + memcpy(mgmt->u.action.u.s1g.variable, twt, 3 + twt->length);
333 +
334 + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
335 + IEEE80211_TX_INTFL_MLME_CONN_TX |
336 + IEEE80211_TX_CTL_REQ_TX_STATUS;
337 + ieee80211_tx_skb(sdata, skb);
338 +}
339 +
340 +static void
341 +ieee80211_s1g_send_twt_teardown(struct ieee80211_sub_if_data *sdata,
342 + const u8 *da, const u8 *bssid, u8 flowid)
343 +{
344 + struct ieee80211_local *local = sdata->local;
345 + struct ieee80211_mgmt *mgmt;
346 + struct sk_buff *skb;
347 + u8 *id;
348 +
349 + skb = dev_alloc_skb(local->hw.extra_tx_headroom +
350 + IEEE80211_MIN_ACTION_SIZE + 2);
351 + if (!skb)
352 + return;
353 +
354 + skb_reserve(skb, local->hw.extra_tx_headroom);
355 + mgmt = skb_put_zero(skb, IEEE80211_MIN_ACTION_SIZE + 2);
356 + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
357 + IEEE80211_STYPE_ACTION);
358 + memcpy(mgmt->da, da, ETH_ALEN);
359 + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
360 + memcpy(mgmt->bssid, bssid, ETH_ALEN);
361 +
362 + mgmt->u.action.category = WLAN_CATEGORY_S1G;
363 + mgmt->u.action.u.s1g.action_code = WLAN_S1G_TWT_TEARDOWN;
364 + id = (u8 *)mgmt->u.action.u.s1g.variable;
365 + *id = flowid;
366 +
367 + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
368 + IEEE80211_TX_CTL_REQ_TX_STATUS;
369 + ieee80211_tx_skb(sdata, skb);
370 +}
371 +
372 +static void
373 +ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
374 + struct sta_info *sta, struct sk_buff *skb)
375 +{
376 + struct ieee80211_mgmt *mgmt = (void *)skb->data;
377 + struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
378 + struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
379 +
380 + twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
381 +
382 + /* broadcast TWT not supported yet */
383 + if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) {
384 + le16p_replace_bits(&twt_agrt->req_type,
385 + TWT_SETUP_CMD_REJECT,
386 + IEEE80211_TWT_REQTYPE_SETUP_CMD);
387 + goto out;
388 + }
389 +
390 + drv_add_twt_setup(sdata->local, sdata, &sta->sta, twt);
391 +out:
392 + ieee80211_s1g_send_twt_setup(sdata, mgmt->sa, sdata->vif.addr, twt);
393 +}
394 +
395 +static void
396 +ieee80211_s1g_rx_twt_teardown(struct ieee80211_sub_if_data *sdata,
397 + struct sta_info *sta, struct sk_buff *skb)
398 +{
399 + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
400 +
401 + drv_twt_teardown_request(sdata->local, sdata, &sta->sta,
402 + mgmt->u.action.u.s1g.variable[0]);
403 +}
404 +
405 +static void
406 +ieee80211_s1g_tx_twt_setup_fail(struct ieee80211_sub_if_data *sdata,
407 + struct sta_info *sta, struct sk_buff *skb)
408 +{
409 + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
410 + struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
411 + struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
412 + u8 flowid = le16_get_bits(twt_agrt->req_type,
413 + IEEE80211_TWT_REQTYPE_FLOWID);
414 +
415 + drv_twt_teardown_request(sdata->local, sdata, &sta->sta, flowid);
416 +
417 + ieee80211_s1g_send_twt_teardown(sdata, mgmt->sa, sdata->vif.addr,
418 + flowid);
419 +}
420 +
421 +void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata,
422 + struct sk_buff *skb)
423 +{
424 + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
425 + struct ieee80211_local *local = sdata->local;
426 + struct sta_info *sta;
427 +
428 + mutex_lock(&local->sta_mtx);
429 +
430 + sta = sta_info_get_bss(sdata, mgmt->sa);
431 + if (!sta)
432 + goto out;
433 +
434 + switch (mgmt->u.action.u.s1g.action_code) {
435 + case WLAN_S1G_TWT_SETUP:
436 + ieee80211_s1g_rx_twt_setup(sdata, sta, skb);
437 + break;
438 + case WLAN_S1G_TWT_TEARDOWN:
439 + ieee80211_s1g_rx_twt_teardown(sdata, sta, skb);
440 + break;
441 + default:
442 + break;
443 + }
444 +
445 +out:
446 + mutex_unlock(&local->sta_mtx);
447 +}
448 +
449 +void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata,
450 + struct sk_buff *skb)
451 +{
452 + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
453 + struct ieee80211_local *local = sdata->local;
454 + struct sta_info *sta;
455 +
456 + mutex_lock(&local->sta_mtx);
457 +
458 + sta = sta_info_get_bss(sdata, mgmt->da);
459 + if (!sta)
460 + goto out;
461 +
462 + switch (mgmt->u.action.u.s1g.action_code) {
463 + case WLAN_S1G_TWT_SETUP:
464 + /* process failed twt setup frames */
465 + ieee80211_s1g_tx_twt_setup_fail(sdata, sta, skb);
466 + break;
467 + default:
468 + break;
469 + }
470 +
471 +out:
472 + mutex_unlock(&local->sta_mtx);
473 +}
474 --- a/net/mac80211/status.c
475 +++ b/net/mac80211/status.c
476 @@ -705,13 +705,26 @@ static void ieee80211_report_used_skb(st
477 /* Check to see if packet is a TDLS teardown packet */
478 if (ieee80211_is_data(hdr->frame_control) &&
479 (ieee80211_get_tdls_action(skb, hdr_size) ==
480 - WLAN_TDLS_TEARDOWN))
481 + WLAN_TDLS_TEARDOWN)) {
482 ieee80211_tdls_td_tx_handle(local, sdata, skb,
483 info->flags);
484 - else
485 + } else if (ieee80211_s1g_is_twt_setup(skb)) {
486 + if (!acked) {
487 + struct sk_buff *qskb;
488 +
489 + qskb = skb_clone(skb, GFP_ATOMIC);
490 + if (qskb) {
491 + skb_queue_tail(&sdata->status_queue,
492 + qskb);
493 + ieee80211_queue_work(&local->hw,
494 + &sdata->work);
495 + }
496 + }
497 + } else {
498 ieee80211_mgd_conn_tx_status(sdata,
499 hdr->frame_control,
500 acked);
501 + }
502 }
503
504 rcu_read_unlock();
505 --- a/net/mac80211/trace.h
506 +++ b/net/mac80211/trace.h
507 @@ -2804,6 +2804,73 @@ DEFINE_EVENT(sta_flag_evt, drv_sta_set_d
508 TP_ARGS(local, sdata, sta, enabled)
509 );
510
511 +TRACE_EVENT(drv_add_twt_setup,
512 + TP_PROTO(struct ieee80211_local *local,
513 + struct ieee80211_sta *sta,
514 + struct ieee80211_twt_setup *twt,
515 + struct ieee80211_twt_params *twt_agrt),
516 +
517 + TP_ARGS(local, sta, twt, twt_agrt),
518 +
519 + TP_STRUCT__entry(
520 + LOCAL_ENTRY
521 + STA_ENTRY
522 + __field(u8, dialog_token)
523 + __field(u8, control)
524 + __field(__le16, req_type)
525 + __field(__le64, twt)
526 + __field(u8, duration)
527 + __field(__le16, mantissa)
528 + __field(u8, channel)
529 + ),
530 +
531 + TP_fast_assign(
532 + LOCAL_ASSIGN;
533 + STA_ASSIGN;
534 + __entry->dialog_token = twt->dialog_token;
535 + __entry->control = twt->control;
536 + __entry->req_type = twt_agrt->req_type;
537 + __entry->twt = twt_agrt->twt;
538 + __entry->duration = twt_agrt->min_twt_dur;
539 + __entry->mantissa = twt_agrt->mantissa;
540 + __entry->channel = twt_agrt->channel;
541 + ),
542 +
543 + TP_printk(
544 + LOCAL_PR_FMT STA_PR_FMT
545 + " token:%d control:0x%02x req_type:0x%04x"
546 + " twt:%llu duration:%d mantissa:%d channel:%d",
547 + LOCAL_PR_ARG, STA_PR_ARG, __entry->dialog_token,
548 + __entry->control, le16_to_cpu(__entry->req_type),
549 + le64_to_cpu(__entry->twt), __entry->duration,
550 + le16_to_cpu(__entry->mantissa), __entry->channel
551 + )
552 +);
553 +
554 +TRACE_EVENT(drv_twt_teardown_request,
555 + TP_PROTO(struct ieee80211_local *local,
556 + struct ieee80211_sta *sta, u8 flowid),
557 +
558 + TP_ARGS(local, sta, flowid),
559 +
560 + TP_STRUCT__entry(
561 + LOCAL_ENTRY
562 + STA_ENTRY
563 + __field(u8, flowid)
564 + ),
565 +
566 + TP_fast_assign(
567 + LOCAL_ASSIGN;
568 + STA_ASSIGN;
569 + __entry->flowid = flowid;
570 + ),
571 +
572 + TP_printk(
573 + LOCAL_PR_FMT STA_PR_FMT " flowid:%d",
574 + LOCAL_PR_ARG, STA_PR_ARG, __entry->flowid
575 + )
576 +);
577 +
578 #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
579
580 #undef TRACE_INCLUDE_PATH