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