xref: /linux/net/mac80211/s1g.c (revision 2bee2e6c983baa3605765621f26173ff0fa40365)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * S1G handling
4  * Copyright(c) 2020 Adapt-IP
5  * Copyright (C) 2023, 2026 Intel Corporation
6  */
7 #include <linux/ieee80211.h>
8 #include <net/mac80211.h>
9 #include "ieee80211_i.h"
10 #include "driver-ops.h"
11 
12 void 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 
20 bool 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.action_code == WLAN_S1G_TWT_SETUP;
31 }
32 
33 static void
34 ieee80211_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(s1g) + 3 + 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(local->hw.extra_tx_headroom + len);
43 	if (!skb)
44 		return;
45 
46 	skb_reserve(skb, 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.action_code = WLAN_S1G_TWT_SETUP;
56 	memcpy(mgmt->u.action.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 
64 static void
65 ieee80211_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(local->hw.extra_tx_headroom +
74 			    IEEE80211_MIN_ACTION_SIZE(s1g) + 1);
75 	if (!skb)
76 		return;
77 
78 	skb_reserve(skb, local->hw.extra_tx_headroom);
79 	mgmt = skb_put_zero(skb, IEEE80211_MIN_ACTION_SIZE(s1g) + 1);
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.action_code = WLAN_S1G_TWT_TEARDOWN;
88 	id = (u8 *)mgmt->u.action.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 
96 static void
97 ieee80211_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.s1g.variable;
102 	struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
103 
104 	if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) &&
105 	    twt->length < sizeof(twt->control) + sizeof(*twt_agrt))
106 		return;
107 
108 	twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
109 
110 	/* broadcast TWT not supported yet */
111 	if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) {
112 		twt_agrt->req_type &=
113 			~cpu_to_le16(IEEE80211_TWT_REQTYPE_SETUP_CMD);
114 		twt_agrt->req_type |=
115 			le16_encode_bits(TWT_SETUP_CMD_REJECT,
116 					 IEEE80211_TWT_REQTYPE_SETUP_CMD);
117 		goto out;
118 	}
119 
120 	/* TWT Information not supported yet */
121 	twt->control |= IEEE80211_TWT_CONTROL_RX_DISABLED;
122 
123 	drv_add_twt_setup(sdata->local, sdata, &sta->sta, twt);
124 out:
125 	ieee80211_s1g_send_twt_setup(sdata, mgmt->sa, sdata->vif.addr, twt);
126 }
127 
128 static void
129 ieee80211_s1g_rx_twt_teardown(struct ieee80211_sub_if_data *sdata,
130 			      struct sta_info *sta, struct sk_buff *skb)
131 {
132 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
133 
134 	drv_twt_teardown_request(sdata->local, sdata, &sta->sta,
135 				 mgmt->u.action.s1g.variable[0]);
136 }
137 
138 static void
139 ieee80211_s1g_tx_twt_setup_fail(struct ieee80211_sub_if_data *sdata,
140 				struct sta_info *sta, struct sk_buff *skb)
141 {
142 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
143 	struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.s1g.variable;
144 	struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
145 	u8 flowid = le16_get_bits(twt_agrt->req_type,
146 				  IEEE80211_TWT_REQTYPE_FLOWID);
147 
148 	drv_twt_teardown_request(sdata->local, sdata, &sta->sta, flowid);
149 
150 	ieee80211_s1g_send_twt_teardown(sdata, mgmt->sa, sdata->vif.addr,
151 					flowid);
152 }
153 
154 void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata,
155 				 struct sk_buff *skb)
156 {
157 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
158 	struct ieee80211_local *local = sdata->local;
159 	struct sta_info *sta;
160 
161 	lockdep_assert_wiphy(local->hw.wiphy);
162 
163 	sta = sta_info_get_bss(sdata, mgmt->sa);
164 	if (!sta)
165 		return;
166 
167 	switch (mgmt->u.action.action_code) {
168 	case WLAN_S1G_TWT_SETUP:
169 		ieee80211_s1g_rx_twt_setup(sdata, sta, skb);
170 		break;
171 	case WLAN_S1G_TWT_TEARDOWN:
172 		ieee80211_s1g_rx_twt_teardown(sdata, sta, skb);
173 		break;
174 	default:
175 		break;
176 	}
177 }
178 
179 void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata,
180 				     struct sk_buff *skb)
181 {
182 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
183 	struct ieee80211_local *local = sdata->local;
184 	struct sta_info *sta;
185 
186 	lockdep_assert_wiphy(local->hw.wiphy);
187 
188 	sta = sta_info_get_bss(sdata, mgmt->da);
189 	if (!sta)
190 		return;
191 
192 	switch (mgmt->u.action.action_code) {
193 	case WLAN_S1G_TWT_SETUP:
194 		/* process failed twt setup frames */
195 		ieee80211_s1g_tx_twt_setup_fail(sdata, sta, skb);
196 		break;
197 	default:
198 		break;
199 	}
200 }
201 
202 void ieee80211_s1g_cap_to_sta_s1g_cap(struct ieee80211_sub_if_data *sdata,
203 				      const struct ieee80211_s1g_cap *s1g_cap_ie,
204 				      struct link_sta_info *link_sta)
205 {
206 	struct ieee80211_sta_s1g_cap *s1g_cap = &link_sta->pub->s1g_cap;
207 
208 	memset(s1g_cap, 0, sizeof(*s1g_cap));
209 
210 	memcpy(s1g_cap->cap, s1g_cap_ie->capab_info, sizeof(s1g_cap->cap));
211 	memcpy(s1g_cap->nss_mcs, s1g_cap_ie->supp_mcs_nss,
212 	       sizeof(s1g_cap->nss_mcs));
213 
214 	s1g_cap->s1g = true;
215 
216 	/* Maximum MPDU length is 1 bit for S1G */
217 	if (s1g_cap->cap[3] & S1G_CAP3_MAX_MPDU_LEN) {
218 		link_sta->pub->agg.max_amsdu_len =
219 			IEEE80211_MAX_MPDU_LEN_VHT_7991;
220 	} else {
221 		link_sta->pub->agg.max_amsdu_len =
222 			IEEE80211_MAX_MPDU_LEN_VHT_3895;
223 	}
224 
225 	ieee80211_sta_recalc_aggregates(&link_sta->sta->sta);
226 }
227 
228 bool ieee80211_s1g_use_ndp_ba(const struct ieee80211_sub_if_data *sdata,
229 			      const struct sta_info *sta)
230 {
231 	return sdata->vif.cfg.s1g &&
232 		ieee80211_hw_check(&sdata->local->hw, SUPPORTS_NDP_BLOCKACK) &&
233 		(sta && sta->sta.deflink.s1g_cap.s1g);
234 }
235