]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
nl80211: Driver command for checking BTM accept/reject
authorKanchanapally, Vidyullatha <vkanchan@qti.qualcomm.com>
Mon, 6 Mar 2017 11:43:10 +0000 (17:13 +0530)
committerJouni Malinen <j@w1.fi>
Mon, 6 Mar 2017 22:20:29 +0000 (00:20 +0200)
Add driver interface command using the QCA vendor extensions to check
the driverr whether to accept or reject a BSS transition candidate. For
the reject case, report an MBO reject reason code.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211.h
src/drivers/driver_nl80211_capa.c
wpa_supplicant/driver_i.h

index 7b3a6bd2f908e8d9400b62df38f461d56ef940e1..1d2dff9f2d245ba1fd200ebc85bff214fadc9ba0 100644 (file)
@@ -1898,6 +1898,20 @@ struct drv_acs_params {
        const int *freq_list;
 };
 
+struct wpa_bss_trans_info {
+       u8 mbo_transition_reason;
+       u8 n_candidates;
+       u8 *bssid;
+};
+
+struct wpa_bss_candidate_info {
+       u8 num;
+       struct candidate_list {
+               u8 bssid[ETH_ALEN];
+               u8 is_accept;
+               u32 reject_reason;
+       } *candidates;
+};
 
 /**
  * struct wpa_driver_ops - Driver interface API definition
@@ -3808,8 +3822,19 @@ struct wpa_driver_ops {
         * trigger control mode to the host driver.
         */
        int (*set_tdls_mode)(void *priv, int tdls_external_control);
-};
 
+       /**
+        * get_bss_transition_status - Get candidate BSS's transition status
+        * @priv: Private driver interface data
+        * @params: Candidate BSS list
+        *
+        * Get the accept or reject reason code for a list of BSS transition
+        * candidates.
+        */
+       struct wpa_bss_candidate_info *
+       (*get_bss_transition_status)(void *priv,
+                                    struct wpa_bss_trans_info *params);
+};
 
 /**
  * enum wpa_event_type - Event type for wpa_supplicant_event() calls
index e9107b3bba8e9ef9e9bbae95a18ff748332b2fc2..5d78113d4e9af93c280cd51401112bf496b0e216 100644 (file)
@@ -9655,6 +9655,204 @@ fail:
        return -1;
 }
 
+
+#ifdef CONFIG_MBO
+
+static enum mbo_transition_reject_reason
+nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status)
+{
+       switch (status) {
+       case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED:
+               return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+       case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED:
+               return MBO_TRANSITION_REJECT_REASON_DELAY;
+       case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY:
+               return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY;
+       case QCA_STATUS_REJECT_LOW_RSSI:
+               return MBO_TRANSITION_REJECT_REASON_RSSI;
+       case QCA_STATUS_REJECT_HIGH_INTERFERENCE:
+               return MBO_TRANSITION_REJECT_REASON_INTERFERENCE;
+       case QCA_STATUS_REJECT_UNKNOWN:
+       default:
+               return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
+       }
+}
+
+
+static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+                                            struct nlattr *tb[], int num)
+{
+       enum qca_wlan_btm_candidate_status status;
+       char buf[50];
+
+       os_memcpy(candidate->bssid,
+                 nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
+                 ETH_ALEN);
+
+       status = nla_get_u32(
+               tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]);
+       candidate->is_accept = status == QCA_STATUS_ACCEPT;
+       candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status);
+
+       if (candidate->is_accept)
+               os_snprintf(buf, sizeof(buf), "Accepted");
+       else
+               os_snprintf(buf, sizeof(buf),
+                           "Rejected, Reject_reason: %d",
+                           candidate->reject_reason);
+       wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR " %s",
+                  num, MAC2STR(candidate->bssid), buf);
+}
+
+
+static int
+nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+{
+       struct wpa_bss_candidate_info *info = arg;
+       struct candidate_list *candidate = info->candidates;
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+       struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
+       static struct nla_policy policy[
+               QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = {
+               [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
+                       .minlen = ETH_ALEN
+               },
+               [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
+                       .type = NLA_U32,
+               },
+       };
+       struct nlattr *attr;
+       int rem;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       u8 num;
+
+       num = info->num; /* number of candidates sent to driver */
+       info->num = 0;
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb_msg[NL80211_ATTR_VENDOR_DATA] ||
+           nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+                            tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) ||
+           !tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO])
+               return NL_SKIP;
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: WNM Candidate list received from driver");
+       nla_for_each_nested(attr,
+                           tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
+                           rem) {
+               if (info->num >= num ||
+                   nla_parse_nested(
+                           tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
+                           attr, policy) ||
+                   !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] ||
+                   !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS])
+                       break;
+
+               nl80211_parse_btm_candidate_info(candidate, tb, info->num);
+
+               candidate++;
+               info->num++;
+       }
+
+       return NL_SKIP;
+}
+
+
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *attr, *attr1, *attr2;
+       struct wpa_bss_candidate_info *info;
+       u8 i;
+       int ret;
+       u8 *pos;
+
+       if (!drv->fetch_bss_trans_status)
+               return NULL;
+
+       info = os_zalloc(sizeof(*info));
+       if (!info)
+               return NULL;
+       /* Allocate memory for number of candidates sent to driver */
+       info->candidates = os_calloc(params->n_candidates,
+                                    sizeof(*info->candidates));
+       if (!info->candidates) {
+               os_free(info);
+               return NULL;
+       }
+
+       /* Copy the number of candidates being sent to driver. This is used in
+        * nl80211_get_bss_transition_status_handler() to limit the number of
+        * candidates that can be populated in info->candidates and will be
+        * later overwritten with the actual number of candidates received from
+        * the driver.
+        */
+       info->num = params->n_candidates;
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
+               goto fail;
+
+       attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+       if (!attr)
+               goto fail;
+
+       if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON,
+                      params->mbo_transition_reason))
+               goto fail;
+
+       attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
+       if (!attr1)
+               goto fail;
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d",
+                  params->mbo_transition_reason, params->n_candidates);
+       pos = params->bssid;
+       for (i = 0; i < params->n_candidates; i++) {
+               wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR, i,
+                          MAC2STR(pos));
+               attr2 = nla_nest_start(msg, i);
+               if (!attr2 ||
+                   nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
+                           ETH_ALEN, pos))
+                       goto fail;
+               pos += ETH_ALEN;
+               nla_nest_end(msg, attr2);
+       }
+
+       nla_nest_end(msg, attr1);
+       nla_nest_end(msg, attr);
+
+       ret = send_and_recv_msgs(drv, msg,
+                                nl80211_get_bss_transition_status_handler,
+                                info);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_ERROR,
+                          "nl80211: WNM Get BSS transition status failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+               goto fail;
+       }
+       return info;
+
+fail:
+       nlmsg_free(msg);
+       os_free(info->candidates);
+       os_free(info);
+       return NULL;
+}
+
+#endif /* CONFIG_MBO */
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -9899,6 +10097,9 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .p2p_lo_stop = nl80211_p2p_lo_stop,
        .set_default_scan_ies = nl80211_set_default_scan_ies,
        .set_tdls_mode = nl80211_set_tdls_mode,
