return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, ¶ms);
}
#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);
+}
#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 */
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;
}
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)) {
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 */
{
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;
__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;
}
}
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;
+}
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,
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 */
};
+/**
+ * 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
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);
};
/**
#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",
.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,
};
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 */
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;
+}