mirror of
https://github.com/torvalds/linux.git
synced 2024-12-25 20:32:22 +00:00
wifi: mac80211: process and save negotiated TID to Link mapping request
An MLD may send TID-to-Link mapping request frame to negotiate TID to link mapping with a peer MLD. Support handling negotiated TID-to-Link mapping request frame by parsing the frame, asking the driver whether it supports the received mapping or not, and sending a TID-to-Link mapping response to the AP MLD. Theoretically, links that became inactive due to the received TID-to-Link mapping request, can be selected to be activated but this would require tearing down the negotiated TID-to-Link mapping, which is still not supported. Signed-off-by: Ayala Beker <ayala.beker@intel.com> Reviewed-by: Johannes Berg <johannes.berg@intel.com> Reviewed-by: Gregory Greenman <gregory.greenman@intel.com> Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com> Link: https://msgid.link/20240102213313.0bc1a24fcc9d.Ie72e47dc6f8c77d4a2f0947b775ef6367fe0edac@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
83e897a961
commit
8f500fbc6c
@ -342,6 +342,7 @@ struct ieee80211_vif_chanctx_switch {
|
|||||||
* status changed.
|
* status changed.
|
||||||
* @BSS_CHANGED_EHT_PUNCTURING: The channel puncturing bitmap changed.
|
* @BSS_CHANGED_EHT_PUNCTURING: The channel puncturing bitmap changed.
|
||||||
* @BSS_CHANGED_MLD_VALID_LINKS: MLD valid links status changed.
|
* @BSS_CHANGED_MLD_VALID_LINKS: MLD valid links status changed.
|
||||||
|
* @BSS_CHANGED_MLD_TTLM: TID to link mapping was changed
|
||||||
*/
|
*/
|
||||||
enum ieee80211_bss_change {
|
enum ieee80211_bss_change {
|
||||||
BSS_CHANGED_ASSOC = 1<<0,
|
BSS_CHANGED_ASSOC = 1<<0,
|
||||||
@ -378,6 +379,7 @@ enum ieee80211_bss_change {
|
|||||||
BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = 1<<31,
|
BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = 1<<31,
|
||||||
BSS_CHANGED_EHT_PUNCTURING = BIT_ULL(32),
|
BSS_CHANGED_EHT_PUNCTURING = BIT_ULL(32),
|
||||||
BSS_CHANGED_MLD_VALID_LINKS = BIT_ULL(33),
|
BSS_CHANGED_MLD_VALID_LINKS = BIT_ULL(33),
|
||||||
|
BSS_CHANGED_MLD_TTLM = BIT_ULL(34),
|
||||||
|
|
||||||
/* when adding here, make sure to change ieee80211_reconfig */
|
/* when adding here, make sure to change ieee80211_reconfig */
|
||||||
};
|
};
|
||||||
@ -1845,6 +1847,35 @@ struct ieee80211_vif_cfg {
|
|||||||
u8 ap_addr[ETH_ALEN] __aligned(2);
|
u8 ap_addr[ETH_ALEN] __aligned(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define IEEE80211_TTLM_NUM_TIDS 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ieee80211_neg_ttlm - negotiated TID to link map info
|
||||||
|
*
|
||||||
|
* @downlink: bitmap of active links per TID for downlink, or 0 if mapping for
|
||||||
|
* this TID is not included.
|
||||||
|
* @uplink: bitmap of active links per TID for uplink, or 0 if mapping for this
|
||||||
|
* TID is not included.
|
||||||
|
* @valid: info is valid or not.
|
||||||
|
*/
|
||||||
|
struct ieee80211_neg_ttlm {
|
||||||
|
u16 downlink[IEEE80211_TTLM_NUM_TIDS];
|
||||||
|
u16 uplink[IEEE80211_TTLM_NUM_TIDS];
|
||||||
|
bool valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ieee80211_neg_ttlm_res - return value for negotiated TTLM handling
|
||||||
|
* @NEG_TTLM_RES_ACCEPT: accept the request
|
||||||
|
* @NEG_TTLM_RES_REJECT: reject the request
|
||||||
|
* @NEG_TTLM_RES_SUGGEST_PREFERRED: reject and suggest a new mapping
|
||||||
|
*/
|
||||||
|
enum ieee80211_neg_ttlm_res {
|
||||||
|
NEG_TTLM_RES_ACCEPT,
|
||||||
|
NEG_TTLM_RES_REJECT,
|
||||||
|
NEG_TTLM_RES_SUGGEST_PREFERRED
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ieee80211_vif - per-interface data
|
* struct ieee80211_vif - per-interface data
|
||||||
*
|
*
|
||||||
@ -1863,6 +1894,11 @@ struct ieee80211_vif_cfg {
|
|||||||
* API calls meant for that purpose.
|
* API calls meant for that purpose.
|
||||||
* @dormant_links: bitmap of valid but disabled links, or 0 for non-MLO.
|
* @dormant_links: bitmap of valid but disabled links, or 0 for non-MLO.
|
||||||
* Must be a subset of valid_links.
|
* Must be a subset of valid_links.
|
||||||
|
* @suspended_links: subset of dormant_links representing links that are
|
||||||
|
* suspended.
|
||||||
|
* 0 for non-MLO.
|
||||||
|
* @neg_ttlm: negotiated TID to link mapping info.
|
||||||
|
* see &struct ieee80211_neg_ttlm.
|
||||||
* @addr: address of this interface
|
* @addr: address of this interface
|
||||||
* @p2p: indicates whether this AP or STA interface is a p2p
|
* @p2p: indicates whether this AP or STA interface is a p2p
|
||||||
* interface, i.e. a GO or p2p-sta respectively
|
* interface, i.e. a GO or p2p-sta respectively
|
||||||
@ -1900,7 +1936,8 @@ struct ieee80211_vif {
|
|||||||
struct ieee80211_vif_cfg cfg;
|
struct ieee80211_vif_cfg cfg;
|
||||||
struct ieee80211_bss_conf bss_conf;
|
struct ieee80211_bss_conf bss_conf;
|
||||||
struct ieee80211_bss_conf __rcu *link_conf[IEEE80211_MLD_MAX_NUM_LINKS];
|
struct ieee80211_bss_conf __rcu *link_conf[IEEE80211_MLD_MAX_NUM_LINKS];
|
||||||
u16 valid_links, active_links, dormant_links;
|
u16 valid_links, active_links, dormant_links, suspended_links;
|
||||||
|
struct ieee80211_neg_ttlm neg_ttlm;
|
||||||
u8 addr[ETH_ALEN] __aligned(2);
|
u8 addr[ETH_ALEN] __aligned(2);
|
||||||
bool p2p;
|
bool p2p;
|
||||||
|
|
||||||
@ -4293,6 +4330,10 @@ struct ieee80211_prep_tx_info {
|
|||||||
* flow offloading for flows originating from the vif.
|
* flow offloading for flows originating from the vif.
|
||||||
* Note that the driver must not assume that the vif driver_data is valid
|
* Note that the driver must not assume that the vif driver_data is valid
|
||||||
* at this point, since the callback can be called during netdev teardown.
|
* at this point, since the callback can be called during netdev teardown.
|
||||||
|
* @can_neg_ttlm: for managed interface, requests the driver to determine
|
||||||
|
* if the requested TID-To-Link mapping can be accepted or not.
|
||||||
|
* If it's not accepted the driver may suggest a preferred mapping and
|
||||||
|
* modify @ttlm parameter with the suggested TID-to-Link mapping.
|
||||||
*/
|
*/
|
||||||
struct ieee80211_ops {
|
struct ieee80211_ops {
|
||||||
void (*tx)(struct ieee80211_hw *hw,
|
void (*tx)(struct ieee80211_hw *hw,
|
||||||
@ -4673,6 +4714,9 @@ struct ieee80211_ops {
|
|||||||
struct net_device *dev,
|
struct net_device *dev,
|
||||||
enum tc_setup_type type,
|
enum tc_setup_type type,
|
||||||
void *type_data);
|
void *type_data);
|
||||||
|
enum ieee80211_neg_ttlm_res
|
||||||
|
(*can_neg_ttlm)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
|
struct ieee80211_neg_ttlm *ttlm);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1695,4 +1695,23 @@ int drv_change_sta_links(struct ieee80211_local *local,
|
|||||||
struct ieee80211_sta *sta,
|
struct ieee80211_sta *sta,
|
||||||
u16 old_links, u16 new_links);
|
u16 old_links, u16 new_links);
|
||||||
|
|
||||||
|
static inline enum ieee80211_neg_ttlm_res
|
||||||
|
drv_can_neg_ttlm(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_neg_ttlm *neg_ttlm)
|
||||||
|
{
|
||||||
|
enum ieee80211_neg_ttlm_res res = NEG_TTLM_RES_REJECT;
|
||||||
|
|
||||||
|
might_sleep();
|
||||||
|
if (!check_sdata_in_driver(sdata))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
trace_drv_can_neg_ttlm(local, sdata, neg_ttlm);
|
||||||
|
if (local->ops->can_neg_ttlm)
|
||||||
|
res = local->ops->can_neg_ttlm(&local->hw, &sdata->vif,
|
||||||
|
neg_ttlm);
|
||||||
|
trace_drv_neg_ttlm_res(local, sdata, res, neg_ttlm);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
#endif /* __MAC80211_DRIVER_OPS */
|
#endif /* __MAC80211_DRIVER_OPS */
|
||||||
|
@ -2603,6 +2603,8 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
|
|||||||
const struct ieee80211_eht_cap_elem *eht_cap_ie_elem,
|
const struct ieee80211_eht_cap_elem *eht_cap_ie_elem,
|
||||||
u8 eht_cap_len,
|
u8 eht_cap_len,
|
||||||
struct link_sta_info *link_sta);
|
struct link_sta_info *link_sta);
|
||||||
|
void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_mgmt *mgmt, size_t len);
|
||||||
|
|
||||||
void ieee80211_check_wbrf_support(struct ieee80211_local *local);
|
void ieee80211_check_wbrf_support(struct ieee80211_local *local);
|
||||||
void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef);
|
void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef);
|
||||||
|
@ -1546,6 +1546,18 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (ieee80211_is_action(mgmt->frame_control) &&
|
||||||
|
mgmt->u.action.category == WLAN_CATEGORY_PROTECTED_EHT) {
|
||||||
|
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||||
|
switch (mgmt->u.action.u.ttlm_req.action_code) {
|
||||||
|
case WLAN_PROTECTED_EHT_ACTION_TTLM_REQ:
|
||||||
|
ieee80211_process_neg_ttlm_req(sdata, mgmt,
|
||||||
|
skb->len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (ieee80211_is_ext(mgmt->frame_control)) {
|
} else if (ieee80211_is_ext(mgmt->frame_control)) {
|
||||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||||
ieee80211_sta_rx_queued_ext(sdata, skb);
|
ieee80211_sta_rx_queued_ext(sdata, skb);
|
||||||
|
@ -208,7 +208,8 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
|
|||||||
BSS_CHANGED_IBSS |\
|
BSS_CHANGED_IBSS |\
|
||||||
BSS_CHANGED_ARP_FILTER |\
|
BSS_CHANGED_ARP_FILTER |\
|
||||||
BSS_CHANGED_SSID |\
|
BSS_CHANGED_SSID |\
|
||||||
BSS_CHANGED_MLD_VALID_LINKS)
|
BSS_CHANGED_MLD_VALID_LINKS |\
|
||||||
|
BSS_CHANGED_MLD_TTLM)
|
||||||
|
|
||||||
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
|
||||||
u64 changed)
|
u64 changed)
|
||||||
|
@ -1318,8 +1318,6 @@ static void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata,
|
|||||||
cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EML_CAPA);
|
cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EML_CAPA);
|
||||||
skb_put_data(skb, &eml_capa, sizeof(eml_capa));
|
skb_put_data(skb, &eml_capa, sizeof(eml_capa));
|
||||||
}
|
}
|
||||||
/* need indication from userspace to support this */
|
|
||||||
mld_capa_ops &= ~cpu_to_le16(IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP);
|
|
||||||
skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops));
|
skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops));
|
||||||
|
|
||||||
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
|
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
|
||||||
@ -5890,6 +5888,56 @@ static void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata,
|
|||||||
TU_TO_JIFFIES(delay));
|
TU_TO_JIFFIES(delay));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ieee80211_ttlm_set_links(struct ieee80211_sub_if_data *sdata,
|
||||||
|
u16 active_links, u16 dormant_links,
|
||||||
|
u16 suspended_links)
|
||||||
|
{
|
||||||
|
u64 changed = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!active_links) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there is an active negotiated TTLM, it should be discarded by
|
||||||
|
* the new negotiated/advertised TTLM.
|
||||||
|
*/
|
||||||
|
if (sdata->vif.neg_ttlm.valid) {
|
||||||
|
memset(&sdata->vif.neg_ttlm, 0, sizeof(sdata->vif.neg_ttlm));
|
||||||
|
sdata->vif.suspended_links = 0;
|
||||||
|
changed = BSS_CHANGED_MLD_TTLM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdata->vif.active_links != active_links) {
|
||||||
|
ret = ieee80211_set_active_links(&sdata->vif, active_links);
|
||||||
|
if (ret) {
|
||||||
|
sdata_info(sdata, "Failed to set TTLM active links\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ieee80211_vif_set_links(sdata, sdata->vif.valid_links,
|
||||||
|
dormant_links);
|
||||||
|
if (ret) {
|
||||||
|
sdata_info(sdata, "Failed to set TTLM dormant links\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
changed |= BSS_CHANGED_MLD_VALID_LINKS;
|
||||||
|
sdata->vif.suspended_links = suspended_links;
|
||||||
|
if (sdata->vif.suspended_links)
|
||||||
|
changed |= BSS_CHANGED_MLD_TTLM;
|
||||||
|
|
||||||
|
ieee80211_vif_cfg_change_notify(sdata, changed);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (ret)
|
||||||
|
ieee80211_disconnect(&sdata->vif, false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy,
|
static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy,
|
||||||
struct wiphy_work *work)
|
struct wiphy_work *work)
|
||||||
{
|
{
|
||||||
@ -5897,30 +5945,19 @@ static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy,
|
|||||||
struct ieee80211_sub_if_data *sdata =
|
struct ieee80211_sub_if_data *sdata =
|
||||||
container_of(work, struct ieee80211_sub_if_data,
|
container_of(work, struct ieee80211_sub_if_data,
|
||||||
u.mgd.ttlm_work.work);
|
u.mgd.ttlm_work.work);
|
||||||
int ret;
|
|
||||||
|
|
||||||
new_active_links = sdata->u.mgd.ttlm_info.map &
|
new_active_links = sdata->u.mgd.ttlm_info.map &
|
||||||
sdata->vif.valid_links;
|
sdata->vif.valid_links;
|
||||||
new_dormant_links = ~sdata->u.mgd.ttlm_info.map &
|
new_dormant_links = ~sdata->u.mgd.ttlm_info.map &
|
||||||
sdata->vif.valid_links;
|
sdata->vif.valid_links;
|
||||||
if (!new_active_links) {
|
|
||||||
ieee80211_disconnect(&sdata->vif, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ieee80211_vif_set_links(sdata, sdata->vif.valid_links, 0);
|
ieee80211_vif_set_links(sdata, sdata->vif.valid_links, 0);
|
||||||
new_active_links = BIT(ffs(new_active_links) - 1);
|
if (ieee80211_ttlm_set_links(sdata, new_active_links, new_dormant_links,
|
||||||
ieee80211_set_active_links(&sdata->vif, new_active_links);
|
0))
|
||||||
|
return;
|
||||||
ret = ieee80211_vif_set_links(sdata, sdata->vif.valid_links,
|
|
||||||
new_dormant_links);
|
|
||||||
|
|
||||||
sdata->u.mgd.ttlm_info.active = true;
|
sdata->u.mgd.ttlm_info.active = true;
|
||||||
sdata->u.mgd.ttlm_info.switch_time = 0;
|
sdata->u.mgd.ttlm_info.switch_time = 0;
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
ieee80211_vif_cfg_change_notify(sdata,
|
|
||||||
BSS_CHANGED_MLD_VALID_LINKS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
|
static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
|
||||||
@ -6437,6 +6474,272 @@ free:
|
|||||||
kfree(elems);
|
kfree(elems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ieee80211_apply_neg_ttlm(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_neg_ttlm neg_ttlm)
|
||||||
|
{
|
||||||
|
u16 new_active_links, new_dormant_links, new_suspended_links, map = 0;
|
||||||
|
u8 i;
|
||||||
|
|
||||||
|
for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++)
|
||||||
|
map |= neg_ttlm.downlink[i] | neg_ttlm.uplink[i];
|
||||||
|
|
||||||
|
/* If there is an active TTLM, unset previously suspended links */
|
||||||
|
if (sdata->vif.neg_ttlm.valid)
|
||||||
|
sdata->vif.dormant_links &= ~sdata->vif.suspended_links;
|
||||||
|
|
||||||
|
/* exclude links that are already disabled by advertised TTLM */
|
||||||
|
new_active_links =
|
||||||
|
map & sdata->vif.valid_links & ~sdata->vif.dormant_links;
|
||||||
|
new_suspended_links =
|
||||||
|
(~map & sdata->vif.valid_links) & ~sdata->vif.dormant_links;
|
||||||
|
new_dormant_links = sdata->vif.dormant_links | new_suspended_links;
|
||||||
|
if (ieee80211_ttlm_set_links(sdata, new_active_links,
|
||||||
|
new_dormant_links, new_suspended_links))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sdata->vif.neg_ttlm = neg_ttlm;
|
||||||
|
sdata->vif.neg_ttlm.valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ieee80211_neg_ttlm_add_suggested_map(struct sk_buff *skb,
|
||||||
|
struct ieee80211_neg_ttlm *neg_ttlm)
|
||||||
|
{
|
||||||
|
u8 i, direction[IEEE80211_TTLM_MAX_CNT];
|
||||||
|
|
||||||
|
if (memcmp(neg_ttlm->downlink, neg_ttlm->uplink,
|
||||||
|
sizeof(neg_ttlm->downlink))) {
|
||||||
|
direction[0] = IEEE80211_TTLM_DIRECTION_DOWN;
|
||||||
|
direction[1] = IEEE80211_TTLM_DIRECTION_UP;
|
||||||
|
} else {
|
||||||
|
direction[0] = IEEE80211_TTLM_DIRECTION_BOTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(direction); i++) {
|
||||||
|
u8 tid, len, map_ind = 0, *len_pos, *map_ind_pos, *pos;
|
||||||
|
__le16 map;
|
||||||
|
|
||||||
|
len = sizeof(struct ieee80211_ttlm_elem) + 1 + 1;
|
||||||
|
|
||||||
|
pos = skb_put(skb, len + 2);
|
||||||
|
*pos++ = WLAN_EID_EXTENSION;
|
||||||
|
len_pos = pos++;
|
||||||
|
*pos++ = WLAN_EID_EXT_TID_TO_LINK_MAPPING;
|
||||||
|
*pos++ = direction[i];
|
||||||
|
map_ind_pos = pos++;
|
||||||
|
for (tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) {
|
||||||
|
map = direction[i] == IEEE80211_TTLM_DIRECTION_UP ?
|
||||||
|
cpu_to_le16(neg_ttlm->uplink[tid]) :
|
||||||
|
cpu_to_le16(neg_ttlm->downlink[tid]);
|
||||||
|
if (!map)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
len += 2;
|
||||||
|
map_ind |= BIT(tid);
|
||||||
|
skb_put_data(skb, &map, sizeof(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
*map_ind_pos = map_ind;
|
||||||
|
*len_pos = len;
|
||||||
|
|
||||||
|
if (direction[i] == IEEE80211_TTLM_DIRECTION_BOTH)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ieee80211_send_neg_ttlm_res(struct ieee80211_sub_if_data *sdata,
|
||||||
|
enum ieee80211_neg_ttlm_res ttlm_res,
|
||||||
|
u8 dialog_token,
|
||||||
|
struct ieee80211_neg_ttlm *neg_ttlm)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_mgmt *mgmt;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.ttlm_res);
|
||||||
|
int ttlm_max_len = 2 + 1 + sizeof(struct ieee80211_ttlm_elem) + 1 +
|
||||||
|
2 * 2 * IEEE80211_TTLM_NUM_TIDS;
|
||||||
|
|
||||||
|
skb = dev_alloc_skb(local->tx_headroom + hdr_len + ttlm_max_len);
|
||||||
|
if (!skb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
skb_reserve(skb, local->tx_headroom);
|
||||||
|
mgmt = skb_put_zero(skb, hdr_len);
|
||||||
|
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||||
|
IEEE80211_STYPE_ACTION);
|
||||||
|
memcpy(mgmt->da, sdata->vif.cfg.ap_addr, ETH_ALEN);
|
||||||
|
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||||
|
memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN);
|
||||||
|
|
||||||
|
mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_EHT;
|
||||||
|
mgmt->u.action.u.ttlm_res.action_code =
|
||||||
|
WLAN_PROTECTED_EHT_ACTION_TTLM_RES;
|
||||||
|
mgmt->u.action.u.ttlm_res.dialog_token = dialog_token;
|
||||||
|
switch (ttlm_res) {
|
||||||
|
default:
|
||||||
|
WARN_ON(1);
|
||||||
|
fallthrough;
|
||||||
|
case NEG_TTLM_RES_REJECT:
|
||||||
|
mgmt->u.action.u.ttlm_res.status_code =
|
||||||
|
WLAN_STATUS_DENIED_TID_TO_LINK_MAPPING;
|
||||||
|
break;
|
||||||
|
case NEG_TTLM_RES_ACCEPT:
|
||||||
|
mgmt->u.action.u.ttlm_res.status_code = WLAN_STATUS_SUCCESS;
|
||||||
|
break;
|
||||||
|
case NEG_TTLM_RES_SUGGEST_PREFERRED:
|
||||||
|
mgmt->u.action.u.ttlm_res.status_code =
|
||||||
|
WLAN_STATUS_PREF_TID_TO_LINK_MAPPING_SUGGESTED;
|
||||||
|
ieee80211_neg_ttlm_add_suggested_map(skb, neg_ttlm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ieee80211_tx_skb(sdata, skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ieee80211_parse_neg_ttlm(struct ieee80211_sub_if_data *sdata,
|
||||||
|
const struct ieee80211_ttlm_elem *ttlm,
|
||||||
|
struct ieee80211_neg_ttlm *neg_ttlm,
|
||||||
|
u8 *direction)
|
||||||
|
{
|
||||||
|
u8 control, link_map_presence, map_size, tid;
|
||||||
|
u8 *pos;
|
||||||
|
|
||||||
|
/* The element size was already validated in
|
||||||
|
* ieee80211_tid_to_link_map_size_ok()
|
||||||
|
*/
|
||||||
|
pos = (void *)ttlm->optional;
|
||||||
|
|
||||||
|
control = ttlm->control;
|
||||||
|
|
||||||
|
/* mapping switch time and expected duration fields are not expected
|
||||||
|
* in case of negotiated TTLM
|
||||||
|
*/
|
||||||
|
if (control & (IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT |
|
||||||
|
IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT)) {
|
||||||
|
mlme_dbg(sdata,
|
||||||
|
"Invalid TTLM element in negotiated TTLM request\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) {
|
||||||
|
for (tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) {
|
||||||
|
neg_ttlm->downlink[tid] = sdata->vif.valid_links;
|
||||||
|
neg_ttlm->uplink[tid] = sdata->vif.valid_links;
|
||||||
|
}
|
||||||
|
*direction = IEEE80211_TTLM_DIRECTION_BOTH;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*direction = u8_get_bits(control, IEEE80211_TTLM_CONTROL_DIRECTION);
|
||||||
|
if (*direction != IEEE80211_TTLM_DIRECTION_DOWN &&
|
||||||
|
*direction != IEEE80211_TTLM_DIRECTION_UP &&
|
||||||
|
*direction != IEEE80211_TTLM_DIRECTION_BOTH)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
link_map_presence = *pos;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE)
|
||||||
|
map_size = 1;
|
||||||
|
else
|
||||||
|
map_size = 2;
|
||||||
|
|
||||||
|
for (tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) {
|
||||||
|
u16 map;
|
||||||
|
|
||||||
|
if (link_map_presence & BIT(tid)) {
|
||||||
|
map = ieee80211_get_ttlm(map_size, pos);
|
||||||
|
if (!map) {
|
||||||
|
mlme_dbg(sdata,
|
||||||
|
"No active links for TID %d", tid);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
map = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*direction) {
|
||||||
|
case IEEE80211_TTLM_DIRECTION_BOTH:
|
||||||
|
neg_ttlm->downlink[tid] = map;
|
||||||
|
neg_ttlm->uplink[tid] = map;
|
||||||
|
break;
|
||||||
|
case IEEE80211_TTLM_DIRECTION_DOWN:
|
||||||
|
neg_ttlm->downlink[tid] = map;
|
||||||
|
break;
|
||||||
|
case IEEE80211_TTLM_DIRECTION_UP:
|
||||||
|
neg_ttlm->uplink[tid] = map;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
pos += map_size;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_mgmt *mgmt, size_t len)
|
||||||
|
{
|
||||||
|
u8 dialog_token, direction[IEEE80211_TTLM_MAX_CNT] = {}, i;
|
||||||
|
size_t ies_len;
|
||||||
|
enum ieee80211_neg_ttlm_res ttlm_res = NEG_TTLM_RES_ACCEPT;
|
||||||
|
struct ieee802_11_elems *elems = NULL;
|
||||||
|
struct ieee80211_neg_ttlm neg_ttlm = {};
|
||||||
|
|
||||||
|
BUILD_BUG_ON(ARRAY_SIZE(direction) != ARRAY_SIZE(elems->ttlm));
|
||||||
|
|
||||||
|
if (!ieee80211_vif_is_mld(&sdata->vif))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dialog_token = mgmt->u.action.u.ttlm_req.dialog_token;
|
||||||
|
ies_len = len - offsetof(struct ieee80211_mgmt,
|
||||||
|
u.action.u.ttlm_req.variable);
|
||||||
|
elems = ieee802_11_parse_elems(mgmt->u.action.u.ttlm_req.variable,
|
||||||
|
ies_len, true, NULL);
|
||||||
|
if (!elems) {
|
||||||
|
ttlm_res = NEG_TTLM_RES_REJECT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < elems->ttlm_num; i++) {
|
||||||
|
if (ieee80211_parse_neg_ttlm(sdata, elems->ttlm[i],
|
||||||
|
&neg_ttlm, &direction[i]) ||
|
||||||
|
(direction[i] == IEEE80211_TTLM_DIRECTION_BOTH &&
|
||||||
|
elems->ttlm_num != 1)) {
|
||||||
|
ttlm_res = NEG_TTLM_RES_REJECT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!elems->ttlm_num ||
|
||||||
|
(elems->ttlm_num == 2 && direction[0] == direction[1])) {
|
||||||
|
ttlm_res = NEG_TTLM_RES_REJECT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) {
|
||||||
|
if ((neg_ttlm.downlink[i] &&
|
||||||
|
(neg_ttlm.downlink[i] & ~sdata->vif.valid_links)) ||
|
||||||
|
(neg_ttlm.uplink[i] &&
|
||||||
|
(neg_ttlm.uplink[i] & ~sdata->vif.valid_links))) {
|
||||||
|
ttlm_res = NEG_TTLM_RES_REJECT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ttlm_res = drv_can_neg_ttlm(sdata->local, sdata, &neg_ttlm);
|
||||||
|
|
||||||
|
if (ttlm_res != NEG_TTLM_RES_ACCEPT)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ieee80211_apply_neg_ttlm(sdata, neg_ttlm);
|
||||||
|
out:
|
||||||
|
kfree(elems);
|
||||||
|
ieee80211_send_neg_ttlm_res(sdata, ttlm_res, dialog_token, &neg_ttlm);
|
||||||
|
}
|
||||||
|
|
||||||
void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
@ -3763,6 +3763,20 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case WLAN_CATEGORY_PROTECTED_EHT:
|
||||||
|
switch (mgmt->u.action.u.ttlm_req.action_code) {
|
||||||
|
case WLAN_PROTECTED_EHT_ACTION_TTLM_REQ:
|
||||||
|
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (len < offsetofend(typeof(*mgmt),
|
||||||
|
u.action.u.ttlm_req))
|
||||||
|
goto invalid;
|
||||||
|
goto queue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RX_CONTINUE;
|
return RX_CONTINUE;
|
||||||
|
@ -3088,6 +3088,58 @@ TRACE_EVENT(stop_queue,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(drv_can_neg_ttlm,
|
||||||
|
TP_PROTO(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_neg_ttlm *neg_ttlm),
|
||||||
|
|
||||||
|
TP_ARGS(local, sdata, neg_ttlm),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(LOCAL_ENTRY
|
||||||
|
VIF_ENTRY
|
||||||
|
__array(u16, downlink, sizeof(u16) * 8)
|
||||||
|
__array(u16, uplink, sizeof(u16) * 8)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(LOCAL_ASSIGN;
|
||||||
|
VIF_ASSIGN;
|
||||||
|
memcpy(__entry->downlink, neg_ttlm->downlink,
|
||||||
|
sizeof(neg_ttlm->downlink));
|
||||||
|
memcpy(__entry->uplink, neg_ttlm->uplink,
|
||||||
|
sizeof(neg_ttlm->uplink));
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(LOCAL_PR_FMT ", " VIF_PR_FMT, LOCAL_PR_ARG, VIF_PR_ARG)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(drv_neg_ttlm_res,
|
||||||
|
TP_PROTO(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
enum ieee80211_neg_ttlm_res res,
|
||||||
|
struct ieee80211_neg_ttlm *neg_ttlm),
|
||||||
|
|
||||||
|
TP_ARGS(local, sdata, res, neg_ttlm),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(LOCAL_ENTRY
|
||||||
|
VIF_ENTRY
|
||||||
|
__field(u32, res)
|
||||||
|
__array(u16, downlink, sizeof(u16) * 8)
|
||||||
|
__array(u16, uplink, sizeof(u16) * 8)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(LOCAL_ASSIGN;
|
||||||
|
VIF_ASSIGN;
|
||||||
|
__entry->res = res;
|
||||||
|
memcpy(__entry->downlink, neg_ttlm->downlink,
|
||||||
|
sizeof(neg_ttlm->downlink));
|
||||||
|
memcpy(__entry->uplink, neg_ttlm->uplink,
|
||||||
|
sizeof(neg_ttlm->uplink));
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk(LOCAL_PR_FMT VIF_PR_FMT " response: %d\n ",
|
||||||
|
LOCAL_PR_ARG, VIF_PR_ARG, __entry->res
|
||||||
|
)
|
||||||
|
);
|
||||||
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
|
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
|
||||||
|
|
||||||
#undef TRACE_INCLUDE_PATH
|
#undef TRACE_INCLUDE_PATH
|
||||||
|
Loading…
Reference in New Issue
Block a user