File Index
Files and directories
Sign in
FILE

s1g.c

File preview with syntax highlighting.

linux-6.19.14/net/mac80211/s1g.c C
  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(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.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(local->hw.extra_tx_headroom +
 74			    IEEE80211_MIN_ACTION_SIZE + 2);
 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 + 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(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(sdata->local, sdata, &sta->sta, twt);
120out:
121	ieee80211_s1g_send_twt_setup(sdata, mgmt->sa, 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(sdata->local, sdata, &sta->sta,
131				 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(twt_agrt->req_type,
142				  IEEE80211_TWT_REQTYPE_FLOWID);
143
144	drv_twt_teardown_request(sdata->local, sdata, &sta->sta, flowid);
145
146	ieee80211_s1g_send_twt_teardown(sdata, mgmt->sa, 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, 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, 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
198void ieee80211_s1g_cap_to_sta_s1g_cap(struct ieee80211_sub_if_data *sdata,
199				      const struct ieee80211_s1g_cap *s1g_cap_ie,
200				      struct link_sta_info *link_sta)
201{
202	struct ieee80211_sta_s1g_cap *s1g_cap = &link_sta->pub->s1g_cap;
203
204	memset(s1g_cap, 0, sizeof(*s1g_cap));
205
206	memcpy(s1g_cap->cap, s1g_cap_ie->capab_info, sizeof(s1g_cap->cap));
207	memcpy(s1g_cap->nss_mcs, s1g_cap_ie->supp_mcs_nss,
208	       sizeof(s1g_cap->nss_mcs));
209
210	s1g_cap->s1g = true;
211
212	/* Maximum MPDU length is 1 bit for S1G */
213	if (s1g_cap->cap[3] & S1G_CAP3_MAX_MPDU_LEN) {
214		link_sta->pub->agg.max_amsdu_len =
215			IEEE80211_MAX_MPDU_LEN_VHT_7991;
216	} else {
217		link_sta->pub->agg.max_amsdu_len =
218			IEEE80211_MAX_MPDU_LEN_VHT_3895;
219	}
220
221	ieee80211_sta_recalc_aggregates(&link_sta->sta->sta);
222}
~/ Read-only file preview.