nl80211: Driver command for checking BTM accept/reject

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>
This commit is contained in:
Kanchanapally, Vidyullatha 2017-03-06 17:13:10 +05:30 committed by Jouni Malinen
parent ae2f1a9c0f
commit 3ab484928a
5 changed files with 241 additions and 1 deletions

View file

@ -1898,6 +1898,20 @@ struct drv_acs_params {
const int *freq_list; 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 * struct wpa_driver_ops - Driver interface API definition
@ -3808,8 +3822,19 @@ struct wpa_driver_ops {
* trigger control mode to the host driver. * trigger control mode to the host driver.
*/ */
int (*set_tdls_mode)(void *priv, int tdls_external_control); 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 * enum wpa_event_type - Event type for wpa_supplicant_event() calls

View file

@ -9655,6 +9655,204 @@ fail:
return -1; 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 */ #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, .p2p_lo_stop = nl80211_p2p_lo_stop,
.set_default_scan_ies = nl80211_set_default_scan_ies, .set_default_scan_ies = nl80211_set_default_scan_ies,
.set_tdls_mode = nl80211_set_tdls_mode, .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 */ #endif /* CONFIG_DRIVER_NL80211_QCA */
.configure_data_frame_filters = nl80211_configure_data_frame_filters, .configure_data_frame_filters = nl80211_configure_data_frame_filters,
.get_ext_capab = nl80211_get_ext_capab, .get_ext_capab = nl80211_get_ext_capab,

View file

@ -162,6 +162,7 @@ struct wpa_driver_nl80211_data {
unsigned int connect_reassoc:1; unsigned int connect_reassoc:1;
unsigned int set_wifi_conf_vendor_cmd_avail:1; unsigned int set_wifi_conf_vendor_cmd_avail:1;
unsigned int he_capab_vendor_cmd_avail:1; unsigned int he_capab_vendor_cmd_avail:1;
unsigned int fetch_bss_trans_status:1;
u64 vendor_scan_cookie; u64 vendor_scan_cookie;
u64 remain_on_chan_cookie; u64 remain_on_chan_cookie;

View file

@ -747,6 +747,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES:
drv->he_capab_vendor_cmd_avail = 1; drv->he_capab_vendor_cmd_avail = 1;
break; break;
case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
drv->fetch_bss_trans_status = 1;
break;
#endif /* CONFIG_DRIVER_NL80211_QCA */ #endif /* CONFIG_DRIVER_NL80211_QCA */
} }
} }

View file

@ -989,4 +989,14 @@ static inline int wpa_drv_set_tdls_mode(struct wpa_supplicant *wpa_s,
tdls_external_control); 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 */ #endif /* DRIVER_I_H */