1// SPDX-License-Identifier: GPL-2.0
2/*
3 * S1G handling
4 * Copyright(c) 2020 Adapt-IP
5 * Copyright (C) 2023 Intel Corporation
6 */
7#include <linux/ieee80211.h>
8#include <net/mac80211.h>
9#include "ieee80211_i.h"
10#include "driver-ops.h"
11
12void ieee80211_s1g_sta_rate_init(struct sta_info *sta)
13{
14 /* avoid indicating legacy bitrates for S1G STAs */
15 sta->deflink.tx_stats.last_rate.flags |= IEEE80211_TX_RC_S1G_MCS;
16 sta->deflink.rx_stats.last_rate =
17 STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_S1G);
18}
19
20bool ieee80211_s1g_is_twt_setup(struct sk_buff *skb)
21{
22 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
23
24 if (likely(!ieee80211_is_action(mgmt->frame_control)))
25 return false;
26
27 if (likely(mgmt->u.action.category != WLAN_CATEGORY_S1G))
28 return false;
29
30 return mgmt->u.action.u.s1g.action_code == WLAN_S1G_TWT_SETUP;
31}
32
33static void
34ieee80211_s1g_send_twt_setup(struct ieee80211_sub_if_data *sdata, const u8 *da,
35 const u8 *bssid, struct ieee80211_twt_setup *twt)
36{
37 int len = IEEE80211_MIN_ACTION_SIZE + 4 + twt->length;
38 struct ieee80211_local *local = sdata->local;
39 struct ieee80211_mgmt *mgmt;
40 struct sk_buff *skb;
41
42 skb = dev_alloc_skb(length: local->hw.extra_tx_headroom + len);
43 if (!skb)
44 return;
45
46 skb_reserve(skb, len: local->hw.extra_tx_headroom);
47 mgmt = skb_put_zero(skb, len);
48 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
49 IEEE80211_STYPE_ACTION);
50 memcpy(mgmt->da, da, ETH_ALEN);
51 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
52 memcpy(mgmt->bssid, bssid, ETH_ALEN);
53
54 mgmt->u.action.category = WLAN_CATEGORY_S1G;
55 mgmt->u.action.u.s1g.action_code = WLAN_S1G_TWT_SETUP;
56 memcpy(mgmt->u.action.u.s1g.variable, twt, 3 + twt->length);
57
58 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
59 IEEE80211_TX_INTFL_MLME_CONN_TX |
60 IEEE80211_TX_CTL_REQ_TX_STATUS;
61 ieee80211_tx_skb(sdata, skb);
62}
63
64static void
65ieee80211_s1g_send_twt_teardown(struct ieee80211_sub_if_data *sdata,
66 const u8 *da, const u8 *bssid, u8 flowid)
67{
68 struct ieee80211_local *local = sdata->local;
69 struct ieee80211_mgmt *mgmt;
70 struct sk_buff *skb;
71 u8 *id;
72
73 skb = dev_alloc_skb(length: local->hw.extra_tx_headroom +
74 IEEE80211_MIN_ACTION_SIZE + 2);
75 if (!skb)
76 return;
77
78 skb_reserve(skb, len: local->hw.extra_tx_headroom);
79 mgmt = skb_put_zero(skb, IEEE80211_MIN_ACTION_SIZE + 2);
80 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
81 IEEE80211_STYPE_ACTION);
82 memcpy(mgmt->da, da, ETH_ALEN);
83 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
84 memcpy(mgmt->bssid, bssid, ETH_ALEN);
85
86 mgmt->u.action.category = WLAN_CATEGORY_S1G;
87 mgmt->u.action.u.s1g.action_code = WLAN_S1G_TWT_TEARDOWN;
88 id = (u8 *)mgmt->u.action.u.s1g.variable;
89 *id = flowid;
90
91 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
92 IEEE80211_TX_CTL_REQ_TX_STATUS;
93 ieee80211_tx_skb(sdata, skb);
94}
95
96static void
97ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
98 struct sta_info *sta, struct sk_buff *skb)
99{
100 struct ieee80211_mgmt *mgmt = (void *)skb->data;
101 struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
102 struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
103
104 twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
105
106 /* broadcast TWT not supported yet */
107 if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) {
108 twt_agrt->req_type &=
109 ~cpu_to_le16(IEEE80211_TWT_REQTYPE_SETUP_CMD);
110 twt_agrt->req_type |=
111 le16_encode_bits(v: TWT_SETUP_CMD_REJECT,
112 IEEE80211_TWT_REQTYPE_SETUP_CMD);
113 goto out;
114 }
115
116 /* TWT Information not supported yet */
117 twt->control |= IEEE80211_TWT_CONTROL_RX_DISABLED;
118
119 drv_add_twt_setup(local: sdata->local, sdata, sta: &sta->sta, twt);
120out:
121 ieee80211_s1g_send_twt_setup(sdata, da: mgmt->sa, bssid: sdata->vif.addr, twt);
122}
123
124static void
125ieee80211_s1g_rx_twt_teardown(struct ieee80211_sub_if_data *sdata,
126 struct sta_info *sta, struct sk_buff *skb)
127{
128 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
129
130 drv_twt_teardown_request(local: sdata->local, sdata, sta: &sta->sta,
131 flowid: mgmt->u.action.u.s1g.variable[0]);
132}
133
134static void
135ieee80211_s1g_tx_twt_setup_fail(struct ieee80211_sub_if_data *sdata,
136 struct sta_info *sta, struct sk_buff *skb)
137{
138 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
139 struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;
140 struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
141 u8 flowid = le16_get_bits(v: twt_agrt->req_type,
142 IEEE80211_TWT_REQTYPE_FLOWID);
143
144 drv_twt_teardown_request(local: sdata->local, sdata, sta: &sta->sta, flowid);
145
146 ieee80211_s1g_send_twt_teardown(sdata, da: mgmt->sa, bssid: sdata->vif.addr,
147 flowid);
148}
149
150void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata,
151 struct sk_buff *skb)
152{
153 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
154 struct ieee80211_local *local = sdata->local;
155 struct sta_info *sta;
156
157 lockdep_assert_wiphy(local->hw.wiphy);
158
159 sta = sta_info_get_bss(sdata, addr: mgmt->sa);
160 if (!sta)
161 return;
162
163 switch (mgmt->u.action.u.s1g.action_code) {
164 case WLAN_S1G_TWT_SETUP:
165 ieee80211_s1g_rx_twt_setup(sdata, sta, skb);
166 break;
167 case WLAN_S1G_TWT_TEARDOWN:
168 ieee80211_s1g_rx_twt_teardown(sdata, sta, skb);
169 break;
170 default:
171 break;
172 }
173}
174
175void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata,
176 struct sk_buff *skb)
177{
178 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
179 struct ieee80211_local *local = sdata->local;
180 struct sta_info *sta;
181
182 lockdep_assert_wiphy(local->hw.wiphy);
183
184 sta = sta_info_get_bss(sdata, addr: mgmt->da);
185 if (!sta)
186 return;
187
188 switch (mgmt->u.action.u.s1g.action_code) {
189 case WLAN_S1G_TWT_SETUP:
190 /* process failed twt setup frames */
191 ieee80211_s1g_tx_twt_setup_fail(sdata, sta, skb);
192 break;
193 default:
194 break;
195 }
196}
197

source code of linux/net/mac80211/s1g.c