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 | |
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.u.s1g.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 + 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 | |
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(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 | |
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.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); |
120 | out: |
121 | ieee80211_s1g_send_twt_setup(sdata, da: mgmt->sa, bssid: sdata->vif.addr, twt); |
122 | } |
123 | |
124 | static void |
125 | ieee80211_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 | |
134 | static void |
135 | ieee80211_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 | |
150 | void 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 | |
175 | void 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 | |