MLD STA: Add per-link MLO signal poll
Add support to fetch and indicate per-link MLO signal poll information via a new control interface command MLO_SIGNAL_POLL. Signed-off-by: Veerendranath Jakkam <quic_vjakkam@quicinc.com>
This commit is contained in:
parent
28b2256da2
commit
b7f98d92dc
5 changed files with 271 additions and 0 deletions
|
@ -2392,6 +2392,11 @@ struct wpa_signal_info {
|
||||||
int center_frq2;
|
int center_frq2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct wpa_mlo_signal_info {
|
||||||
|
u16 valid_links;
|
||||||
|
struct wpa_signal_info links[MAX_NUM_MLD_LINKS];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct wpa_channel_info - Information about the current channel
|
* struct wpa_channel_info - Information about the current channel
|
||||||
* @frequency: Center frequency of the primary 20 MHz channel
|
* @frequency: Center frequency of the primary 20 MHz channel
|
||||||
|
@ -3986,6 +3991,14 @@ struct wpa_driver_ops {
|
||||||
*/
|
*/
|
||||||
int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
|
int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlo_signal_poll - Get current MLO connection information
|
||||||
|
* @priv: Private driver interface data
|
||||||
|
* @mlo_signal_info: MLO connection info structure
|
||||||
|
*/
|
||||||
|
int (*mlo_signal_poll)(void *priv,
|
||||||
|
struct wpa_mlo_signal_info *mlo_signal_info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* channel_info - Get parameters of the current operating channel
|
* channel_info - Get parameters of the current operating channel
|
||||||
* @priv: Private driver interface data
|
* @priv: Private driver interface data
|
||||||
|
|
|
@ -8775,6 +8775,163 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int get_links_noise(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
struct nlattr *tb[NL80211_ATTR_MAX + 1];
|
||||||
|
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||||
|
struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
|
||||||
|
static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
|
||||||
|
[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
|
||||||
|
[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
|
||||||
|
};
|
||||||
|
struct wpa_mlo_signal_info *mlo_sig = arg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||||
|
genlmsg_attrlen(gnlh, 0), NULL);
|
||||||
|
|
||||||
|
if (!tb[NL80211_ATTR_SURVEY_INFO]) {
|
||||||
|
wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
|
||||||
|
tb[NL80211_ATTR_SURVEY_INFO],
|
||||||
|
survey_policy)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"nl80211: Failed to parse nested attributes");
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
|
||||||
|
return NL_SKIP;
|
||||||
|
|
||||||
|
if (!sinfo[NL80211_SURVEY_INFO_NOISE])
|
||||||
|
return NL_SKIP;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
|
||||||
|
if (!(mlo_sig->valid_links & BIT(i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
|
||||||
|
mlo_sig->links[i].frequency)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mlo_sig->links[i].current_noise =
|
||||||
|
(s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int nl80211_get_links_noise(struct wpa_driver_nl80211_data *drv,
|
||||||
|
struct wpa_mlo_signal_info *mlo_sig)
|
||||||
|
{
|
||||||
|
struct nl_msg *msg;
|
||||||
|
|
||||||
|
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
|
||||||
|
return send_and_recv_msgs(drv, msg, get_links_noise, mlo_sig,
|
||||||
|
NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int get_links_channel_width(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
struct nlattr *tb[NL80211_ATTR_MAX + 1];
|
||||||
|
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||||
|
struct wpa_mlo_signal_info *mlo_sig = arg;
|
||||||
|
struct nlattr *link;
|
||||||
|
int rem_links;
|
||||||
|
|
||||||
|
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||||
|
genlmsg_attrlen(gnlh, 0), NULL);
|
||||||
|
|
||||||
|
if (!tb[NL80211_ATTR_MLO_LINKS])
|
||||||
|
return NL_SKIP;
|
||||||
|
|
||||||
|
nla_for_each_nested(link, tb[NL80211_ATTR_MLO_LINKS], rem_links) {
|
||||||
|
struct nlattr *tb2[NL80211_ATTR_MAX + 1];
|
||||||
|
int link_id;
|
||||||
|
|
||||||
|
nla_parse(tb2, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!tb2[NL80211_ATTR_MLO_LINK_ID])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
link_id = nla_get_u8(tb2[NL80211_ATTR_MLO_LINK_ID]);
|
||||||
|
if (link_id >= MAX_NUM_MLD_LINKS)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!tb2[NL80211_ATTR_CHANNEL_WIDTH])
|
||||||
|
continue;
|
||||||
|
mlo_sig->links[link_id].chanwidth = convert2width(
|
||||||
|
nla_get_u32(tb2[NL80211_ATTR_CHANNEL_WIDTH]));
|
||||||
|
if (tb2[NL80211_ATTR_CENTER_FREQ1])
|
||||||
|
mlo_sig->links[link_id].center_frq1 =
|
||||||
|
nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ1]);
|
||||||
|
if (tb2[NL80211_ATTR_CENTER_FREQ2])
|
||||||
|
mlo_sig->links[link_id].center_frq2 =
|
||||||
|
nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int nl80211_get_links_channel_width(struct wpa_driver_nl80211_data *drv,
|
||||||
|
struct wpa_mlo_signal_info *mlo_sig)
|
||||||
|
{
|
||||||
|
struct nl_msg *msg;
|
||||||
|
|
||||||
|
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
|
||||||
|
return send_and_recv_msgs(drv, msg, get_links_channel_width, mlo_sig,
|
||||||
|
NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int nl80211_mlo_signal_poll(void *priv,
|
||||||
|
struct wpa_mlo_signal_info *mlo_si)
|
||||||
|
{
|
||||||
|
struct i802_bss *bss = priv;
|
||||||
|
struct wpa_driver_nl80211_data *drv = bss->drv;
|
||||||
|
int res;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (drv->nlmode != NL80211_IFTYPE_STATION ||
|
||||||
|
!drv->sta_mlo_info.valid_links)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
os_memset(mlo_si, 0, sizeof(*mlo_si));
|
||||||
|
mlo_si->valid_links = drv->sta_mlo_info.valid_links;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
|
||||||
|
if (!(mlo_si->valid_links & BIT(i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
res = nl80211_get_link_signal(drv,
|
||||||
|
drv->sta_mlo_info.links[i].bssid,
|
||||||
|
&mlo_si->links[i]);
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
mlo_si->links[i].center_frq1 = -1;
|
||||||
|
mlo_si->links[i].center_frq2 = -1;
|
||||||
|
mlo_si->links[i].chanwidth = CHAN_WIDTH_UNKNOWN;
|
||||||
|
mlo_si->links[i].current_noise = WPA_INVALID_NOISE;
|
||||||
|
mlo_si->links[i].frequency = drv->sta_mlo_info.links[i].freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = nl80211_get_links_channel_width(drv, mlo_si);
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
return nl80211_get_links_noise(drv, mlo_si);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int nl80211_set_param(void *priv, const char *param)
|
static int nl80211_set_param(void *priv, const char *param)
|
||||||
{
|
{
|
||||||
struct i802_bss *bss = priv;
|
struct i802_bss *bss = priv;
|
||||||
|
@ -12710,6 +12867,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
|
||||||
.resume = wpa_driver_nl80211_resume,
|
.resume = wpa_driver_nl80211_resume,
|
||||||
.signal_monitor = nl80211_signal_monitor,
|
.signal_monitor = nl80211_signal_monitor,
|
||||||
.signal_poll = nl80211_signal_poll,
|
.signal_poll = nl80211_signal_poll,
|
||||||
|
.mlo_signal_poll = nl80211_mlo_signal_poll,
|
||||||
.channel_info = nl80211_channel_info,
|
.channel_info = nl80211_channel_info,
|
||||||
.set_param = nl80211_set_param,
|
.set_param = nl80211_set_param,
|
||||||
.get_radio_name = nl80211_get_radio_name,
|
.get_radio_name = nl80211_get_radio_name,
|
||||||
|
|
|
@ -11540,6 +11540,86 @@ static int wpas_ctrl_iface_send_dscp_query(struct wpa_supplicant *wpa_s,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpas_ctrl_iface_mlo_signal_poll(struct wpa_supplicant *wpa_s,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
char *pos, *end;
|
||||||
|
struct wpa_mlo_signal_info mlo_si;
|
||||||
|
|
||||||
|
if (!wpa_s->valid_links)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = wpa_drv_mlo_signal_poll(wpa_s, &mlo_si);
|
||||||
|
if (ret)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = buf;
|
||||||
|
end = buf + buflen;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
|
||||||
|
if (!(mlo_si.valid_links & BIT(i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = os_snprintf(pos, end - pos,
|
||||||
|
"LINK_ID=%d\nRSSI=%d\nLINKSPEED=%d\n"
|
||||||
|
"NOISE=%d\nFREQUENCY=%u\n",
|
||||||
|
i, mlo_si.links[i].current_signal,
|
||||||
|
mlo_si.links[i].current_txrate / 1000,
|
||||||
|
mlo_si.links[i].current_noise,
|
||||||
|
mlo_si.links[i].frequency);
|
||||||
|
if (os_snprintf_error(end - pos, ret))
|
||||||
|
return -1;
|
||||||
|
pos += ret;
|
||||||
|
|
||||||
|
if (mlo_si.links[i].chanwidth != CHAN_WIDTH_UNKNOWN) {
|
||||||
|
ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
|
||||||
|
channel_width_to_string(
|
||||||
|
mlo_si.links[i].chanwidth));
|
||||||
|
if (os_snprintf_error(end - pos, ret))
|
||||||
|
return -1;
|
||||||
|
pos += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mlo_si.links[i].center_frq1 > 0) {
|
||||||
|
ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\n",
|
||||||
|
mlo_si.links[i].center_frq1);
|
||||||
|
if (os_snprintf_error(end - pos, ret))
|
||||||
|
return -1;
|
||||||
|
pos += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mlo_si.links[i].center_frq2 > 0) {
|
||||||
|
ret = os_snprintf(pos, end - pos, "CENTER_FRQ2=%d\n",
|
||||||
|
mlo_si.links[i].center_frq2);
|
||||||
|
if (os_snprintf_error(end - pos, ret))
|
||||||
|
return -1;
|
||||||
|
pos += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mlo_si.links[i].avg_signal) {
|
||||||
|
ret = os_snprintf(pos, end - pos,
|
||||||
|
"AVG_RSSI=%d\n",
|
||||||
|
mlo_si.links[i].avg_signal);
|
||||||
|
if (os_snprintf_error(end - pos, ret))
|
||||||
|
return -1;
|
||||||
|
pos += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mlo_si.links[i].avg_beacon_signal) {
|
||||||
|
ret = os_snprintf(pos, end - pos,
|
||||||
|
"AVG_BEACON_RSSI=%d\n",
|
||||||
|
mlo_si.links[i].avg_beacon_signal);
|
||||||
|
if (os_snprintf_error(end - pos, ret))
|
||||||
|
return -1;
|
||||||
|
pos += ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos - buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int wpas_ctrl_iface_mlo_status(struct wpa_supplicant *wpa_s,
|
static int wpas_ctrl_iface_mlo_status(struct wpa_supplicant *wpa_s,
|
||||||
char *buf, size_t buflen)
|
char *buf, size_t buflen)
|
||||||
{
|
{
|
||||||
|
@ -12588,6 +12668,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
|
||||||
} else if (os_strcmp(buf, "MLO_STATUS") == 0) {
|
} else if (os_strcmp(buf, "MLO_STATUS") == 0) {
|
||||||
reply_len = wpas_ctrl_iface_mlo_status(wpa_s, reply,
|
reply_len = wpas_ctrl_iface_mlo_status(wpa_s, reply,
|
||||||
reply_size);
|
reply_size);
|
||||||
|
} else if (os_strcmp(buf, "MLO_SIGNAL_POLL") == 0) {
|
||||||
|
reply_len = wpas_ctrl_iface_mlo_signal_poll(wpa_s, reply,
|
||||||
|
reply_size);
|
||||||
} else {
|
} else {
|
||||||
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
||||||
reply_len = 16;
|
reply_len = 16;
|
||||||
|
|
|
@ -523,6 +523,14 @@ static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s,
|
||||||
int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
|
int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
|
||||||
struct wpa_signal_info *si);
|
struct wpa_signal_info *si);
|
||||||
|
|
||||||
|
static inline int wpa_drv_mlo_signal_poll(struct wpa_supplicant *wpa_s,
|
||||||
|
struct wpa_mlo_signal_info *mlo_si)
|
||||||
|
{
|
||||||
|
if (wpa_s->driver->mlo_signal_poll)
|
||||||
|
return wpa_s->driver->mlo_signal_poll(wpa_s->drv_priv, mlo_si);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int wpa_drv_channel_info(struct wpa_supplicant *wpa_s,
|
static inline int wpa_drv_channel_info(struct wpa_supplicant *wpa_s,
|
||||||
struct wpa_channel_info *ci)
|
struct wpa_channel_info *ci)
|
||||||
{
|
{
|
||||||
|
|
|
@ -419,6 +419,12 @@ static int wpa_cli_cmd_mlo_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_cli_cmd_mlo_signal_poll(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return wpa_ctrl_command(ctrl, "MLO_SIGNAL_POLL");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char cmd[256];
|
char cmd[256];
|
||||||
|
@ -4046,6 +4052,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
|
||||||
{ "mlo_status", wpa_cli_cmd_mlo_status, NULL,
|
{ "mlo_status", wpa_cli_cmd_mlo_status, NULL,
|
||||||
cli_cmd_flag_none,
|
cli_cmd_flag_none,
|
||||||
"= get MLO status" },
|
"= get MLO status" },
|
||||||
|
{ "mlo_signal_poll", wpa_cli_cmd_mlo_signal_poll, NULL,
|
||||||
|
cli_cmd_flag_none,
|
||||||
|
"= get mlo signal parameters" },
|
||||||
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
|
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue