xref: /linux/net/mac80211/ap.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
149e62ec6SJohannes Berg // SPDX-License-Identifier: GPL-2.0-only
249e62ec6SJohannes Berg /*
349e62ec6SJohannes Berg  * AP handling
449e62ec6SJohannes Berg  *
549e62ec6SJohannes Berg  * Partially
649e62ec6SJohannes Berg  * Copyright (C) 2026 Intel Corporation
749e62ec6SJohannes Berg  */
849e62ec6SJohannes Berg 
949e62ec6SJohannes Berg #include "driver-ops.h"
1049e62ec6SJohannes Berg #include "ieee80211_i.h"
11*14b1a855SJohannes Berg #include "rate.h"
1249e62ec6SJohannes Berg 
1349e62ec6SJohannes Berg static void
1449e62ec6SJohannes Berg ieee80211_send_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
1549e62ec6SJohannes Berg 				 struct ieee80211_mgmt *req, int opt_len)
1649e62ec6SJohannes Berg {
1749e62ec6SJohannes Berg 	int len = IEEE80211_MIN_ACTION_SIZE(eml_omn);
1849e62ec6SJohannes Berg 	struct ieee80211_local *local = sdata->local;
1949e62ec6SJohannes Berg 	struct ieee80211_mgmt *mgmt;
2049e62ec6SJohannes Berg 	struct sk_buff *skb;
2149e62ec6SJohannes Berg 
2249e62ec6SJohannes Berg 	len += opt_len; /* optional len */
2349e62ec6SJohannes Berg 	skb = dev_alloc_skb(local->tx_headroom + len);
2449e62ec6SJohannes Berg 	if (!skb)
2549e62ec6SJohannes Berg 		return;
2649e62ec6SJohannes Berg 
2749e62ec6SJohannes Berg 	skb_reserve(skb, local->tx_headroom);
2849e62ec6SJohannes Berg 	mgmt = skb_put_zero(skb, len);
2949e62ec6SJohannes Berg 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
3049e62ec6SJohannes Berg 					  IEEE80211_STYPE_ACTION);
3149e62ec6SJohannes Berg 	memcpy(mgmt->da, req->sa, ETH_ALEN);
3249e62ec6SJohannes Berg 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
3349e62ec6SJohannes Berg 	memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
3449e62ec6SJohannes Berg 
3549e62ec6SJohannes Berg 	mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_EHT;
3649e62ec6SJohannes Berg 	mgmt->u.action.action_code =
3749e62ec6SJohannes Berg 		WLAN_PROTECTED_EHT_ACTION_EML_OP_MODE_NOTIF;
3849e62ec6SJohannes Berg 	mgmt->u.action.eml_omn.dialog_token =
3949e62ec6SJohannes Berg 		req->u.action.eml_omn.dialog_token;
4049e62ec6SJohannes Berg 	mgmt->u.action.eml_omn.control = req->u.action.eml_omn.control &
4149e62ec6SJohannes Berg 		~(IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE |
4249e62ec6SJohannes Berg 		  IEEE80211_EML_CTRL_INDEV_COEX_ACT);
4349e62ec6SJohannes Berg 	/* Copy optional fields from the received notification frame */
4449e62ec6SJohannes Berg 	memcpy(mgmt->u.action.eml_omn.variable,
4549e62ec6SJohannes Berg 	       req->u.action.eml_omn.variable, opt_len);
4649e62ec6SJohannes Berg 
4749e62ec6SJohannes Berg 	ieee80211_tx_skb(sdata, skb);
4849e62ec6SJohannes Berg }
4949e62ec6SJohannes Berg 
5049e62ec6SJohannes Berg static void
5149e62ec6SJohannes Berg ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
5249e62ec6SJohannes Berg 			       struct sk_buff *skb)
5349e62ec6SJohannes Berg {
5449e62ec6SJohannes Berg 	int len = IEEE80211_MIN_ACTION_SIZE(eml_omn);
5549e62ec6SJohannes Berg 	enum nl80211_iftype type = ieee80211_vif_type_p2p(&sdata->vif);
5649e62ec6SJohannes Berg 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
5749e62ec6SJohannes Berg 	const struct wiphy_iftype_ext_capab *ift_ext_capa;
5849e62ec6SJohannes Berg 	struct ieee80211_mgmt *mgmt = (void *)skb->data;
5949e62ec6SJohannes Berg 	struct ieee80211_local *local = sdata->local;
6049e62ec6SJohannes Berg 	u8 control = mgmt->u.action.eml_omn.control;
6149e62ec6SJohannes Berg 	u8 *ptr = mgmt->u.action.eml_omn.variable;
6249e62ec6SJohannes Berg 	struct ieee80211_eml_params eml_params = {
6349e62ec6SJohannes Berg 		.link_id = status->link_id,
6449e62ec6SJohannes Berg 		.control = control,
6549e62ec6SJohannes Berg 	};
6649e62ec6SJohannes Berg 	struct sta_info *sta;
6749e62ec6SJohannes Berg 	int opt_len = 0;
6849e62ec6SJohannes Berg 
6949e62ec6SJohannes Berg 	if (!ieee80211_vif_is_mld(&sdata->vif))
7049e62ec6SJohannes Berg 		return;
7149e62ec6SJohannes Berg 
7249e62ec6SJohannes Berg 	/* eMLSR and eMLMR can't be enabled at the same time */
7349e62ec6SJohannes Berg 	if ((control & IEEE80211_EML_CTRL_EMLSR_MODE) &&
7449e62ec6SJohannes Berg 	    (control & IEEE80211_EML_CTRL_EMLMR_MODE))
7549e62ec6SJohannes Berg 		return;
7649e62ec6SJohannes Berg 
7749e62ec6SJohannes Berg 	if ((control & IEEE80211_EML_CTRL_EMLMR_MODE) &&
7849e62ec6SJohannes Berg 	    (control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE))
7949e62ec6SJohannes Berg 		return;
8049e62ec6SJohannes Berg 
8149e62ec6SJohannes Berg 	ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy, type);
8249e62ec6SJohannes Berg 	if (!ift_ext_capa)
8349e62ec6SJohannes Berg 		return;
8449e62ec6SJohannes Berg 
8549e62ec6SJohannes Berg 	if (!status->link_valid)
8649e62ec6SJohannes Berg 		return;
8749e62ec6SJohannes Berg 
8849e62ec6SJohannes Berg 	sta = sta_info_get_bss(sdata, mgmt->sa);
8949e62ec6SJohannes Berg 	if (!sta)
9049e62ec6SJohannes Berg 		return;
9149e62ec6SJohannes Berg 
9249e62ec6SJohannes Berg 	if (control & IEEE80211_EML_CTRL_EMLSR_MODE) {
9349e62ec6SJohannes Berg 		u8 emlsr_param_update_len;
9449e62ec6SJohannes Berg 
9549e62ec6SJohannes Berg 		if (!(ift_ext_capa->eml_capabilities &
9649e62ec6SJohannes Berg 		      IEEE80211_EML_CAP_EMLSR_SUPP))
9749e62ec6SJohannes Berg 			return;
9849e62ec6SJohannes Berg 
9949e62ec6SJohannes Berg 		opt_len += sizeof(__le16); /* eMLSR link_bitmap */
10049e62ec6SJohannes Berg 		/*
10149e62ec6SJohannes Berg 		 * eMLSR param update field is not part of Notification frame
10249e62ec6SJohannes Berg 		 * sent by the AP to client so account it separately.
10349e62ec6SJohannes Berg 		 */
10449e62ec6SJohannes Berg 		emlsr_param_update_len =
10549e62ec6SJohannes Berg 			!!(control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE);
10649e62ec6SJohannes Berg 
10749e62ec6SJohannes Berg 		if (skb->len < len + opt_len + emlsr_param_update_len)
10849e62ec6SJohannes Berg 			return;
10949e62ec6SJohannes Berg 
11049e62ec6SJohannes Berg 		if (control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE) {
11149e62ec6SJohannes Berg 			u8 pad_delay, trans_delay;
11249e62ec6SJohannes Berg 
11349e62ec6SJohannes Berg 			pad_delay = u8_get_bits(ptr[2],
11449e62ec6SJohannes Berg 						IEEE80211_EML_EMLSR_PAD_DELAY);
11549e62ec6SJohannes Berg 			if (pad_delay >
11649e62ec6SJohannes Berg 			    IEEE80211_EML_CAP_EML_PADDING_DELAY_256US)
11749e62ec6SJohannes Berg 				return;
11849e62ec6SJohannes Berg 
11949e62ec6SJohannes Berg 			trans_delay = u8_get_bits(ptr[2],
12049e62ec6SJohannes Berg 					IEEE80211_EML_EMLSR_TRANS_DELAY);
12149e62ec6SJohannes Berg 			if (trans_delay >
12249e62ec6SJohannes Berg 			    IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US)
12349e62ec6SJohannes Berg 				return;
12449e62ec6SJohannes Berg 
12549e62ec6SJohannes Berg 			/* Update sta padding and transition delay */
12649e62ec6SJohannes Berg 			sta->sta.eml_cap =
12749e62ec6SJohannes Berg 				u8_replace_bits(sta->sta.eml_cap,
12849e62ec6SJohannes Berg 						pad_delay,
12949e62ec6SJohannes Berg 						IEEE80211_EML_CAP_EML_PADDING_DELAY);
13049e62ec6SJohannes Berg 			sta->sta.eml_cap =
13149e62ec6SJohannes Berg 				u8_replace_bits(sta->sta.eml_cap,
13249e62ec6SJohannes Berg 						trans_delay,
13349e62ec6SJohannes Berg 						IEEE80211_EML_CAP_EML_TRANSITION_DELAY);
13449e62ec6SJohannes Berg 		}
13549e62ec6SJohannes Berg 	}
13649e62ec6SJohannes Berg 
13749e62ec6SJohannes Berg 	if (control & IEEE80211_EML_CTRL_EMLMR_MODE) {
13849e62ec6SJohannes Berg 		u8 mcs_map_size;
13949e62ec6SJohannes Berg 		int i;
14049e62ec6SJohannes Berg 
14149e62ec6SJohannes Berg 		if (!(ift_ext_capa->eml_capabilities &
14249e62ec6SJohannes Berg 		      IEEE80211_EML_CAP_EMLMR_SUPPORT))
14349e62ec6SJohannes Berg 			return;
14449e62ec6SJohannes Berg 
14549e62ec6SJohannes Berg 		opt_len += sizeof(__le16); /* eMLMR link_bitmap */
14649e62ec6SJohannes Berg 		opt_len++; /* eMLMR mcs_map_count */
14749e62ec6SJohannes Berg 		if (skb->len < len + opt_len)
14849e62ec6SJohannes Berg 			return;
14949e62ec6SJohannes Berg 
15049e62ec6SJohannes Berg 		eml_params.emlmr_mcs_map_count = ptr[2];
15149e62ec6SJohannes Berg 		if (eml_params.emlmr_mcs_map_count > 2)
15249e62ec6SJohannes Berg 			return;
15349e62ec6SJohannes Berg 
15449e62ec6SJohannes Berg 		mcs_map_size = 3 * (1 + eml_params.emlmr_mcs_map_count);
15549e62ec6SJohannes Berg 		opt_len += mcs_map_size;
15649e62ec6SJohannes Berg 		if (skb->len < len + opt_len)
15749e62ec6SJohannes Berg 			return;
15849e62ec6SJohannes Berg 
15949e62ec6SJohannes Berg 		for (i = 0; i < mcs_map_size; i++) {
16049e62ec6SJohannes Berg 			u8 rx_mcs, tx_mcs;
16149e62ec6SJohannes Berg 
16249e62ec6SJohannes Berg 			rx_mcs = u8_get_bits(ptr[3 + i],
16349e62ec6SJohannes Berg 					     IEEE80211_EML_EMLMR_RX_MCS_MAP);
16449e62ec6SJohannes Berg 			if (rx_mcs > 8)
16549e62ec6SJohannes Berg 				return;
16649e62ec6SJohannes Berg 
16749e62ec6SJohannes Berg 			tx_mcs = u8_get_bits(ptr[3 + i],
16849e62ec6SJohannes Berg 					     IEEE80211_EML_EMLMR_TX_MCS_MAP);
16949e62ec6SJohannes Berg 			if (tx_mcs > 8)
17049e62ec6SJohannes Berg 				return;
17149e62ec6SJohannes Berg 		}
17249e62ec6SJohannes Berg 
17349e62ec6SJohannes Berg 		memcpy(eml_params.emlmr_mcs_map_bw, &ptr[3], mcs_map_size);
17449e62ec6SJohannes Berg 	}
17549e62ec6SJohannes Berg 
17649e62ec6SJohannes Berg 	if ((control & IEEE80211_EML_CTRL_EMLSR_MODE) ||
17749e62ec6SJohannes Berg 	    (control & IEEE80211_EML_CTRL_EMLMR_MODE)) {
17849e62ec6SJohannes Berg 		eml_params.link_bitmap = get_unaligned_le16(ptr);
17949e62ec6SJohannes Berg 		if ((eml_params.link_bitmap & sdata->vif.active_links) !=
18049e62ec6SJohannes Berg 		    eml_params.link_bitmap)
18149e62ec6SJohannes Berg 			return;
18249e62ec6SJohannes Berg 	}
18349e62ec6SJohannes Berg 
18449e62ec6SJohannes Berg 	if (drv_set_eml_op_mode(sdata, &sta->sta, &eml_params))
18549e62ec6SJohannes Berg 		return;
18649e62ec6SJohannes Berg 
18749e62ec6SJohannes Berg 	ieee80211_send_eml_op_mode_notif(sdata, mgmt, opt_len);
18849e62ec6SJohannes Berg }
18949e62ec6SJohannes Berg 
190*14b1a855SJohannes Berg static void
191*14b1a855SJohannes Berg ieee80211_rx_uhr_link_reconfig_req(struct ieee80211_sub_if_data *sdata,
192*14b1a855SJohannes Berg 				   struct sk_buff *skb)
193*14b1a855SJohannes Berg {
194*14b1a855SJohannes Berg 	struct ieee80211_mgmt *mgmt = (void *)skb->data;
195*14b1a855SJohannes Berg 	const struct element *sub;
196*14b1a855SJohannes Berg 	struct sta_info *sta;
197*14b1a855SJohannes Berg 
198*14b1a855SJohannes Berg 	/*
199*14b1a855SJohannes Berg 	 * rx.c only accepts IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST
200*14b1a855SJohannes Berg 	 * which is valid, so no need to check the frame type/format/etc.
201*14b1a855SJohannes Berg 	 */
202*14b1a855SJohannes Berg 
203*14b1a855SJohannes Berg 	sta = sta_info_get_bss(sdata, mgmt->sa);
204*14b1a855SJohannes Berg 	if (!sta)
205*14b1a855SJohannes Berg 		return;
206*14b1a855SJohannes Berg 
207*14b1a855SJohannes Berg 	struct ieee802_11_elems *elems __free(kfree) =
208*14b1a855SJohannes Berg 		ieee802_11_parse_elems(mgmt->u.action.uhr_link_reconf_req.variable,
209*14b1a855SJohannes Berg 				       skb->len - IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req),
210*14b1a855SJohannes Berg 				       IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION,
211*14b1a855SJohannes Berg 				       NULL);
212*14b1a855SJohannes Berg 	/* STA will assume we processed it, not good */
213*14b1a855SJohannes Berg 	if (!elems)
214*14b1a855SJohannes Berg 		return;
215*14b1a855SJohannes Berg 
216*14b1a855SJohannes Berg 	if (!elems->ml_reconf)
217*14b1a855SJohannes Berg 		return;
218*14b1a855SJohannes Berg 
219*14b1a855SJohannes Berg 	for_each_mle_subelement(sub, (u8 *)elems->ml_reconf,
220*14b1a855SJohannes Berg 				elems->ml_reconf_len) {
221*14b1a855SJohannes Berg 		const struct ieee80211_mle_per_sta_profile *prof =
222*14b1a855SJohannes Berg 			 (const void *)sub->data;
223*14b1a855SJohannes Berg 		struct ieee80211_chanctx_conf *chanctx_conf;
224*14b1a855SJohannes Berg 		struct ieee80211_chanctx *chanctx;
225*14b1a855SJohannes Berg 		struct ieee80211_link_data *link;
226*14b1a855SJohannes Berg 		struct link_sta_info *link_sta;
227*14b1a855SJohannes Berg 		const struct element *chg;
228*14b1a855SJohannes Berg 		u16 control;
229*14b1a855SJohannes Berg 		u8 link_id;
230*14b1a855SJohannes Berg 
231*14b1a855SJohannes Berg 		if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
232*14b1a855SJohannes Berg 			continue;
233*14b1a855SJohannes Berg 
234*14b1a855SJohannes Berg 		if (!ieee80211_mle_reconf_sta_prof_size_ok(sub->data,
235*14b1a855SJohannes Berg 							   sub->datalen))
236*14b1a855SJohannes Berg 			return;
237*14b1a855SJohannes Berg 
238*14b1a855SJohannes Berg 		control = le16_to_cpu(prof->control);
239*14b1a855SJohannes Berg 		link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID;
240*14b1a855SJohannes Berg 
241*14b1a855SJohannes Berg 		if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
242*14b1a855SJohannes Berg 			return;
243*14b1a855SJohannes Berg 
244*14b1a855SJohannes Berg 		link = sdata_dereference(sdata->link[link_id], sdata);
245*14b1a855SJohannes Berg 		if (!link)
246*14b1a855SJohannes Berg 			continue;
247*14b1a855SJohannes Berg 
248*14b1a855SJohannes Berg 		chanctx_conf = sdata_dereference(link->conf->chanctx_conf,
249*14b1a855SJohannes Berg 						 sdata);
250*14b1a855SJohannes Berg 		if (!chanctx_conf)
251*14b1a855SJohannes Berg 			continue;
252*14b1a855SJohannes Berg 		chanctx = container_of(chanctx_conf, struct ieee80211_chanctx,
253*14b1a855SJohannes Berg 				       conf);
254*14b1a855SJohannes Berg 
255*14b1a855SJohannes Berg 		link_sta = sdata_dereference(sta->link[link_id], sdata);
256*14b1a855SJohannes Berg 		if (!link_sta)
257*14b1a855SJohannes Berg 			continue;
258*14b1a855SJohannes Berg 
259*14b1a855SJohannes Berg 		/* do we need to handle any other bits? */
260*14b1a855SJohannes Berg 		if (control & ~(IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID |
261*14b1a855SJohannes Berg 				IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE))
262*14b1a855SJohannes Berg 			continue;
263*14b1a855SJohannes Berg 
264*14b1a855SJohannes Berg 		if (u16_get_bits(control, IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE) !=
265*14b1a855SJohannes Berg 				IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_UHR_OMP_UPD)
266*14b1a855SJohannes Berg 			continue;
267*14b1a855SJohannes Berg 
268*14b1a855SJohannes Berg 		for_each_element_extid(chg, WLAN_EID_EXT_UHR_MODE_CHG,
269*14b1a855SJohannes Berg 				       prof->variable + prof->sta_info_len - 1,
270*14b1a855SJohannes Berg 				       sub->datalen - sizeof(*prof) -
271*14b1a855SJohannes Berg 				       prof->sta_info_len + 1) {
272*14b1a855SJohannes Berg 			const struct ieee80211_uhr_mode_change_tuple *tuple;
273*14b1a855SJohannes Berg 
274*14b1a855SJohannes Berg 			for_each_uhr_mode_change_tuple(chg->data + 1,
275*14b1a855SJohannes Berg 						       chg->datalen - 1,
276*14b1a855SJohannes Berg 						       tuple) {
277*14b1a855SJohannes Berg 				u8 id = le16_get_bits(tuple->control,
278*14b1a855SJohannes Berg 						      IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ID);
279*14b1a855SJohannes Berg 				bool enabled = le16_get_bits(tuple->control,
280*14b1a855SJohannes Berg 							     IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ENABLE);
281*14b1a855SJohannes Berg 
282*14b1a855SJohannes Berg 				/* only handle DBE (for now?) */
283*14b1a855SJohannes Berg 				if (id != IEEE80211_UHR_MODE_CHANGE_MODE_ID_DBE)
284*14b1a855SJohannes Berg 					continue;
285*14b1a855SJohannes Berg 
286*14b1a855SJohannes Berg 				link_sta->uhr_dbe_enabled = enabled;
287*14b1a855SJohannes Berg 				/* also recalculates and updates per-STA bw */
288*14b1a855SJohannes Berg 				ieee80211_recalc_chanctx_min_def(sdata->local,
289*14b1a855SJohannes Berg 								 chanctx);
290*14b1a855SJohannes Berg 			}
291*14b1a855SJohannes Berg 		}
292*14b1a855SJohannes Berg 	}
293*14b1a855SJohannes Berg 
294*14b1a855SJohannes Berg 	/* TODO: send a response */
295*14b1a855SJohannes Berg }
296*14b1a855SJohannes Berg 
29749e62ec6SJohannes Berg void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata,
29849e62ec6SJohannes Berg 				  struct sk_buff *skb)
29949e62ec6SJohannes Berg {
30049e62ec6SJohannes Berg 	struct ieee80211_mgmt *mgmt = (void *)skb->data;
30149e62ec6SJohannes Berg 
30249e62ec6SJohannes Berg 	/* rx.c cannot queue any non-action frames to AP interfaces */
30349e62ec6SJohannes Berg 	if (WARN_ON(!ieee80211_is_action(mgmt->frame_control)))
30449e62ec6SJohannes Berg 		return;
30549e62ec6SJohannes Berg 
30649e62ec6SJohannes Berg 	switch (mgmt->u.action.category) {
30749e62ec6SJohannes Berg 	case WLAN_CATEGORY_PROTECTED_EHT:
30849e62ec6SJohannes Berg 		switch (mgmt->u.action.action_code) {
30949e62ec6SJohannes Berg 		case WLAN_PROTECTED_EHT_ACTION_EML_OP_MODE_NOTIF:
31049e62ec6SJohannes Berg 			ieee80211_rx_eml_op_mode_notif(sdata, skb);
31149e62ec6SJohannes Berg 			break;
31249e62ec6SJohannes Berg 		}
31349e62ec6SJohannes Berg 		break;
314*14b1a855SJohannes Berg 	case WLAN_CATEGORY_PROTECTED_UHR:
315*14b1a855SJohannes Berg 		switch (mgmt->u.action.action_code) {
316*14b1a855SJohannes Berg 		case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST:
317*14b1a855SJohannes Berg 			ieee80211_rx_uhr_link_reconfig_req(sdata, skb);
318*14b1a855SJohannes Berg 			break;
31949e62ec6SJohannes Berg 		}
320*14b1a855SJohannes Berg 		break;
321*14b1a855SJohannes Berg 	}
322*14b1a855SJohannes Berg }
323*14b1a855SJohannes Berg 
324*14b1a855SJohannes Berg void ieee80211_uhr_disable_dbe_all_stas(struct ieee80211_link_data *link)
325*14b1a855SJohannes Berg {
326*14b1a855SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
327*14b1a855SJohannes Berg 	struct ieee80211_local *local = sdata->local;
328*14b1a855SJohannes Berg 	struct ieee80211_chanctx_conf *chanctx_conf;
329*14b1a855SJohannes Berg 	struct ieee80211_chanctx *chanctx;
330*14b1a855SJohannes Berg 	int link_id = link->link_id;
331*14b1a855SJohannes Berg 	struct sta_info *sta;
332*14b1a855SJohannes Berg 
333*14b1a855SJohannes Berg 	chanctx_conf = sdata_dereference(link->conf->chanctx_conf, sdata);
334*14b1a855SJohannes Berg 	if (!chanctx_conf)
335*14b1a855SJohannes Berg 		return;
336*14b1a855SJohannes Berg 	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
337*14b1a855SJohannes Berg 
338*14b1a855SJohannes Berg 	list_for_each_entry(sta, &local->sta_list, list) {
339*14b1a855SJohannes Berg 		struct link_sta_info *link_sta;
340*14b1a855SJohannes Berg 
341*14b1a855SJohannes Berg 		if (sta->sdata->bss != sdata->bss)
342*14b1a855SJohannes Berg 			continue;
343*14b1a855SJohannes Berg 
344*14b1a855SJohannes Berg 		link_sta = sdata_dereference(sta->link[link_id], sdata);
345*14b1a855SJohannes Berg 		if (!link_sta)
346*14b1a855SJohannes Berg 			continue;
347*14b1a855SJohannes Berg 
348*14b1a855SJohannes Berg 		link_sta->uhr_dbe_enabled = false;
349*14b1a855SJohannes Berg 	}
350*14b1a855SJohannes Berg 
351*14b1a855SJohannes Berg 	/* also recalculates and updates per-STA bw */
352*14b1a855SJohannes Berg 	ieee80211_recalc_chanctx_min_def(local, chanctx);
35349e62ec6SJohannes Berg }
354