]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
hostapd: Fetch multiple radios information from the driver
authorAditya Kumar Singh <quic_adisi@quicinc.com>
Wed, 24 Jul 2024 07:08:13 +0000 (12:38 +0530)
committerJouni Malinen <j@w1.fi>
Fri, 9 Aug 2024 19:20:24 +0000 (22:20 +0300)
When a driver consolidates several hardware components under a single
radio, it provides details about these components via the
NL80211_CMD_GET_WIPHY command.

Parse this information and store it. A subsequent change will use this
information for validation in certain scenarios.

Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
src/ap/ap_drv_ops.c
src/ap/ap_drv_ops.h
src/ap/hostapd.c
src/ap/hostapd.h
src/ap/hw_features.c
src/ap/hw_features.h
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211.h
src/drivers/driver_nl80211_capa.c

index c47349110e5f27613adf6ed1655a37097ac37d9a..92dbc165308a5934230a26827bad05e7cb3e99fc 100644 (file)
@@ -1250,3 +1250,14 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
        return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, &params);
 }
 #endif /* CONFIG_PASN */
+
+
+struct hostapd_multi_hw_info *
+hostapd_get_multi_hw_info(struct hostapd_data *hapd,
+                         unsigned int *num_multi_hws)
+{
+       if (!hapd->driver || !hapd->driver->get_multi_hw_info)
+               return NULL;
+
+       return hapd->driver->get_multi_hw_info(hapd->drv_priv, num_multi_hws);
+}
index d7e79c840cb82c9bf6df9fa01e8b54d0092b9c53..6b7f02a1f4a5cffb76aa7522cc8865280dd3b13d 100644 (file)
@@ -478,4 +478,8 @@ static inline int hostapd_drv_link_sta_remove(struct hostapd_data *hapd,
 
 #endif /* CONFIG_IEEE80211BE */
 
+struct hostapd_multi_hw_info *
+hostapd_get_multi_hw_info(struct hostapd_data *hapd,
+                         unsigned int *num_multi_hws);
+
 #endif /* AP_DRV_OPS */
index 57ae12d4a1e929b2b081f7fca60527acf9a56aae..bf40f389d6d5810badcec02b9f58f35c4abce41e 100644 (file)
@@ -712,6 +712,9 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
        ap_list_deinit(iface);
        sta_track_deinit(iface);
        airtime_policy_update_deinit(iface);
+       hostapd_free_multi_hw_info(iface->multi_hw_info);
+       iface->multi_hw_info = NULL;
+       iface->current_hw_info = NULL;
 }
 
 
@@ -2505,6 +2508,12 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
                           hostapd_hw_mode_txt(iface->conf->hw_mode),
                           iface->conf->channel, iface->freq);
 
+               if (hostapd_set_current_hw_info(iface, iface->freq)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Failed to set current hardware info");
+                       goto fail;
+               }
+
 #ifdef NEED_AP_MLME
                /* Handle DFS only if it is not offloaded to the driver */
                if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
index 278e9c3cad0d781bc4df9ad6c68368c585954197..898dc0d75b939424736ae254c9a3c3b192b93d91 100644 (file)
@@ -730,6 +730,10 @@ struct hostapd_iface {
        bool is_no_ir;
 
        bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */
+
+       struct hostapd_multi_hw_info *multi_hw_info;
+       unsigned int num_multi_hws;
+       struct hostapd_multi_hw_info *current_hw_info;
 };
 
 /* hostapd.c */
index c4556603d3daa366978c5d68aebcddb9a7871fbf..02d67593a370355a90900cfd6910cc8b7d09c980 100644 (file)
@@ -76,12 +76,15 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
 {
        struct hostapd_data *hapd = iface->bss[0];
        int i, j;
+       unsigned int k;
        u16 num_modes, flags;
        struct hostapd_hw_modes *modes;
        u8 dfs_domain;
        enum hostapd_hw_mode mode = HOSTAPD_MODE_IEEE80211ANY;
        bool is_6ghz = false;
        bool orig_mode_valid = false;
+       struct hostapd_multi_hw_info *multi_hw_info;
+       unsigned int num_multi_hws;
 
        if (hostapd_drv_none(hapd))
                return -1;
@@ -168,6 +171,25 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
                           __func__);
        }
 
+       multi_hw_info = hostapd_get_multi_hw_info(hapd, &num_multi_hws);
+       if (!multi_hw_info)
+               return 0;
+
+       hostapd_free_multi_hw_info(iface->multi_hw_info);
+       iface->multi_hw_info = multi_hw_info;
+       iface->num_multi_hws = num_multi_hws;
+
+       wpa_printf(MSG_DEBUG, "Multiple underlying hardwares info:");
+
+       for (k = 0; k < num_multi_hws; k++) {
+               struct hostapd_multi_hw_info *hw_info = &multi_hw_info[k];
+
+               wpa_printf(MSG_DEBUG,
+                          "  %d. hw_idx=%u, frequency range: %d-%d MHz",
+                          k + 1, hw_info->hw_idx, hw_info->start_freq,
+                          hw_info->end_freq);
+       }
+
        return 0;
 }
 
@@ -1391,3 +1413,34 @@ int hostapd_hw_skip_mode(struct hostapd_iface *iface,
        }
        return 0;
 }
+
+
+void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info)
+{
+       os_free(multi_hw_info);
+}
+
+
+int hostapd_set_current_hw_info(struct hostapd_iface *iface, int oper_freq)
+{
+       struct hostapd_multi_hw_info *hw_info;
+       unsigned int i;
+
+       if (!iface->num_multi_hws)
+               return 0;
+
+       for (i = 0; i < iface->num_multi_hws; i++) {
+               hw_info = &iface->multi_hw_info[i];
+
+               if (hw_info->start_freq <= oper_freq &&
+                   hw_info->end_freq >= oper_freq) {
+                       iface->current_hw_info = hw_info;
+                       wpa_printf(MSG_DEBUG,
+                                  "Mode: Selected underlying hardware: hw_idx=%u",
+                                  iface->current_hw_info->hw_idx);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
index c682c6d20b817c6ba68be348c1b3c46e5f9be7ce..73663d0af051aa21720324a4f7179384462c0100 100644 (file)
@@ -30,6 +30,8 @@ void hostapd_stop_setup_timers(struct hostapd_iface *iface);
 int hostapd_hw_skip_mode(struct hostapd_iface *iface,
                         struct hostapd_hw_modes *mode);
 int hostapd_determine_mode(struct hostapd_iface *iface);
+void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info);
+int hostapd_set_current_hw_info(struct hostapd_iface *iface, int oper_freq);
 #else /* NEED_AP_MLME */
 static inline void
 hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -103,6 +105,16 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface)
        return 0;
 }
 
+static inline
+void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info)
+{
+}
+
+static inline int hostapd_set_current_hw_info(struct hostapd_iface *iface,
+                                             u32 oper_freq)
+{
+       return 0;
+}
 #endif /* NEED_AP_MLME */
 
 #endif /* HW_FEATURES_H */
index c4cf46c30508178e0162163eeb723f39650195f1..e9c6a83250345e4ac01e2d8bae4004f1319ea8cf 100644 (file)
@@ -317,6 +317,27 @@ struct hostapd_hw_modes {
 };
 
 
+/**
+ * struct hostapd_multi_hw_info: Supported multiple underlying hardware info
+ */
+struct hostapd_multi_hw_info {
+       /**
+        * hw_idx - Hardware index
+        */
+       u8 hw_idx;
+
+       /**
+        * start_freq - Frequency range start in MHz
+        */
+       int start_freq;
+
+       /**
+        * end_freq - Frequency range end in MHz
+        */
+       int end_freq;
+};
+
+
 #define IEEE80211_CAP_ESS      0x0001
 #define IEEE80211_CAP_IBSS     0x0002
 #define IEEE80211_CAP_PRIVACY  0x0010
@@ -5224,6 +5245,18 @@ struct wpa_driver_ops {
                              const u8 *match, size_t match_len,
                              bool multicast);
 #endif /* CONFIG_TESTING_OPTIONS */
+
+       /**
+        * get_multi_hw_info - Get multiple underlying hardware information
+        *                     (hardware IDx and supported frequency range)
+        * @priv: Private driver interface data
+        * @num_multi_hws: Variable for returning the number of returned
+        *      hardware info data
+        * Returns: Pointer to allocated multiple hardware data on success
+        * or %NULL on failure. Caller is responsible for freeing this.
+        */
+       struct hostapd_multi_hw_info *
+       (*get_multi_hw_info)(void *priv, unsigned int *num_multi_hws);
 };
 
 /**
index b729b3e5b0054c58a7088518f4a3c9675aa2bc8b..a71fbf8b1f1564ca89c99e2b5cd61a70ef9c52fe 100644 (file)
@@ -14036,6 +14036,15 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
+static struct hostapd_multi_hw_info *
+wpa_driver_get_multi_hw_info(void *priv, unsigned int *num_multi_hws)
+{
+       struct i802_bss *bss = priv;
+
+       return nl80211_get_multi_hw_info(bss, num_multi_hws);
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -14194,4 +14203,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .register_frame = testing_nl80211_register_frame,
        .radio_disable = testing_nl80211_radio_disable,
 #endif /* CONFIG_TESTING_OPTIONS */
+       .get_multi_hw_info = wpa_driver_get_multi_hw_info,
 };
