hostapd: Fetch multiple radios information from the driver
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>
This commit is contained in:
parent
c43e1e5a5f
commit
15bf093b5b
10 changed files with 268 additions and 0 deletions
|
@ -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, ¶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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue