]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
MLD STA: Add per-link MLO signal poll
authorVeerendranath Jakkam <quic_vjakkam@quicinc.com>
Thu, 8 Sep 2022 14:44:21 +0000 (20:14 +0530)
committerJouni Malinen <j@w1.fi>
Thu, 15 Sep 2022 13:00:51 +0000 (16:00 +0300)
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>
src/drivers/driver.h
src/drivers/driver_nl80211.c
wpa_supplicant/ctrl_iface.c
wpa_supplicant/driver_i.h
wpa_supplicant/wpa_cli.c

index 0852e1082c1bda27b9d01a7f35ecbe95b2092c8e..fca7d4b454e1723860fcf90758c9bb4d9ead7328 100644 (file)
@@ -2392,6 +2392,11 @@ struct wpa_signal_info {
        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
  * @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);
 
+       /**
+        * 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
         * @priv: Private driver interface data
index 00879abea4f02c5056fb651f2025d316ba60f908..531699138e1dc03a796b4269199bf024b8eda503 100644 (file)
@@ -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)
 {
        struct i802_bss *bss = priv;
@@ -12710,6 +12867,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .resume = wpa_driver_nl80211_resume,
        .signal_monitor = nl80211_signal_monitor,
        .signal_poll = nl80211_signal_poll,
+       .mlo_signal_poll = nl80211_mlo_signal_poll,
        .channel_info = nl80211_channel_info,
        .set_param = nl80211_set_param,
        .get_radio_name = nl80211_get_radio_name,
index ca2971389ae3161021631f0aeeacbefa81f2454c..90eb67f2cc05878746fdaf6df0407536a77bb8fd 100644 (file)
@@ -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,
                                      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) {
                reply_len = wpas_ctrl_iface_mlo_status(wpa_s, reply,
                                                       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 {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
index 41d353f911a5bf3b809e6bd800dc34e6fb14f847..6be117c1dfe248a128d337396a67643075b5a716 100644 (file)
@@ -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,
                        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,
                                       struct wpa_channel_info *ci)
 {
index d4d09adc2e0d44423bb45d4383e617866f0bbc69..197efe0b739b9a20ab1e88e86266933e17ae54ba 100644 (file)
@@ -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[])
 {
        char cmd[256];
@@ -4046,6 +4052,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
        { "mlo_status", wpa_cli_cmd_mlo_status, NULL,
          cli_cmd_flag_none,
          "= 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 }
 };