wifi: mac80211: Parse station profile from association response
[carl9170fw.git] / include / linux / ieee80211.h
index c27acf67d88f6527f0d27c2591cbfa791883e02f..a4e66d4bce86291500da3f721d6fbfb9a4a5600d 100644 (file)
@@ -308,9 +308,11 @@ struct ieee80211_hdr {
 struct ieee80211_hdr_3addr {
        __le16 frame_control;
        __le16 duration_id;
-       u8 addr1[6];
-       u8 addr2[6];
-       u8 addr3[6];
+       struct_group(addrs,
+               u8 addr1[6];
+               u8 addr2[6];
+               u8 addr3[6];
+       );
        __le16 seq_ctrl;
 } __packed __aligned(2);
 
@@ -1333,6 +1335,15 @@ struct ieee80211_mgmt {
                                        u8 action_code;
                                        u8 variable[];
                                } __packed s1g;
+                               struct {
+                                       u8 action_code;
+                                       u8 dialog_token;
+                                       u8 follow_up;
+                                       u32 tod;
+                                       u32 toa;
+                                       u8 max_tod_error;
+                                       u8 max_toa_error;
+                               } __packed wnm_timing_msr;
                        } u;
                } __packed __aligned(4) action;
        } u __aligned(2);
@@ -2895,7 +2906,8 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
 /* Calculate 802.11be EHT capabilities IE Tx/Rx EHT MCS NSS Support Field size */
 static inline u8
 ieee80211_eht_mcs_nss_size(const struct ieee80211_he_cap_elem *he_cap,
-                          const struct ieee80211_eht_cap_elem_fixed *eht_cap)
+                          const struct ieee80211_eht_cap_elem_fixed *eht_cap,
+                          bool from_ap)
 {
        u8 count = 0;
 
@@ -2916,7 +2928,10 @@ ieee80211_eht_mcs_nss_size(const struct ieee80211_he_cap_elem *he_cap,
        if (eht_cap->phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)
                count += 3;
 
-       return count ? count : 4;
+       if (count)
+               return count;
+
+       return from_ap ? 3 : 4;
 }
 
 /* 802.11be EHT PPE Thresholds */
@@ -2952,7 +2967,8 @@ ieee80211_eht_ppe_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
 }
 
 static inline bool
-ieee80211_eht_capa_size_ok(const u8 *he_capa, const u8 *data, u8 len)
+ieee80211_eht_capa_size_ok(const u8 *he_capa, const u8 *data, u8 len,
+                          bool from_ap)
 {
        const struct ieee80211_eht_cap_elem_fixed *elem = (const void *)data;
        u8 needed = sizeof(struct ieee80211_eht_cap_elem_fixed);
@@ -2961,7 +2977,8 @@ ieee80211_eht_capa_size_ok(const u8 *he_capa, const u8 *data, u8 len)
                return false;
 
        needed += ieee80211_eht_mcs_nss_size((const void *)he_capa,
-                                            (const void *)data);
+                                            (const void *)data,
+                                            from_ap);
        if (len < needed)
                return false;
 
@@ -3540,6 +3557,17 @@ enum ieee80211_mesh_actioncode {
        WLAN_MESH_ACTION_TBTT_ADJUSTMENT_RESPONSE,
 };
 
