mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
wifi: mac80211: AP: handle DBE for clients
In AP mode, track the BSS non-DBE bandwidth and apply that to all non-DBE clients, then track OMP updates from the clients and enable/disable DBE accordingly. For now don't send a response, clients need to have a timer anyway (it's up to the driver to set the right timeout in UHR capabilities.) Link: https://patch.msgid.link/20260529102644.be84f2b055cc.I4d2c067dfe54c47621d5a872ca07a0e754d6c20f@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
d3ea22270d
commit
14b1a85518
|
|
@ -393,14 +393,24 @@ ieee80211_eht_oper_size_ok(const u8 *data, u8 len)
|
|||
return len >= needed;
|
||||
}
|
||||
|
||||
/* must validate ieee80211_eht_oper_size_ok() first */
|
||||
static inline const struct ieee80211_eht_operation_info *
|
||||
ieee80211_eht_oper_info(const struct ieee80211_eht_operation *eht_oper)
|
||||
{
|
||||
if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT))
|
||||
return NULL;
|
||||
|
||||
return (const void *)eht_oper->optional;
|
||||
}
|
||||
|
||||
/* must validate ieee80211_eht_oper_size_ok() first */
|
||||
static inline u16
|
||||
ieee80211_eht_oper_dis_subchan_bitmap(const struct ieee80211_eht_operation *eht_oper)
|
||||
{
|
||||
const struct ieee80211_eht_operation_info *info =
|
||||
(const void *)eht_oper->optional;
|
||||
const struct ieee80211_eht_operation_info *info;
|
||||
|
||||
if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT))
|
||||
info = ieee80211_eht_oper_info(eht_oper);
|
||||
if (!info)
|
||||
return 0;
|
||||
|
||||
if (!(eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT))
|
||||
|
|
|
|||
|
|
@ -628,4 +628,20 @@ struct ieee80211_uhr_mode_change_tuple {
|
|||
u8 variable[];
|
||||
} __packed;
|
||||
|
||||
static inline int
|
||||
ieee80211_uhr_mode_change_tuple_size(const struct ieee80211_uhr_mode_change_tuple *tuple)
|
||||
{
|
||||
return sizeof(*tuple) +
|
||||
le16_get_bits(tuple->control,
|
||||
IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_LENGTH);
|
||||
}
|
||||
|
||||
#define for_each_uhr_mode_change_tuple(data, len, tuple) \
|
||||
for (tuple = (const void *)(data); \
|
||||
(len) - ((const u8 *)tuple - (data)) >= sizeof(*tuple) && \
|
||||
(len) - ((const u8 *)tuple - (data)) >= \
|
||||
ieee80211_uhr_mode_change_tuple_size(tuple); \
|
||||
tuple = (const void *)((const u8 *)tuple + \
|
||||
ieee80211_uhr_mode_change_tuple_size(tuple)))
|
||||
|
||||
#endif /* LINUX_IEEE80211_UHR_H */
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "driver-ops.h"
|
||||
#include "ieee80211_i.h"
|
||||
#include "rate.h"
|
||||
|
||||
static void
|
||||
ieee80211_send_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
|
||||
|
|
@ -186,6 +187,113 @@ ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
|
|||
ieee80211_send_eml_op_mode_notif(sdata, mgmt, opt_len);
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_rx_uhr_link_reconfig_req(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_mgmt *mgmt = (void *)skb->data;
|
||||
const struct element *sub;
|
||||
struct sta_info *sta;
|
||||
|
||||
/*
|
||||
* rx.c only accepts IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST
|
||||
* which is valid, so no need to check the frame type/format/etc.
|
||||
*/
|
||||
|
||||
sta = sta_info_get_bss(sdata, mgmt->sa);
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
struct ieee802_11_elems *elems __free(kfree) =
|
||||
ieee802_11_parse_elems(mgmt->u.action.uhr_link_reconf_req.variable,
|
||||
skb->len - IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req),
|
||||
IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION,
|
||||
NULL);
|
||||
/* STA will assume we processed it, not good */
|
||||
if (!elems)
|
||||
return;
|
||||
|
||||
if (!elems->ml_reconf)
|
||||
return;
|
||||
|
||||
for_each_mle_subelement(sub, (u8 *)elems->ml_reconf,
|
||||
elems->ml_reconf_len) {
|
||||
const struct ieee80211_mle_per_sta_profile *prof =
|
||||
(const void *)sub->data;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
struct ieee80211_link_data *link;
|
||||
struct link_sta_info *link_sta;
|
||||
const struct element *chg;
|
||||
u16 control;
|
||||
u8 link_id;
|
||||
|
||||
if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
|
||||
continue;
|
||||
|
||||
if (!ieee80211_mle_reconf_sta_prof_size_ok(sub->data,
|
||||
sub->datalen))
|
||||
return;
|
||||
|
||||
control = le16_to_cpu(prof->control);
|
||||
link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID;
|
||||
|
||||
if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
|
||||
return;
|
||||
|
||||
link = sdata_dereference(sdata->link[link_id], sdata);
|
||||
if (!link)
|
||||
continue;
|
||||
|
||||
chanctx_conf = sdata_dereference(link->conf->chanctx_conf,
|
||||
sdata);
|
||||
if (!chanctx_conf)
|
||||
continue;
|
||||
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx,
|
||||
conf);
|
||||
|
||||
link_sta = sdata_dereference(sta->link[link_id], sdata);
|
||||
if (!link_sta)
|
||||
continue;
|
||||
|
||||
/* do we need to handle any other bits? */
|
||||
if (control & ~(IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID |
|
||||
IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE))
|
||||
continue;
|
||||
|
||||
if (u16_get_bits(control, IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE) !=
|
||||
IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_UHR_OMP_UPD)
|
||||
continue;
|
||||
|
||||
for_each_element_extid(chg, WLAN_EID_EXT_UHR_MODE_CHG,
|
||||
prof->variable + prof->sta_info_len - 1,
|
||||
sub->datalen - sizeof(*prof) -
|
||||
prof->sta_info_len + 1) {
|
||||
const struct ieee80211_uhr_mode_change_tuple *tuple;
|
||||
|
||||
for_each_uhr_mode_change_tuple(chg->data + 1,
|
||||
chg->datalen - 1,
|
||||
tuple) {
|
||||
u8 id = le16_get_bits(tuple->control,
|
||||
IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ID);
|
||||
bool enabled = le16_get_bits(tuple->control,
|
||||
IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ENABLE);
|
||||
|
||||
/* only handle DBE (for now?) */
|
||||
if (id != IEEE80211_UHR_MODE_CHANGE_MODE_ID_DBE)
|
||||
continue;
|
||||
|
||||
link_sta->uhr_dbe_enabled = enabled;
|
||||
/* also recalculates and updates per-STA bw */
|
||||
ieee80211_recalc_chanctx_min_def(sdata->local,
|
||||
chanctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: send a response */
|
||||
}
|
||||
|
||||
void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -203,5 +311,43 @@ void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata,
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case WLAN_CATEGORY_PROTECTED_UHR:
|
||||
switch (mgmt->u.action.action_code) {
|
||||
case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST:
|
||||
ieee80211_rx_uhr_link_reconfig_req(sdata, skb);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_uhr_disable_dbe_all_stas(struct ieee80211_link_data *link)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = link->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
int link_id = link->link_id;
|
||||
struct sta_info *sta;
|
||||
|
||||
chanctx_conf = sdata_dereference(link->conf->chanctx_conf, sdata);
|
||||
if (!chanctx_conf)
|
||||
return;
|
||||
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
||||
|
||||
list_for_each_entry(sta, &local->sta_list, list) {
|
||||
struct link_sta_info *link_sta;
|
||||
|
||||
if (sta->sdata->bss != sdata->bss)
|
||||
continue;
|
||||
|
||||
link_sta = sdata_dereference(sta->link[link_id], sdata);
|
||||
if (!link_sta)
|
||||
continue;
|
||||
|
||||
link_sta->uhr_dbe_enabled = false;
|
||||
}
|
||||
|
||||
/* also recalculates and updates per-STA bw */
|
||||
ieee80211_recalc_chanctx_min_def(local, chanctx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1391,6 +1391,36 @@ ieee80211_calc_ap_he_and_lower(struct cfg80211_beacon_data *params)
|
|||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
|
||||
static enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_calc_ap_eht_bw(struct cfg80211_beacon_data *params,
|
||||
enum ieee80211_sta_rx_bandwidth he_and_lower)
|
||||
{
|
||||
const struct ieee80211_eht_operation_info *info;
|
||||
|
||||
if (!params->eht_oper)
|
||||
return he_and_lower;
|
||||
|
||||
info = ieee80211_eht_oper_info(params->eht_oper);
|
||||
if (!info)
|
||||
return he_and_lower;
|
||||
|
||||
switch (u8_get_bits(info->control, IEEE80211_EHT_OPER_CHAN_WIDTH)) {
|
||||
case IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ:
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
case IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ:
|
||||
return IEEE80211_STA_RX_BW_40;
|
||||
case IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ:
|
||||
return IEEE80211_STA_RX_BW_80;
|
||||
case IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ:
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ:
|
||||
return IEEE80211_STA_RX_BW_320;
|
||||
}
|
||||
|
||||
/* invalid setting, assume 20 MHz */
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
|
||||
static void ieee80211_update_ap_bandwidth(struct ieee80211_link_data *link,
|
||||
struct cfg80211_beacon_data *params)
|
||||
{
|
||||
|
|
@ -1415,6 +1445,8 @@ static void ieee80211_update_ap_bandwidth(struct ieee80211_link_data *link,
|
|||
return;
|
||||
|
||||
link->bss_bw.he_and_lower = ieee80211_calc_ap_he_and_lower(params);
|
||||
link->bss_bw.eht = ieee80211_calc_ap_eht_bw(params,
|
||||
link->bss_bw.he_and_lower);
|
||||
|
||||
chanctx_conf = sdata_dereference(link->conf->chanctx_conf, link->sdata);
|
||||
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
||||
|
|
@ -4469,6 +4501,9 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
|
|||
|
||||
ieee80211_link_info_change_notify(sdata, link_data, changed);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
ieee80211_uhr_disable_dbe_all_stas(link_data);
|
||||
|
||||
ieee80211_vif_unblock_queues_csa(sdata);
|
||||
|
||||
err = drv_post_channel_switch(link_data);
|
||||
|
|
|
|||
|
|
@ -1146,6 +1146,7 @@ struct ieee80211_link_data {
|
|||
|
||||
struct {
|
||||
enum ieee80211_sta_rx_bandwidth he_and_lower;
|
||||
enum ieee80211_sta_rx_bandwidth eht; /* and UHR non-DBE */
|
||||
} bss_bw;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
|
|
@ -2003,6 +2004,7 @@ bool ieee80211_is_our_addr(struct ieee80211_sub_if_data *sdata,
|
|||
/* AP code */
|
||||
void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
void ieee80211_uhr_disable_dbe_all_stas(struct ieee80211_link_data *link);
|
||||
|
||||
/* STA code */
|
||||
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
|
||||
|
|
|
|||
|
|
@ -3952,6 +3952,15 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
|||
break;
|
||||
|
||||
switch (mgmt->u.action.action_code) {
|
||||
case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST:
|
||||
if (sdata->vif.type != NL80211_IFTYPE_AP)
|
||||
break;
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req))
|
||||
goto invalid;
|
||||
if (mgmt->u.action.uhr_link_reconf_req.type !=
|
||||
IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST)
|
||||
break;
|
||||
goto queue;
|
||||
case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_NOTIFY:
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -3701,10 +3701,14 @@ ieee80211_sta_usable_bw(struct link_sta_info *link_sta,
|
|||
if (WARN_ON(!link))
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
|
||||
if (link_sta->pub->eht_cap.has_eht)
|
||||
return bw;
|
||||
if (!link_sta->pub->eht_cap.has_eht)
|
||||
return min(bw, link->bss_bw.he_and_lower);
|
||||
|
||||
return min(bw, link->bss_bw.he_and_lower);
|
||||
if (!link_sta->pub->uhr_cap.has_uhr ||
|
||||
!link_sta->uhr_dbe_enabled)
|
||||
return min(bw, link->bss_bw.eht);
|
||||
|
||||
return bw;
|
||||
}
|
||||
|
||||
static enum ieee80211_sta_rx_bandwidth
|
||||
|
|
|
|||
|
|
@ -514,6 +514,8 @@ struct ieee80211_fragment_cache {
|
|||
* @uhr_usable_tx_width: bandwidth restriction for UHR for TX, only when
|
||||
* the link_sta is an AP, to restrict TX to BSS width during DBE
|
||||
* enablement
|
||||
* @uhr_dbe_enabled: for STAs as clients to an AP interface indicates
|
||||
* DBE is enabled by the STA
|
||||
* @debugfs_dir: debug filesystem directory dentry
|
||||
* @pub: public (driver visible) link STA data
|
||||
*/
|
||||
|
|
@ -567,6 +569,8 @@ struct link_sta_info {
|
|||
rx_omi_bw_staging;
|
||||
enum ieee80211_sta_rx_bandwidth uhr_usable_tx_width;
|
||||
|
||||
bool uhr_dbe_enabled;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
struct dentry *debugfs_dir;
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user