struct nlattr *tb)
{
struct wpa_driver_capa *capa = info->capa;
+ u8 *ext_features;
+ int len;
if (tb == NULL)
return;
- if (ext_feature_isset(nla_data(tb), nla_len(tb),
- NL80211_EXT_FEATURE_VHT_IBSS))
+ ext_features = nla_data(tb);
+ len = nla_len(tb);
+
+ if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VHT_IBSS))
capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
+
+ if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM))
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM;
}
if (flags & NL80211_FEATURE_HT_IBSS)
capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
+
+ if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE)
+ capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
}
}
+static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb)
+{
+ int rem = 0, i;
+ struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr;
+
+ if (!tb || drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
+ return;
+
+ nla_for_each_nested(attr, tb, rem) {
+ unsigned int len;
+ struct drv_nl80211_ext_capa *capa;
+
+ nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr),
+ nla_len(attr), NULL);
+
+ if (!tb1[NL80211_ATTR_IFTYPE] ||
+ !tb1[NL80211_ATTR_EXT_CAPA] ||
+ !tb1[NL80211_ATTR_EXT_CAPA_MASK])
+ continue;
+
+ capa = &drv->iface_ext_capa[drv->num_iface_ext_capa];
+ capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Driver-advertised extended capabilities for interface type %s",
+ nl80211_iftype_str(capa->iftype));
+
+ len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]);
+ capa->ext_capa = os_malloc(len);
+ if (!capa->ext_capa)
+ goto err;
+
+ os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
+ len);
+ capa->ext_capa_len = len;
+ wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities",
+ capa->ext_capa, capa->ext_capa_len);
+
+ len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]);
+ capa->ext_capa_mask = os_malloc(len);
+ if (!capa->ext_capa_mask)
+ goto err;
+
+ os_memcpy(capa->ext_capa_mask,
+ nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len);
+ wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
+ capa->ext_capa_mask, capa->ext_capa_len);
+
+ drv->num_iface_ext_capa++;
+ if (drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
+ break;
+ }
+
+ return;
+
+err:
+ /* Cleanup allocated memory on error */
+ for (i = 0; i < NL80211_IFTYPE_MAX; i++) {
+ os_free(drv->iface_ext_capa[i].ext_capa);
+ drv->iface_ext_capa[i].ext_capa = NULL;
+ os_free(drv->iface_ext_capa[i].ext_capa_mask);
+ drv->iface_ext_capa[i].ext_capa_mask = NULL;
+ drv->iface_ext_capa[i].ext_capa_len = 0;
+ }
+ drv->num_iface_ext_capa = 0;
+}
+
+
static int wiphy_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
+ if (tb[NL80211_ATTR_WIPHY])
+ drv->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
if (tb[NL80211_ATTR_WIPHY_NAME])
os_strlcpy(drv->phyname,
nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
capa->max_sched_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+ if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] &&
+ tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] &&
+ tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) {
+ capa->max_sched_scan_plans =
+ nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]);
+
+ capa->max_sched_scan_plan_interval =
+ nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]);
+
+ capa->max_sched_scan_plan_iterations =
+ nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+ }
+
if (tb[NL80211_ATTR_MAX_MATCH_SETS])
capa->max_match_sets =
nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
nla_len(tb[NL80211_ATTR_EXT_CAPA]));
drv->extended_capa_len =
nla_len(tb[NL80211_ATTR_EXT_CAPA]);
+ wpa_hexdump(MSG_DEBUG,
+ "nl80211: Driver-advertised extended capabilities (default)",
+ drv->extended_capa, drv->extended_capa_len);
}
drv->extended_capa_mask =
os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
os_memcpy(drv->extended_capa_mask,
nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+ wpa_hexdump(MSG_DEBUG,
+ "nl80211: Driver-advertised extended capabilities mask (default)",
+ drv->extended_capa_mask,
+ drv->extended_capa_len);
} else {
os_free(drv->extended_capa);
drv->extended_capa = NULL;
}
}
+ wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]);
+
if (tb[NL80211_ATTR_VENDOR_DATA]) {
struct nlattr *nl;
int rem;
drv->capa.max_csa_counters = 1;
}
+ if (!drv->capa.max_sched_scan_plans) {
+ drv->capa.max_sched_scan_plans = 1;
+ drv->capa.max_sched_scan_plan_interval = UINT32_MAX;
+ drv->capa.max_sched_scan_plan_iterations = 0;
+ }
+
return 0;
}
attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
if (attr) {
- info->flags = nla_data(attr);
- info->flags_len = nla_len(attr);
+ int len = nla_len(attr);
+ info->flags = os_malloc(len);
+ if (info->flags != NULL) {
+ os_memcpy(info->flags, nla_data(attr), len);
+ info->flags_len = len;
+ }
}
attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
if (attr)
if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
&info))
drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
+ os_free(info.flags);
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
u16 *num_modes;
struct hostapd_hw_modes *modes;
int last_mode, last_chan_idx;
+ int failed;
};
static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
mode->num_channels + new_channels,
sizeof(struct hostapd_channel_data));
if (!channel)
- return NL_SKIP;
+ return NL_STOP;
mode->channels = channel;
mode->num_channels += new_channels;
mode->rates = os_calloc(mode->num_rates, sizeof(int));
if (!mode->rates)
- return NL_SKIP;
+ return NL_STOP;
idx = 0;
mode = os_realloc_array(phy_info->modes,
*phy_info->num_modes + 1,
sizeof(*mode));
- if (!mode)
- return NL_SKIP;
+ if (!mode) {
+ phy_info->failed = 1;
+ return NL_STOP;
+ }
phy_info->modes = mode;
mode = &phy_info->modes[*(phy_info->num_modes)];
phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
- if (ret != NL_OK)
- return ret;
- ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
- if (ret != NL_OK)
+ if (ret == NL_OK)
+ ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+ if (ret != NL_OK) {
+ phy_info->failed = 1;
return ret;
+ }
return NL_OK;
}
static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
- int end)
+ int end, int max_bw)
{
int c;
if (chan->freq - 70 >= start && chan->freq + 10 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_70_10;
+
+ if (max_bw >= 160) {
+ if (chan->freq - 10 >= start && chan->freq + 150 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_10_150;
+
+ if (chan->freq - 30 >= start && chan->freq + 130 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_30_130;
+
+ if (chan->freq - 50 >= start && chan->freq + 110 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_50_110;
+
+ if (chan->freq - 70 >= start && chan->freq + 90 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_70_90;
+
+ if (chan->freq - 90 >= start && chan->freq + 70 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_90_70;
+
+ if (chan->freq - 110 >= start && chan->freq + 50 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_110_50;
+
+ if (chan->freq - 130 >= start && chan->freq + 30 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_130_30;
+
+ if (chan->freq - 150 >= start && chan->freq + 10 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_150_10;
+ }
}
}
if (!results->modes[m].vht_capab)
continue;
- nl80211_set_vht_mode(&results->modes[m], start, end);
+ nl80211_set_vht_mode(&results->modes[m], start, end, max_bw);
}
}
.num_modes = num_modes,
.modes = NULL,
.last_mode = -1,
+ .failed = 0,
};
*num_modes = 0;
if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
nl80211_set_regulatory_flags(drv, &result);
+ if (result.failed) {
+ int i;
+
+ for (i = 0; result.modes && i < *num_modes; i++) {
+ os_free(result.modes[i].channels);
+ os_free(result.modes[i].rates);
+ }
+ os_free(result.modes);
+ return NULL;
+ }
return wpa_driver_nl80211_postprocess_modes(result.modes,
num_modes);
}