+/* Unprotected WNM action codes */
+enum ieee80211_unprotected_wnm_actioncode {
+       WLAN_UNPROTECTED_WNM_ACTION_TIM = 0,
+       WLAN_UNPROTECTED_WNM_ACTION_TIMING_MEASUREMENT_RESPONSE = 1,
+};
+
+/* Public action codes */
+enum ieee80211_public_actioncode {
+       WLAN_PUBLIC_ACTION_FTM_RESPONSE = 33,
+};
+
 /* Security key length */
 enum ieee80211_key_len {
        WLAN_KEY_LEN_WEP40 = 5,
@@ -4228,6 +4256,40 @@ static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim,
 #define TU_TO_JIFFIES(x)       (usecs_to_jiffies((x) * 1024))
 #define TU_TO_EXP_TIME(x)      (jiffies + TU_TO_JIFFIES(x))
 
+static inline bool ieee80211_is_timing_measurement(struct ieee80211_hdr *hdr, size_t len)
+{
+       struct ieee80211_mgmt *mgmt = (void *)hdr;
+
+       if (len < IEEE80211_MIN_ACTION_SIZE)
+               return false;
+
+       if (!ieee80211_is_action(hdr->frame_control))
+               return false;
+
+       if (mgmt->u.action.category == WLAN_CATEGORY_WNM_UNPROTECTED &&
+           mgmt->u.action.u.wnm_timing_msr.action_code ==
+               WLAN_UNPROTECTED_WNM_ACTION_TIMING_MEASUREMENT_RESPONSE &&
+           len >= offsetofend(typeof(*mgmt), u.action.u.wnm_timing_msr))
+               return true;
+
+       return false;
+}
+
+static inline bool ieee80211_is_ftm(struct ieee80211_hdr *hdr, size_t len)
+{
+       struct ieee80211_mgmt *mgmt = (void *)hdr;
+
+       if (!ieee80211_is_public_action((void *)mgmt, len))
+               return false;
+
+       if (mgmt->u.action.u.ftm.action_code ==
+               WLAN_PUBLIC_ACTION_FTM_RESPONSE &&
+           len >= offsetofend(typeof(*mgmt), u.action.u.ftm))
+               return true;
+
+       return false;
+}
+
 struct element {
        u8 id;
        u8 datalen;
@@ -4440,18 +4502,17 @@ static inline u8 ieee80211_mle_common_size(const u8 *data)
 
        switch (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE)) {
        case IEEE80211_ML_CONTROL_TYPE_BASIC:
-               common += sizeof(struct ieee80211_mle_basic_common_info);
-               break;
        case IEEE80211_ML_CONTROL_TYPE_PREQ:
-               common += sizeof(struct ieee80211_mle_preq_common_info);
+       case IEEE80211_ML_CONTROL_TYPE_TDLS:
+               /*
+                * The length is the first octet pointed by mle->variable so no
+                * need to add anything
+                */
                break;
        case IEEE80211_ML_CONTROL_TYPE_RECONF:
                if (control & IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR)
                        common += 6;
                return common;
-       case IEEE80211_ML_CONTROL_TYPE_TDLS:
-               common += sizeof(struct ieee80211_mle_tdls_common_info);
-               break;
        case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS:
                if (control & IEEE80211_MLC_PRIO_ACCESS_PRES_AP_MLD_MAC_ADDR)
                        common += 6;
@@ -4468,7 +4529,7 @@ static inline u8 ieee80211_mle_common_size(const u8 *data)
  * @data: pointer to the element data
  * @len: length of the containing element
  */
-static inline bool ieee80211_mle_size_ok(const u8 *data, u8 len)
+static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len)
 {
        const struct ieee80211_multi_link_elem *mle = (const void *)data;
        u8 fixed = sizeof(*mle);
@@ -4533,6 +4594,7 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, u8 len)
 
 enum ieee80211_mle_subelems {
        IEEE80211_MLE_SUBELEM_PER_STA_PROFILE           = 0,
+       IEEE80211_MLE_SUBELEM_FRAGMENT                  = 254,
 };
 
 #define IEEE80211_MLE_STA_CONTROL_LINK_ID                      0x000f
@@ -4551,6 +4613,46 @@ struct ieee80211_mle_per_sta_profile {
        u8 variable[];
 } __packed;
 
+/**
+ * ieee80211_mle_sta_prof_size_ok - validate multi-link element sta profile size
+ * @data: pointer to the sub element data
+ * @len: length of the containing sub element
+ */
+static inline bool ieee80211_mle_sta_prof_size_ok(const u8 *data, size_t len)
+{
+       const struct ieee80211_mle_per_sta_profile *prof = (const void *)data;
+       u16 control;
+       u8 fixed = sizeof(*prof);
+       u8 info_len = 1;
+
+       if (len < fixed)
+               return false;
+
+       control = le16_to_cpu(prof->control);
+
+       if (control & IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT)
+               info_len += 6;
+       if (control & IEEE80211_MLE_STA_CONTROL_BEACON_INT_PRESENT)
+               info_len += 2;
+       if (control & IEEE80211_MLE_STA_CONTROL_TSF_OFFS_PRESENT)
+               info_len += 8;
+       if (control & IEEE80211_MLE_STA_CONTROL_DTIM_INFO_PRESENT)
+               info_len += 2;
+       if (control & IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT)
+               info_len += 1;
+
+       if (control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE &&
+           control & IEEE80211_MLE_STA_CONTROL_NSTR_BITMAP_SIZE) {
+               if (control & IEEE80211_MLE_STA_CONTROL_NSTR_BITMAP_SIZE)
+                       info_len += 2;
+               else
+                       info_len += 1;
+       }
+
+       return prof->sta_info_len >= info_len &&
+              fixed + prof->sta_info_len <= len;
+}
+
 #define for_each_mle_subelement(_elem, _data, _len)                    \
        if (ieee80211_mle_size_ok(_data, _len))                         \
                for_each_element(_elem,                                 \