+#ifdef CONFIG_MBO
+       .get_bss_transition_status = nl80211_get_bss_transition_status,
+#endif /* CONFIG_MBO */
 #endif /* CONFIG_DRIVER_NL80211_QCA */
        .configure_data_frame_filters = nl80211_configure_data_frame_filters,
        .get_ext_capab = nl80211_get_ext_capab,
index bdc79c597670eee138c4e180dca1ca6d22e0b090..7e1f52c2562dcc649ba8ce627cbeaf68587ccccd 100644 (file)
@@ -162,6 +162,7 @@ struct wpa_driver_nl80211_data {
        unsigned int connect_reassoc:1;
        unsigned int set_wifi_conf_vendor_cmd_avail:1;
        unsigned int he_capab_vendor_cmd_avail:1;
+       unsigned int fetch_bss_trans_status:1;
 
        u64 vendor_scan_cookie;
        u64 remain_on_chan_cookie;
index 7064ce1d5b9f4b17e40f8f06ba6921f85340069e..d20b04cc9957e35500044559b3f760e30b865608 100644 (file)
@@ -747,6 +747,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                                case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES:
                                        drv->he_capab_vendor_cmd_avail = 1;
                                        break;
+                               case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
+                                       drv->fetch_bss_trans_status = 1;
+                                       break;
 #endif /* CONFIG_DRIVER_NL80211_QCA */
                                }
                        }
index 0af63c9f15ed55af01e57d504a9e8ddc5938c1f7..cc41b27e993f481a9a0982fae1c9b0dae6e6e60f 100644 (file)
@@ -989,4 +989,14 @@ static inline int wpa_drv_set_tdls_mode(struct wpa_supplicant *wpa_s,
                                            tdls_external_control);
 }
 
+static inline struct wpa_bss_candidate_info *
+wpa_drv_get_bss_trans_status(struct wpa_supplicant *wpa_s,
+                            struct wpa_bss_trans_info *params)
+{
+       if (!wpa_s->driver->get_bss_transition_status)
+               return NULL;
+       return wpa_s->driver->get_bss_transition_status(wpa_s->drv_priv,
+                                                       params);
+}
+
 #endif /* DRIVER_I_H */