index d1e0f195ee0eea9b000a7e9a2c24bee381baa4fd..87bcc880bae6cf461e87a459d8d3eceb2b80e862 100644 (file)
@@ -411,5 +411,7 @@ int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
 int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
                                   struct wpa_driver_scan_params *params);
 int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len);
+struct hostapd_multi_hw_info *
+nl80211_get_multi_hw_info(struct i802_bss *bss, unsigned int *num_multi_hws);
 
 #endif /* DRIVER_NL80211_H */
index 26c1f41406d2ee291cc07e559251acf866ce4de9..c84a9478ce53e900851f90981f1017ebf93ec897 100644 (file)
@@ -2736,3 +2736,133 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
 
        return NULL;
 }
+
+
+static int phy_multi_hw_info_parse(struct hostapd_multi_hw_info *hw_info,
+                                  struct nlattr *radio_attr)
+{
+       struct nlattr *tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_MAX + 1];
+       int start_freq, end_freq;
+
+       switch (nla_type(radio_attr)) {
+       case NL80211_WIPHY_RADIO_ATTR_INDEX:
+               hw_info->hw_idx = nla_get_u32(radio_attr);
+               return NL_OK;
+       case NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE:
+               nla_parse_nested(tb_freq, NL80211_WIPHY_RADIO_FREQ_ATTR_MAX,
+                                radio_attr, NULL);
+
+               if (!tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_START] ||
+                   !tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_END])
+                       return NL_STOP;
+
+               start_freq = nla_get_u32(
+                       tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_START]);
+               end_freq = nla_get_u32(
+                       tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_END]);
+
+               /* Convert to MHz and store */
+               hw_info->start_freq = start_freq / 1000;
+               hw_info->end_freq = end_freq / 1000;
+               return NL_OK;
+       default:
+               return NL_OK;
+       }
+}
+
+
+struct phy_multi_hw_info_arg {
+       bool failed;
+       unsigned int *num_multi_hws;
+       struct hostapd_multi_hw_info *multi_hws;
+};
+
+
+static int phy_multi_hw_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct phy_multi_hw_info_arg *multi_hw_info = arg;
+       struct hostapd_multi_hw_info *multi_hws, hw_info;
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct nlattr *nl_hw, *radio_attr;
+       int rem_hw, rem_radio_prop, res;
+
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb_msg[NL80211_ATTR_WIPHY_RADIOS])
+               return NL_SKIP;
+
+       *multi_hw_info->num_multi_hws = 0;
+
+       nla_for_each_nested(nl_hw, tb_msg[NL80211_ATTR_WIPHY_RADIOS], rem_hw) {
+               os_memset(&hw_info, 0, sizeof(hw_info));
+
+               nla_for_each_nested(radio_attr, nl_hw, rem_radio_prop) {
+                       res = phy_multi_hw_info_parse(&hw_info, radio_attr);
+                       if (res != NL_OK)
+                               goto out;
+               }
+
+               if (hw_info.start_freq == 0 || hw_info.end_freq == 0)
+                       goto out;
+
+               multi_hws = os_realloc_array(multi_hw_info->multi_hws,
+                                            *multi_hw_info->num_multi_hws + 1,
+                                            sizeof(*multi_hws));
+               if (!multi_hws)
+                       goto out;
+
+               multi_hw_info->multi_hws = multi_hws;
+               os_memcpy(&multi_hws[*(multi_hw_info->num_multi_hws)],
+                         &hw_info, sizeof(struct hostapd_multi_hw_info));
+               *(multi_hw_info->num_multi_hws) += 1;
+       }
+
+       return NL_OK;
+out:
+       multi_hw_info->failed = true;
+       return NL_STOP;
+}
+
+
+struct hostapd_multi_hw_info *
+nl80211_get_multi_hw_info(struct i802_bss *bss, unsigned int *num_multi_hws)
+{
+       u32 feat;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int nl_flags = 0;
+       struct nl_msg *msg;
+       struct phy_multi_hw_info_arg result = {
+               .failed = false,
+               .num_multi_hws = num_multi_hws,
+               .multi_hws = NULL,
+       };
+
+       *num_multi_hws = 0;
+
+       if (!drv->has_capability || !(drv->capa.flags2 & WPA_DRIVER_FLAGS2_MLO))
+               return NULL;
+
+       feat = get_nl80211_protocol_features(drv);
+       if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+               nl_flags = NLM_F_DUMP;
+       if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) ||
+           nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+               nlmsg_free(msg);
+               return NULL;
+       }
+
+       if (send_and_recv_resp(drv, msg, phy_multi_hw_info_handler,
+                              &result) == 0) {
+               if (result.failed) {
+                       os_free(result.multi_hws);
+                       *num_multi_hws = 0;
+                       return NULL;
+               }
+
+               return result.multi_hws;
+       }
+
+       return NULL;
+}