wifi: mac80211: process and save negotiated TID to Link mapping request
authorAyala Beker <ayala.beker@intel.com>
Tue, 2 Jan 2024 19:35:34 +0000 (21:35 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 26 Jan 2024 08:39:48 +0000 (09:39 +0100)
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>
include/net/mac80211.h
net/mac80211/driver-ops.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/trace.h

index d400fe2e8668d2cb70b22f0c5c70378bbdb78c01..6490b92d5cc1ba0272f648d01cb13c4541aa942a 100644 (file)
@@ -342,6 +342,7 @@ struct ieee80211_vif_chanctx_switch {
  *     status changed.
  * @BSS_CHANGED_EHT_PUNCTURING: The channel puncturing bitmap 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 {
        BSS_CHANGED_ASSOC               = 1<<0,
@@ -378,6 +379,7 @@ enum ieee80211_bss_change {
        BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = 1<<31,
        BSS_CHANGED_EHT_PUNCTURING      = BIT_ULL(32),
        BSS_CHANGED_MLD_VALID_LINKS     = BIT_ULL(33),
+       BSS_CHANGED_MLD_TTLM            = BIT_ULL(34),
 
        /* when adding here, make sure to change ieee80211_reconfig */
 };
@@ -1845,6 +1847,35 @@ struct ieee80211_vif_cfg {
        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
  *
@@ -1863,6 +1894,11 @@ struct ieee80211_vif_cfg {
  *     API calls meant for that purpose.
  * @dormant_links: bitmap of valid but disabled links, or 0 for non-MLO.
  *     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
  * @p2p: indicates whether this AP or STA interface is a p2p
  *     interface, i.e. a GO or p2p-sta respectively
@@ -1900,7 +1936,8 @@ struct ieee80211_vif {
        struct ieee80211_vif_cfg cfg;
        struct ieee80211_bss_conf bss_conf;
        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);
        bool p2p;
 
@@ -4293,6 +4330,10 @@ struct ieee80211_prep_tx_info {
  *     flow offloading for flows originating from the vif.
  *     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.
+ * @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 {
        void (*tx)(struct ieee80211_hw *hw,
@@ -4673,6 +4714,9 @@ struct ieee80211_ops {
                            struct net_device *dev,
                            enum tc_setup_type type,
                            void *type_data);
+       enum ieee80211_neg_ttlm_res
+       (*can_neg_ttlm)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                       struct ieee80211_neg_ttlm *ttlm);
 };
 
 /**
index eb482fb8c3afe5b513d7cbc85eabb11cd3155aae..e20c64edb880ce156fb7668a723c1a1bf1343765 100644 (file)
@@ -1695,4 +1695,23 @@ int drv_change_sta_links(struct ieee80211_local *local,
                         struct ieee80211_sta *sta,
                         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 */
index 0b2b53550bd99f234d722deda957b8160bf89754..38755f6f6fa0b0ae1667fa39f2763723f8e7c611 100644 (file)
@@ -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,
                                    u8 eht_cap_len,
                                    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_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef);
index e4e7c0b38cb6efcbb65786d071f436e09c7bf322..4a87d2d336ae245cdfcd6cbccef51a9c95ba14b6 100644 (file)
@@ -1546,6 +1546,18 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
                default:
                        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)) {
                if (sdata->vif.type == NL80211_IFTYPE_STATION)
                        ieee80211_sta_rx_queued_ext(sdata, skb);
index f2ece77935739fe47e398f50c466fc6cbdaff0c5..13c417eda28106faaee7bc61a91e239abe924b21 100644 (file)
@@ -208,7 +208,8 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
                                   BSS_CHANGED_IBSS |\
                                   BSS_CHANGED_ARP_FILTER |\
                                   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,
                                      u64 changed)
index 073105deb42481f2792a33fa0341d509f7a95017..b56f7de3b53fad8a742efb9951a9b24c543135ad 100644 (file)
@@ -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);
                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));
 
        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));
 }
 
+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,
                                           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 =
                container_of(work, struct ieee80211_sub_if_data,
                             u.mgd.ttlm_work.work);
-       int ret;
 
        new_active_links = sdata->u.mgd.ttlm_info.map &
                           sdata->vif.valid_links;
        new_dormant_links = ~sdata->u.mgd.ttlm_info.map &
                            sdata->vif.valid_links;
-       if (!new_active_links) {
-               ieee80211_disconnect(&sdata->vif, false);
-               return;
-       }
 
        ieee80211_vif_set_links(sdata, sdata->vif.valid_links, 0);
-       new_active_links = BIT(ffs(new_active_links) - 1);
-       ieee80211_set_active_links(&sdata->vif, new_active_links);
-
-       ret = ieee80211_vif_set_links(sdata, sdata->vif.valid_links,
-                                     new_dormant_links);
+       if (ieee80211_ttlm_set_links(sdata, new_active_links, new_dormant_links,
+                                    0))
+               return;
 
        sdata->u.mgd.ttlm_info.active = true;
        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)
@@ -6437,6 +6474,272 @@ free:
        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,
                                 struct sk_buff *skb)
 {
index 0bf72928ccfca9cb4320200d51a3278b109ce7d2..75eb3e55eaecfb27efcbe25a89092a09df2d5ac2 100644 (file)
@@ -3763,6 +3763,20 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                        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;
index 06835ed4c44f136ed2cbcad79c1235688c7b7ed3..1c0c46b11c6d7363fb7c6d0330f5c650d1d665df 100644 (file)
@@ -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 */
 
 #undef TRACE_INCLUDE_PATH