if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD))
+ capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK))
+ capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
+ capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL))
+ capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_DFS_OFFLOAD))
+ capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+
+#ifdef CONFIG_MBO
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) &&
+ ext_feature_isset(
+ ext_features, len,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION))
+ capa->flags |= WPA_DRIVER_FLAGS_OCE_STA;
+#endif /* CONFIG_MBO */
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
+ capa->flags |= WPA_DRIVER_FLAGS_FTM_RESPONDER;
}
case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION:
drv->set_wifi_conf_vendor_cmd_avail = 1;
break;
- case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES:
- drv->he_capab_vendor_cmd_avail = 1;
- break;
case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
drv->fetch_bss_trans_status = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_ROAM:
+ drv->roam_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS:
+ drv->get_supported_akm_suites_avail = 1;
+ break;
#endif /* CONFIG_DRIVER_NL80211_QCA */
}
}
capa->max_csa_counters =
nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]);
+ if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG])
+ capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY;
+
return NL_SKIP;
}
}
-static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg)
+static unsigned int get_akm_suites_info(struct nlattr *tb)
{
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct he_capabilities *he_capab = arg;
- struct nlattr *nl_vend;
- struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX + 1];
- size_t len;
-
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
-
- if (!tb[NL80211_ATTR_VENDOR_DATA])
- return NL_SKIP;
+ int i, num;
+ unsigned int key_mgmt = 0;
+ u32 *akms;
- nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
- nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX,
- nla_data(nl_vend), nla_len(nl_vend), NULL);
+ if (!tb)
+ return 0;
- if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]) {
- u8 he_supported;
+ num = nla_len(tb) / sizeof(u32);
+ akms = nla_data(tb);
+ for (i = 0; i < num; i++) {
+ u32 a = akms[i];
- he_supported = nla_get_u8(
- tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]);
- wpa_printf(MSG_DEBUG, "nl80211: HE capabilities supported: %u",
- he_supported);
- he_capab->he_supported = he_supported;
- if (!he_supported)
- return NL_SKIP;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Supported AKM %02x-%02x-%02x:%u",
+ a >> 24, (a >> 16) & 0xff,
+ (a >> 8) & 0xff, a & 0xff);
+ switch (a) {
+ case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+ break;
+ case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_PSK:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+ break;
+ case RSN_AUTH_KEY_MGMT_OWE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
+ break;
+ case RSN_AUTH_KEY_MGMT_DPP:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_SAE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+ break;
+ }
}
- if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]) {
- len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]);
-
- if (len > sizeof(he_capab->phy_cap))
- len = sizeof(he_capab->phy_cap);
- os_memcpy(he_capab->phy_cap,
- nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]),
- len);
- }
+ return key_mgmt;
+}
- if (tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB])
- he_capab->mac_cap =
- nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]);
- if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS])
- he_capab->mcs =
- nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]);
+static int get_akm_suites_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ unsigned int *key_mgmt = arg;
- if (tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS])
- he_capab->ppet.numss_m1 =
- nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]);
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
- if (tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK])
- he_capab->ppet.ru_count =
- nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]);
+ if (tb[NL80211_ATTR_VENDOR_DATA]) {
+ struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+ struct nlattr *tb_data[NL80211_ATTR_MAX + 1];
- if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]) {
- len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]);
+ nla_parse(tb_data, NL80211_ATTR_MAX,
+ nla_data(nl_vend), nla_len(nl_vend), NULL);
- if (len > sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0))
- len = sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0);
- os_memcpy(he_capab->ppet.ppet16_ppet8_ru3_ru0,
- nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]),
- len);
+ *key_mgmt =
+ get_akm_suites_info(tb_data[NL80211_ATTR_AKM_SUITES]);
}
return NL_SKIP;
}
-static void qca_nl80211_check_he_capab(struct wpa_driver_nl80211_data *drv)
+static int qca_nl80211_get_akm_suites(struct wpa_driver_nl80211_data *drv)
{
struct nl_msg *msg;
+ unsigned int key_mgmt = 0;
int ret;
- if (!drv->he_capab_vendor_cmd_avail)
- return;
+ if (!drv->get_supported_akm_suites_avail)
+ return -1;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
- QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES)) {
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS)) {
nlmsg_free(msg);
- return;
+ return -1;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, get_akm_suites_handler, &key_mgmt);
+ if (!ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Replace capa.key_mgmt based on driver advertised capabilities: 0x%x",
+ key_mgmt);
+ drv->capa.key_mgmt = key_mgmt;
}
- ret = send_and_recv_msgs(drv, msg, qca_nl80211_he_capab_handler,
- &drv->he_capab);
- if (!ret && drv->he_capab.he_supported)
- drv->capa.flags |= WPA_DRIVER_FLAGS_HE_CAPABILITIES;
+ return ret;
}
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;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON;
os_free(info.flags);
}
WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
- WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+ WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 |
+ WPA_DRIVER_CAPA_KEY_MGMT_OWE |
+ WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+ else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ /* Override drv->capa.key_mgmt based on driver advertised capability
+ * constraints, if available. */
+ qca_nl80211_get_akm_suites(drv);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
#ifdef CONFIG_DRIVER_NL80211_QCA
- qca_nl80211_check_dfs_capa(drv);
+ if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD))
+ qca_nl80211_check_dfs_capa(drv);
qca_nl80211_get_features(drv);
- qca_nl80211_check_he_capab(drv);
/*
* To enable offchannel simultaneous support in wpa_supplicant, the
struct hostapd_hw_modes *modes;
int last_mode, last_chan_idx;
int failed;
+ u8 dfs_domain;
};
static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
u8 channel;
chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
chan->flag = 0;
+ chan->allowed_bw = ~0;
chan->dfs_cac_ms = 0;
if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
chan->chan = channel;
if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT])
chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_10;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_20;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40P;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40M;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160;
+
if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
enum nl80211_dfs_state state =
nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
chan->dfs_cac_ms = nla_get_u32(
tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
}
+
+ chan->wmm_rules_valid = 0;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_WMM]) {
+ static struct nla_policy wmm_policy[NL80211_WMMR_MAX + 1] = {
+ [NL80211_WMMR_CW_MIN] = { .type = NLA_U16 },
+ [NL80211_WMMR_CW_MAX] = { .type = NLA_U16 },
+ [NL80211_WMMR_AIFSN] = { .type = NLA_U8 },
+ [NL80211_WMMR_TXOP] = { .type = NLA_U16 },
+ };
+ struct nlattr *nl_wmm;
+ struct nlattr *tb_wmm[NL80211_WMMR_MAX + 1];
+ int rem_wmm, ac, count = 0;
+
+ nla_for_each_nested(nl_wmm, tb_freq[NL80211_FREQUENCY_ATTR_WMM],
+ rem_wmm) {
+ if (nla_parse_nested(tb_wmm, NL80211_WMMR_MAX, nl_wmm,
+ wmm_policy)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to parse WMM rules attribute");
+ return;
+ }
+ if (!tb_wmm[NL80211_WMMR_CW_MIN] ||
+ !tb_wmm[NL80211_WMMR_CW_MAX] ||
+ !tb_wmm[NL80211_WMMR_AIFSN] ||
+ !tb_wmm[NL80211_WMMR_TXOP]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Channel is missing WMM rule attribute");
+ return;
+ }
+ ac = nl_wmm->nla_type;
+ if (ac < 0 || ac >= WMM_AC_NUM) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Invalid AC value %d", ac);
+ return;
+ }
+
+ chan->wmm_rules[ac].min_cwmin =
+ nla_get_u16(tb_wmm[NL80211_WMMR_CW_MIN]);
+ chan->wmm_rules[ac].min_cwmax =
+ nla_get_u16(tb_wmm[NL80211_WMMR_CW_MAX]);
+ chan->wmm_rules[ac].min_aifs =
+ nla_get_u8(tb_wmm[NL80211_WMMR_AIFSN]);
+ chan->wmm_rules[ac].max_txop =
+ nla_get_u16(tb_wmm[NL80211_WMMR_TXOP]) / 32;
+ count++;
+ }
+
+ /* Set valid flag if all the AC rules are present */
+ if (count == WMM_AC_NUM)
+ chan->wmm_rules_valid = 1;
+ }
}
[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
[NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
+ [NL80211_FREQUENCY_ATTR_NO_10MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_20MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG },
};
int new_channels = 0;
struct hostapd_channel_data *channel;
}
+static int phy_info_iftype(struct hostapd_hw_modes *mode,
+ struct nlattr *nl_iftype)
+{
+ struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1];
+ struct he_capabilities *he_capab = &mode->he_capab;
+ struct nlattr *tb_flags[NL80211_IFTYPE_MAX + 1];
+ size_t len;
+
+ nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
+ nla_data(nl_iftype), nla_len(nl_iftype), NULL);
+
+ if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES])
+ return NL_STOP;
+
+ if (nla_parse_nested(tb_flags, NL80211_IFTYPE_MAX,
+ tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES], NULL))
+ return NL_STOP;
+
+ if (!nla_get_flag(tb_flags[NL80211_IFTYPE_AP]))
+ return NL_OK;
+
+ he_capab->he_supported = 1;
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]);
+
+ if (len > sizeof(he_capab->phy_cap))
+ len = sizeof(he_capab->phy_cap);
+ os_memcpy(he_capab->phy_cap,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]),
+ len);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]);
+
+ if (len > sizeof(he_capab->mac_cap))
+ len = sizeof(he_capab->mac_cap);
+ os_memcpy(he_capab->mac_cap,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]),
+ len);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]);
+
+ if (len > sizeof(he_capab->mcs))
+ len = sizeof(he_capab->mcs);
+ os_memcpy(he_capab->mcs,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]),
+ len);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]);
+
+ if (len > sizeof(he_capab->ppet))
+ len = sizeof(he_capab->ppet);
+ os_memcpy(&he_capab->ppet,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]),
+ len);
+ }
+
+ return NL_OK;
+}
+
+
static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
{
struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
return ret;
}
+ if (tb_band[NL80211_BAND_ATTR_IFTYPE_DATA]) {
+ struct nlattr *nl_iftype;
+ int rem_band;
+
+ nla_for_each_nested(nl_iftype,
+ tb_band[NL80211_BAND_ATTR_IFTYPE_DATA],
+ rem_band) {
+ ret = phy_info_iftype(mode, nl_iftype);
+ if (ret != NL_OK)
+ return ret;
+ }
+ }
+
return NL_OK;
}
}
+static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
+ u8 *dfs_domain)
+{
+ if (region == NL80211_DFS_FCC)
+ *dfs_domain = HOSTAPD_DFS_REGION_FCC;
+ else if (region == NL80211_DFS_ETSI)
+ *dfs_domain = HOSTAPD_DFS_REGION_ETSI;
+ else if (region == NL80211_DFS_JP)
+ *dfs_domain = HOSTAPD_DFS_REGION_JP;
+ else
+ *dfs_domain = 0;
+}
+
+
static const char * dfs_domain_name(enum nl80211_dfs_regions region)
{
switch (region) {
if (tb_msg[NL80211_ATTR_DFS_REGION]) {
enum nl80211_dfs_regions dfs_domain;
dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+ nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain);
wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
(char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
dfs_domain_name(dfs_domain));
return -ENOMEM;
nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) {
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+
return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
}
+static const char * modestr(enum hostapd_hw_mode mode)
+{
+ switch (mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ return "802.11b";
+ case HOSTAPD_MODE_IEEE80211G:
+ return "802.11g";
+ case HOSTAPD_MODE_IEEE80211A:
+ return "802.11a";
+ case HOSTAPD_MODE_IEEE80211AD:
+ return "802.11ad";
+ default:
+ return "?";
+ }
+}
+
+
+static void nl80211_dump_chan_list(struct hostapd_hw_modes *modes,
+ u16 num_modes)
+{
+ int i;
+
+ if (!modes)
+ return;
+
+ for (i = 0; i < num_modes; i++) {
+ struct hostapd_hw_modes *mode = &modes[i];
+ char str[200];
+ char *pos = str;
+ char *end = pos + sizeof(str);
+ int j, res;
+
+ for (j = 0; j < mode->num_channels; j++) {
+ struct hostapd_channel_data *chan = &mode->channels[j];
+
+ res = os_snprintf(pos, end - pos, " %d%s%s%s",
+ chan->freq,
+ (chan->flag & HOSTAPD_CHAN_DISABLED) ?
+ "[DISABLED]" : "",
+ (chan->flag & HOSTAPD_CHAN_NO_IR) ?
+ "[NO_IR]" : "",
+ (chan->flag & HOSTAPD_CHAN_RADAR) ?
+ "[RADAR]" : "");
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "nl80211: Mode IEEE %s:%s",
+ modestr(mode->mode), str);
+ }
+}
+
+
struct hostapd_hw_modes *
-nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+ u8 *dfs_domain)
{
u32 feat;
struct i802_bss *bss = priv;
.modes = NULL,
.last_mode = -1,
.failed = 0,
+ .dfs_domain = 0,
};
*num_modes = 0;
*flags = 0;
+ *dfs_domain = 0;
feat = get_nl80211_protocol_features(drv);
if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
}
if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+ struct hostapd_hw_modes *modes;
+
nl80211_set_regulatory_flags(drv, &result);
if (result.failed) {
int i;
*num_modes = 0;
return NULL;
}
- return wpa_driver_nl80211_postprocess_modes(result.modes,
- num_modes);
+
+ *dfs_domain = result.dfs_domain;
+
+ modes = wpa_driver_nl80211_postprocess_modes(result.modes,
+ num_modes);
+ nl80211_dump_chan_list(modes, *num_modes);
+ return modes;
}
return NULL;