diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 7b3a6bd2f..1d2dff9f2 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -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 diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index e9107b3bb..5d78113d4 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -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, diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index bdc79c597..7e1f52c25 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -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; diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 7064ce1d5..d20b04cc9 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -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 */ } } diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 0af63c9f1..cc41b27e9 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -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 */