15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * Driver interaction with Linux nl80211/cfg80211 - Capabilities 35b9c547cSRui Paulo * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> 45b9c547cSRui Paulo * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> 55b9c547cSRui Paulo * Copyright (c) 2009-2010, Atheros Communications 65b9c547cSRui Paulo * 75b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 85b9c547cSRui Paulo * See README for more details. 95b9c547cSRui Paulo */ 105b9c547cSRui Paulo 115b9c547cSRui Paulo #include "includes.h" 125b9c547cSRui Paulo #include <netlink/genl/genl.h> 135b9c547cSRui Paulo 145b9c547cSRui Paulo #include "utils/common.h" 155b9c547cSRui Paulo #include "common/ieee802_11_common.h" 1685732ac8SCy Schubert #include "common/wpa_common.h" 175b9c547cSRui Paulo #include "common/qca-vendor.h" 185b9c547cSRui Paulo #include "common/qca-vendor-attr.h" 19c1d255d3SCy Schubert #include "common/brcm_vendor.h" 205b9c547cSRui Paulo #include "driver_nl80211.h" 215b9c547cSRui Paulo 225b9c547cSRui Paulo 235b9c547cSRui Paulo static int protocol_feature_handler(struct nl_msg *msg, void *arg) 245b9c547cSRui Paulo { 255b9c547cSRui Paulo u32 *feat = arg; 265b9c547cSRui Paulo struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; 275b9c547cSRui Paulo struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 285b9c547cSRui Paulo 295b9c547cSRui Paulo nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 305b9c547cSRui Paulo genlmsg_attrlen(gnlh, 0), NULL); 315b9c547cSRui Paulo 325b9c547cSRui Paulo if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) 335b9c547cSRui Paulo *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]); 345b9c547cSRui Paulo 355b9c547cSRui Paulo return NL_SKIP; 365b9c547cSRui Paulo } 375b9c547cSRui Paulo 385b9c547cSRui Paulo 395b9c547cSRui Paulo static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv) 405b9c547cSRui Paulo { 415b9c547cSRui Paulo u32 feat = 0; 425b9c547cSRui Paulo struct nl_msg *msg; 435b9c547cSRui Paulo 445b9c547cSRui Paulo msg = nlmsg_alloc(); 455b9c547cSRui Paulo if (!msg) 465b9c547cSRui Paulo return 0; 475b9c547cSRui Paulo 485b9c547cSRui Paulo if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) { 495b9c547cSRui Paulo nlmsg_free(msg); 505b9c547cSRui Paulo return 0; 515b9c547cSRui Paulo } 525b9c547cSRui Paulo 53c1d255d3SCy Schubert if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat, 54c1d255d3SCy Schubert NULL, NULL) == 0) 555b9c547cSRui Paulo return feat; 565b9c547cSRui Paulo 575b9c547cSRui Paulo return 0; 585b9c547cSRui Paulo } 595b9c547cSRui Paulo 605b9c547cSRui Paulo 615b9c547cSRui Paulo struct wiphy_info_data { 625b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv; 635b9c547cSRui Paulo struct wpa_driver_capa *capa; 645b9c547cSRui Paulo 655b9c547cSRui Paulo unsigned int num_multichan_concurrent; 665b9c547cSRui Paulo 675b9c547cSRui Paulo unsigned int error:1; 685b9c547cSRui Paulo unsigned int device_ap_sme:1; 695b9c547cSRui Paulo unsigned int poll_command_supported:1; 705b9c547cSRui Paulo unsigned int data_tx_status:1; 715b9c547cSRui Paulo unsigned int auth_supported:1; 725b9c547cSRui Paulo unsigned int connect_supported:1; 735b9c547cSRui Paulo unsigned int p2p_go_supported:1; 745b9c547cSRui Paulo unsigned int p2p_client_supported:1; 755b9c547cSRui Paulo unsigned int p2p_go_ctwindow_supported:1; 765b9c547cSRui Paulo unsigned int p2p_concurrent:1; 775b9c547cSRui Paulo unsigned int channel_switch_supported:1; 785b9c547cSRui Paulo unsigned int set_qos_map_supported:1; 795b9c547cSRui Paulo unsigned int have_low_prio_scan:1; 805b9c547cSRui Paulo unsigned int wmm_ac_supported:1; 815b9c547cSRui Paulo unsigned int mac_addr_rand_scan_supported:1; 825b9c547cSRui Paulo unsigned int mac_addr_rand_sched_scan_supported:1; 83c1d255d3SCy Schubert unsigned int update_ft_ies_supported:1; 84c1d255d3SCy Schubert unsigned int has_key_mgmt:1; 85c1d255d3SCy Schubert unsigned int has_key_mgmt_iftype:1; 865b9c547cSRui Paulo }; 875b9c547cSRui Paulo 885b9c547cSRui Paulo 895b9c547cSRui Paulo static unsigned int probe_resp_offload_support(int supp_protocols) 905b9c547cSRui Paulo { 915b9c547cSRui Paulo unsigned int prot = 0; 925b9c547cSRui Paulo 935b9c547cSRui Paulo if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS) 945b9c547cSRui Paulo prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS; 955b9c547cSRui Paulo if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2) 965b9c547cSRui Paulo prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2; 975b9c547cSRui Paulo if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P) 985b9c547cSRui Paulo prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P; 995b9c547cSRui Paulo if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U) 1005b9c547cSRui Paulo prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING; 1015b9c547cSRui Paulo 1025b9c547cSRui Paulo return prot; 1035b9c547cSRui Paulo } 1045b9c547cSRui Paulo 1055b9c547cSRui Paulo 1065b9c547cSRui Paulo static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, 1075b9c547cSRui Paulo struct nlattr *tb) 1085b9c547cSRui Paulo { 1095b9c547cSRui Paulo struct nlattr *nl_mode; 1105b9c547cSRui Paulo int i; 1115b9c547cSRui Paulo 1125b9c547cSRui Paulo if (tb == NULL) 1135b9c547cSRui Paulo return; 1145b9c547cSRui Paulo 1155b9c547cSRui Paulo nla_for_each_nested(nl_mode, tb, i) { 1165b9c547cSRui Paulo switch (nla_type(nl_mode)) { 1175b9c547cSRui Paulo case NL80211_IFTYPE_AP: 1185b9c547cSRui Paulo info->capa->flags |= WPA_DRIVER_FLAGS_AP; 1195b9c547cSRui Paulo break; 1205b9c547cSRui Paulo case NL80211_IFTYPE_MESH_POINT: 1215b9c547cSRui Paulo info->capa->flags |= WPA_DRIVER_FLAGS_MESH; 1225b9c547cSRui Paulo break; 1235b9c547cSRui Paulo case NL80211_IFTYPE_ADHOC: 1245b9c547cSRui Paulo info->capa->flags |= WPA_DRIVER_FLAGS_IBSS; 1255b9c547cSRui Paulo break; 1265b9c547cSRui Paulo case NL80211_IFTYPE_P2P_DEVICE: 1275b9c547cSRui Paulo info->capa->flags |= 1285b9c547cSRui Paulo WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; 1295b9c547cSRui Paulo break; 1305b9c547cSRui Paulo case NL80211_IFTYPE_P2P_GO: 1315b9c547cSRui Paulo info->p2p_go_supported = 1; 1325b9c547cSRui Paulo break; 1335b9c547cSRui Paulo case NL80211_IFTYPE_P2P_CLIENT: 1345b9c547cSRui Paulo info->p2p_client_supported = 1; 1355b9c547cSRui Paulo break; 1365b9c547cSRui Paulo } 1375b9c547cSRui Paulo } 1385b9c547cSRui Paulo } 1395b9c547cSRui Paulo 1405b9c547cSRui Paulo 1415b9c547cSRui Paulo static int wiphy_info_iface_comb_process(struct wiphy_info_data *info, 1425b9c547cSRui Paulo struct nlattr *nl_combi) 1435b9c547cSRui Paulo { 1445b9c547cSRui Paulo struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; 1455b9c547cSRui Paulo struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; 1465b9c547cSRui Paulo struct nlattr *nl_limit, *nl_mode; 1475b9c547cSRui Paulo int err, rem_limit, rem_mode; 1485b9c547cSRui Paulo int combination_has_p2p = 0, combination_has_mgd = 0; 1495b9c547cSRui Paulo static struct nla_policy 1505b9c547cSRui Paulo iface_combination_policy[NUM_NL80211_IFACE_COMB] = { 1515b9c547cSRui Paulo [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, 1525b9c547cSRui Paulo [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, 1535b9c547cSRui Paulo [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, 1545b9c547cSRui Paulo [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, 1555b9c547cSRui Paulo [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 }, 1565b9c547cSRui Paulo }, 1575b9c547cSRui Paulo iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { 1585b9c547cSRui Paulo [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, 1595b9c547cSRui Paulo [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, 1605b9c547cSRui Paulo }; 1615b9c547cSRui Paulo 1625b9c547cSRui Paulo err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, 1635b9c547cSRui Paulo nl_combi, iface_combination_policy); 1645b9c547cSRui Paulo if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || 1655b9c547cSRui Paulo !tb_comb[NL80211_IFACE_COMB_MAXNUM] || 1665b9c547cSRui Paulo !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) 1675b9c547cSRui Paulo return 0; /* broken combination */ 1685b9c547cSRui Paulo 1695b9c547cSRui Paulo if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) 1705b9c547cSRui Paulo info->capa->flags |= WPA_DRIVER_FLAGS_RADAR; 1715b9c547cSRui Paulo 1725b9c547cSRui Paulo nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], 1735b9c547cSRui Paulo rem_limit) { 1745b9c547cSRui Paulo err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT, 1755b9c547cSRui Paulo nl_limit, iface_limit_policy); 1765b9c547cSRui Paulo if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) 1775b9c547cSRui Paulo return 0; /* broken combination */ 1785b9c547cSRui Paulo 1795b9c547cSRui Paulo nla_for_each_nested(nl_mode, 1805b9c547cSRui Paulo tb_limit[NL80211_IFACE_LIMIT_TYPES], 1815b9c547cSRui Paulo rem_mode) { 1825b9c547cSRui Paulo int ift = nla_type(nl_mode); 1835b9c547cSRui Paulo if (ift == NL80211_IFTYPE_P2P_GO || 1845b9c547cSRui Paulo ift == NL80211_IFTYPE_P2P_CLIENT) 1855b9c547cSRui Paulo combination_has_p2p = 1; 1865b9c547cSRui Paulo if (ift == NL80211_IFTYPE_STATION) 1875b9c547cSRui Paulo combination_has_mgd = 1; 1885b9c547cSRui Paulo } 1895b9c547cSRui Paulo if (combination_has_p2p && combination_has_mgd) 1905b9c547cSRui Paulo break; 1915b9c547cSRui Paulo } 1925b9c547cSRui Paulo 1935b9c547cSRui Paulo if (combination_has_p2p && combination_has_mgd) { 1945b9c547cSRui Paulo unsigned int num_channels = 1955b9c547cSRui Paulo nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]); 1965b9c547cSRui Paulo 1975b9c547cSRui Paulo info->p2p_concurrent = 1; 1985b9c547cSRui Paulo if (info->num_multichan_concurrent < num_channels) 1995b9c547cSRui Paulo info->num_multichan_concurrent = num_channels; 2005b9c547cSRui Paulo } 2015b9c547cSRui Paulo 2025b9c547cSRui Paulo return 0; 2035b9c547cSRui Paulo } 2045b9c547cSRui Paulo 2055b9c547cSRui Paulo 2065b9c547cSRui Paulo static void wiphy_info_iface_comb(struct wiphy_info_data *info, 2075b9c547cSRui Paulo struct nlattr *tb) 2085b9c547cSRui Paulo { 2095b9c547cSRui Paulo struct nlattr *nl_combi; 2105b9c547cSRui Paulo int rem_combi; 2115b9c547cSRui Paulo 2125b9c547cSRui Paulo if (tb == NULL) 2135b9c547cSRui Paulo return; 2145b9c547cSRui Paulo 2155b9c547cSRui Paulo nla_for_each_nested(nl_combi, tb, rem_combi) { 2165b9c547cSRui Paulo if (wiphy_info_iface_comb_process(info, nl_combi) > 0) 2175b9c547cSRui Paulo break; 2185b9c547cSRui Paulo } 2195b9c547cSRui Paulo } 2205b9c547cSRui Paulo 2215b9c547cSRui Paulo 2225b9c547cSRui Paulo static void wiphy_info_supp_cmds(struct wiphy_info_data *info, 2235b9c547cSRui Paulo struct nlattr *tb) 2245b9c547cSRui Paulo { 2255b9c547cSRui Paulo struct nlattr *nl_cmd; 2265b9c547cSRui Paulo int i; 2275b9c547cSRui Paulo 2285b9c547cSRui Paulo if (tb == NULL) 2295b9c547cSRui Paulo return; 2305b9c547cSRui Paulo 2315b9c547cSRui Paulo nla_for_each_nested(nl_cmd, tb, i) { 2325b9c547cSRui Paulo switch (nla_get_u32(nl_cmd)) { 2335b9c547cSRui Paulo case NL80211_CMD_AUTHENTICATE: 2345b9c547cSRui Paulo info->auth_supported = 1; 2355b9c547cSRui Paulo break; 2365b9c547cSRui Paulo case NL80211_CMD_CONNECT: 2375b9c547cSRui Paulo info->connect_supported = 1; 2385b9c547cSRui Paulo break; 2395b9c547cSRui Paulo case NL80211_CMD_START_SCHED_SCAN: 2405b9c547cSRui Paulo info->capa->sched_scan_supported = 1; 2415b9c547cSRui Paulo break; 2425b9c547cSRui Paulo case NL80211_CMD_PROBE_CLIENT: 2435b9c547cSRui Paulo info->poll_command_supported = 1; 2445b9c547cSRui Paulo break; 2455b9c547cSRui Paulo case NL80211_CMD_CHANNEL_SWITCH: 2465b9c547cSRui Paulo info->channel_switch_supported = 1; 2475b9c547cSRui Paulo break; 2485b9c547cSRui Paulo case NL80211_CMD_SET_QOS_MAP: 2495b9c547cSRui Paulo info->set_qos_map_supported = 1; 2505b9c547cSRui Paulo break; 251c1d255d3SCy Schubert case NL80211_CMD_UPDATE_FT_IES: 252c1d255d3SCy Schubert info->update_ft_ies_supported = 1; 253c1d255d3SCy Schubert break; 2545b9c547cSRui Paulo } 2555b9c547cSRui Paulo } 2565b9c547cSRui Paulo } 2575b9c547cSRui Paulo 2585b9c547cSRui Paulo 259c1d255d3SCy Schubert static unsigned int get_akm_suites_info(struct nlattr *tb) 260c1d255d3SCy Schubert { 261c1d255d3SCy Schubert int i, num; 262c1d255d3SCy Schubert unsigned int key_mgmt = 0; 263c1d255d3SCy Schubert u32 *akms; 264c1d255d3SCy Schubert 265c1d255d3SCy Schubert if (!tb) 266c1d255d3SCy Schubert return 0; 267c1d255d3SCy Schubert 268c1d255d3SCy Schubert num = nla_len(tb) / sizeof(u32); 269c1d255d3SCy Schubert akms = nla_data(tb); 270c1d255d3SCy Schubert for (i = 0; i < num; i++) { 271c1d255d3SCy Schubert switch (akms[i]) { 272c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X: 273c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | 274c1d255d3SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_WPA2; 275c1d255d3SCy Schubert break; 276c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X: 277c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | 278c1d255d3SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; 279c1d255d3SCy Schubert break; 280c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_FT_802_1X: 281c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT; 282c1d255d3SCy Schubert break; 283c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_FT_PSK: 284c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; 285c1d255d3SCy Schubert break; 286c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_802_1X_SHA256: 287c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256; 288c1d255d3SCy Schubert break; 289c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_PSK_SHA256: 290c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256; 291c1d255d3SCy Schubert break; 292c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE: 293c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE; 294c1d255d3SCy Schubert break; 295c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_FT_SAE: 296c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE; 297c1d255d3SCy Schubert break; 298c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384: 299c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384; 300c1d255d3SCy Schubert break; 301c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_CCKM: 302c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_CCKM; 303c1d255d3SCy Schubert break; 304c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_OSEN: 305c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OSEN; 306c1d255d3SCy Schubert break; 307c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B: 308c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B; 309c1d255d3SCy Schubert break; 310c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192: 311c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192; 312c1d255d3SCy Schubert break; 313c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_OWE: 314c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE; 315c1d255d3SCy Schubert break; 316c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_DPP: 317c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP; 318c1d255d3SCy Schubert break; 319c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_FILS_SHA256: 320c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256; 321c1d255d3SCy Schubert break; 322c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_FILS_SHA384: 323c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384; 324c1d255d3SCy Schubert break; 325c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256: 326c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256; 327c1d255d3SCy Schubert break; 328c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384: 329c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384; 330c1d255d3SCy Schubert break; 331c1d255d3SCy Schubert case RSN_AUTH_KEY_MGMT_SAE: 332c1d255d3SCy Schubert key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE; 333c1d255d3SCy Schubert break; 334c1d255d3SCy Schubert } 335c1d255d3SCy Schubert } 336c1d255d3SCy Schubert 337c1d255d3SCy Schubert return key_mgmt; 338c1d255d3SCy Schubert } 339c1d255d3SCy Schubert 340c1d255d3SCy Schubert 341c1d255d3SCy Schubert static void get_iface_akm_suites_info(struct wiphy_info_data *info, 342c1d255d3SCy Schubert struct nlattr *nl_akms) 343c1d255d3SCy Schubert { 344c1d255d3SCy Schubert struct nlattr *tb[NL80211_IFTYPE_AKM_ATTR_MAX + 1]; 345c1d255d3SCy Schubert struct nlattr *nl_iftype; 346c1d255d3SCy Schubert unsigned int key_mgmt; 347c1d255d3SCy Schubert int i; 348c1d255d3SCy Schubert 349c1d255d3SCy Schubert if (!nl_akms) 350c1d255d3SCy Schubert return; 351c1d255d3SCy Schubert 352c1d255d3SCy Schubert nla_parse(tb, NL80211_IFTYPE_AKM_ATTR_MAX, 353c1d255d3SCy Schubert nla_data(nl_akms), nla_len(nl_akms), NULL); 354c1d255d3SCy Schubert 355c1d255d3SCy Schubert if (!tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES] || 356c1d255d3SCy Schubert !tb[NL80211_IFTYPE_AKM_ATTR_SUITES]) 357c1d255d3SCy Schubert return; 358c1d255d3SCy Schubert 359c1d255d3SCy Schubert info->has_key_mgmt_iftype = 1; 360c1d255d3SCy Schubert key_mgmt = get_akm_suites_info(tb[NL80211_IFTYPE_AKM_ATTR_SUITES]); 361c1d255d3SCy Schubert 362c1d255d3SCy Schubert nla_for_each_nested(nl_iftype, tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES], i) { 363c1d255d3SCy Schubert switch (nla_type(nl_iftype)) { 364c1d255d3SCy Schubert case NL80211_IFTYPE_ADHOC: 365c1d255d3SCy Schubert info->drv->capa.key_mgmt_iftype[WPA_IF_IBSS] = key_mgmt; 366c1d255d3SCy Schubert break; 367c1d255d3SCy Schubert case NL80211_IFTYPE_STATION: 368c1d255d3SCy Schubert info->drv->capa.key_mgmt_iftype[WPA_IF_STATION] = 369c1d255d3SCy Schubert key_mgmt; 370c1d255d3SCy Schubert break; 371c1d255d3SCy Schubert case NL80211_IFTYPE_AP: 372c1d255d3SCy Schubert info->drv->capa.key_mgmt_iftype[WPA_IF_AP_BSS] = 373c1d255d3SCy Schubert key_mgmt; 374c1d255d3SCy Schubert break; 375c1d255d3SCy Schubert case NL80211_IFTYPE_AP_VLAN: 376c1d255d3SCy Schubert info->drv->capa.key_mgmt_iftype[WPA_IF_AP_VLAN] = 377c1d255d3SCy Schubert key_mgmt; 378c1d255d3SCy Schubert break; 379c1d255d3SCy Schubert case NL80211_IFTYPE_MESH_POINT: 380c1d255d3SCy Schubert info->drv->capa.key_mgmt_iftype[WPA_IF_MESH] = key_mgmt; 381c1d255d3SCy Schubert break; 382c1d255d3SCy Schubert case NL80211_IFTYPE_P2P_CLIENT: 383c1d255d3SCy Schubert info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_CLIENT] = 384c1d255d3SCy Schubert key_mgmt; 385c1d255d3SCy Schubert break; 386c1d255d3SCy Schubert case NL80211_IFTYPE_P2P_GO: 387c1d255d3SCy Schubert info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_GO] = 388c1d255d3SCy Schubert key_mgmt; 389c1d255d3SCy Schubert break; 390c1d255d3SCy Schubert case NL80211_IFTYPE_P2P_DEVICE: 391c1d255d3SCy Schubert info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_DEVICE] = 392c1d255d3SCy Schubert key_mgmt; 393c1d255d3SCy Schubert break; 394c1d255d3SCy Schubert case NL80211_IFTYPE_NAN: 395c1d255d3SCy Schubert info->drv->capa.key_mgmt_iftype[WPA_IF_NAN] = key_mgmt; 396c1d255d3SCy Schubert break; 397c1d255d3SCy Schubert } 398c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: %s supported key_mgmt 0x%x", 399c1d255d3SCy Schubert nl80211_iftype_str(nla_type(nl_iftype)), 400c1d255d3SCy Schubert key_mgmt); 401c1d255d3SCy Schubert } 402c1d255d3SCy Schubert } 403c1d255d3SCy Schubert 404c1d255d3SCy Schubert 405c1d255d3SCy Schubert static void wiphy_info_iftype_akm_suites(struct wiphy_info_data *info, 406c1d255d3SCy Schubert struct nlattr *tb) 407c1d255d3SCy Schubert { 408c1d255d3SCy Schubert struct nlattr *nl_if; 409c1d255d3SCy Schubert int rem_if; 410c1d255d3SCy Schubert 411c1d255d3SCy Schubert if (!tb) 412c1d255d3SCy Schubert return; 413c1d255d3SCy Schubert 414c1d255d3SCy Schubert nla_for_each_nested(nl_if, tb, rem_if) 415c1d255d3SCy Schubert get_iface_akm_suites_info(info, nl_if); 416c1d255d3SCy Schubert } 417c1d255d3SCy Schubert 418c1d255d3SCy Schubert 419c1d255d3SCy Schubert static void wiphy_info_akm_suites(struct wiphy_info_data *info, 420c1d255d3SCy Schubert struct nlattr *tb) 421c1d255d3SCy Schubert { 422c1d255d3SCy Schubert if (!tb) 423c1d255d3SCy Schubert return; 424c1d255d3SCy Schubert 425c1d255d3SCy Schubert info->has_key_mgmt = 1; 426c1d255d3SCy Schubert info->capa->key_mgmt = get_akm_suites_info(tb); 427c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: wiphy supported key_mgmt 0x%x", 428c1d255d3SCy Schubert info->capa->key_mgmt); 429c1d255d3SCy Schubert } 430c1d255d3SCy Schubert 431c1d255d3SCy Schubert 4325b9c547cSRui Paulo static void wiphy_info_cipher_suites(struct wiphy_info_data *info, 4335b9c547cSRui Paulo struct nlattr *tb) 4345b9c547cSRui Paulo { 4355b9c547cSRui Paulo int i, num; 4365b9c547cSRui Paulo u32 *ciphers; 4375b9c547cSRui Paulo 4385b9c547cSRui Paulo if (tb == NULL) 4395b9c547cSRui Paulo return; 4405b9c547cSRui Paulo 4415b9c547cSRui Paulo num = nla_len(tb) / sizeof(u32); 4425b9c547cSRui Paulo ciphers = nla_data(tb); 4435b9c547cSRui Paulo for (i = 0; i < num; i++) { 4445b9c547cSRui Paulo u32 c = ciphers[i]; 4455b9c547cSRui Paulo 4465b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d", 4475b9c547cSRui Paulo c >> 24, (c >> 16) & 0xff, 4485b9c547cSRui Paulo (c >> 8) & 0xff, c & 0xff); 4495b9c547cSRui Paulo switch (c) { 45085732ac8SCy Schubert case RSN_CIPHER_SUITE_CCMP_256: 4515b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256; 4525b9c547cSRui Paulo break; 45385732ac8SCy Schubert case RSN_CIPHER_SUITE_GCMP_256: 4545b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256; 4555b9c547cSRui Paulo break; 45685732ac8SCy Schubert case RSN_CIPHER_SUITE_CCMP: 4575b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP; 4585b9c547cSRui Paulo break; 45985732ac8SCy Schubert case RSN_CIPHER_SUITE_GCMP: 4605b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP; 4615b9c547cSRui Paulo break; 46285732ac8SCy Schubert case RSN_CIPHER_SUITE_TKIP: 4635b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP; 4645b9c547cSRui Paulo break; 46585732ac8SCy Schubert case RSN_CIPHER_SUITE_WEP104: 4665b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104; 4675b9c547cSRui Paulo break; 46885732ac8SCy Schubert case RSN_CIPHER_SUITE_WEP40: 4695b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40; 4705b9c547cSRui Paulo break; 47185732ac8SCy Schubert case RSN_CIPHER_SUITE_AES_128_CMAC: 4725b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP; 4735b9c547cSRui Paulo break; 47485732ac8SCy Schubert case RSN_CIPHER_SUITE_BIP_GMAC_128: 4755b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128; 4765b9c547cSRui Paulo break; 47785732ac8SCy Schubert case RSN_CIPHER_SUITE_BIP_GMAC_256: 4785b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256; 4795b9c547cSRui Paulo break; 48085732ac8SCy Schubert case RSN_CIPHER_SUITE_BIP_CMAC_256: 4815b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256; 4825b9c547cSRui Paulo break; 48385732ac8SCy Schubert case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED: 4845b9c547cSRui Paulo info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED; 4855b9c547cSRui Paulo break; 4865b9c547cSRui Paulo } 4875b9c547cSRui Paulo } 4885b9c547cSRui Paulo } 4895b9c547cSRui Paulo 4905b9c547cSRui Paulo 4915b9c547cSRui Paulo static void wiphy_info_max_roc(struct wpa_driver_capa *capa, 4925b9c547cSRui Paulo struct nlattr *tb) 4935b9c547cSRui Paulo { 4945b9c547cSRui Paulo if (tb) 4955b9c547cSRui Paulo capa->max_remain_on_chan = nla_get_u32(tb); 4965b9c547cSRui Paulo } 4975b9c547cSRui Paulo 4985b9c547cSRui Paulo 4995b9c547cSRui Paulo static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, 5005b9c547cSRui Paulo struct nlattr *ext_setup) 5015b9c547cSRui Paulo { 5025b9c547cSRui Paulo if (tdls == NULL) 5035b9c547cSRui Paulo return; 5045b9c547cSRui Paulo 5055b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); 5065b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; 5075b9c547cSRui Paulo 5085b9c547cSRui Paulo if (ext_setup) { 5095b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); 5105b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; 5115b9c547cSRui Paulo } 5125b9c547cSRui Paulo } 5135b9c547cSRui Paulo 5145b9c547cSRui Paulo 515325151a3SRui Paulo static int ext_feature_isset(const u8 *ext_features, int ext_features_len, 516325151a3SRui Paulo enum nl80211_ext_feature_index ftidx) 517325151a3SRui Paulo { 518325151a3SRui Paulo u8 ft_byte; 519325151a3SRui Paulo 520325151a3SRui Paulo if ((int) ftidx / 8 >= ext_features_len) 521325151a3SRui Paulo return 0; 522325151a3SRui Paulo 523325151a3SRui Paulo ft_byte = ext_features[ftidx / 8]; 524325151a3SRui Paulo return (ft_byte & BIT(ftidx % 8)) != 0; 525325151a3SRui Paulo } 526325151a3SRui Paulo 527325151a3SRui Paulo 528325151a3SRui Paulo static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, 529325151a3SRui Paulo struct nlattr *tb) 530325151a3SRui Paulo { 531325151a3SRui Paulo struct wpa_driver_capa *capa = info->capa; 532780fb4a2SCy Schubert u8 *ext_features; 533780fb4a2SCy Schubert int len; 534325151a3SRui Paulo 535325151a3SRui Paulo if (tb == NULL) 536325151a3SRui Paulo return; 537325151a3SRui Paulo 538780fb4a2SCy Schubert ext_features = nla_data(tb); 539780fb4a2SCy Schubert len = nla_len(tb); 540780fb4a2SCy Schubert 541780fb4a2SCy Schubert if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VHT_IBSS)) 542325151a3SRui Paulo capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS; 543780fb4a2SCy Schubert 544780fb4a2SCy Schubert if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM)) 545780fb4a2SCy Schubert capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM; 54685732ac8SCy Schubert 54785732ac8SCy Schubert if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_FILS_STA)) 54885732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_SUPPORT_FILS; 54985732ac8SCy Schubert 55085732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 55185732ac8SCy Schubert NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) 55285732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY; 55385732ac8SCy Schubert 55485732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 55585732ac8SCy Schubert NL80211_EXT_FEATURE_BEACON_RATE_HT)) 55685732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_HT; 55785732ac8SCy Schubert 55885732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 55985732ac8SCy Schubert NL80211_EXT_FEATURE_BEACON_RATE_VHT)) 56085732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_VHT; 56185732ac8SCy Schubert 56285732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 563c1d255d3SCy Schubert NL80211_EXT_FEATURE_BEACON_RATE_HE)) 564c1d255d3SCy Schubert capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_RATE_HE; 565c1d255d3SCy Schubert 566c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 56785732ac8SCy Schubert NL80211_EXT_FEATURE_SET_SCAN_DWELL)) 56885732ac8SCy Schubert capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL; 56985732ac8SCy Schubert 57085732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 57185732ac8SCy Schubert NL80211_EXT_FEATURE_SCAN_START_TIME) && 57285732ac8SCy Schubert ext_feature_isset(ext_features, len, 57385732ac8SCy Schubert NL80211_EXT_FEATURE_BSS_PARENT_TSF) && 57485732ac8SCy Schubert ext_feature_isset(ext_features, len, 57585732ac8SCy Schubert NL80211_EXT_FEATURE_SET_SCAN_DWELL)) 57685732ac8SCy Schubert capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT; 57785732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 57885732ac8SCy Schubert NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA)) 57985732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA; 58085732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 58185732ac8SCy Schubert NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED)) 58285732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED; 58385732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 58485732ac8SCy Schubert NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI)) 58585732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI; 58685732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 58785732ac8SCy Schubert NL80211_EXT_FEATURE_FILS_SK_OFFLOAD)) 58885732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD; 58985732ac8SCy Schubert 59085732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 5914bc52338SCy Schubert NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK)) 5924bc52338SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK; 5934bc52338SCy Schubert if (ext_feature_isset(ext_features, len, 59485732ac8SCy Schubert NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) 5954bc52338SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X; 59685732ac8SCy Schubert 59785732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 59885732ac8SCy Schubert NL80211_EXT_FEATURE_MFP_OPTIONAL)) 59985732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL; 60085732ac8SCy Schubert 60185732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 60285732ac8SCy Schubert NL80211_EXT_FEATURE_DFS_OFFLOAD)) 60385732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD; 60485732ac8SCy Schubert 60585732ac8SCy Schubert #ifdef CONFIG_MBO 60685732ac8SCy Schubert if (ext_feature_isset(ext_features, len, 60785732ac8SCy Schubert NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) && 60885732ac8SCy Schubert ext_feature_isset(ext_features, len, 60985732ac8SCy Schubert NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) && 61085732ac8SCy Schubert ext_feature_isset(ext_features, len, 61185732ac8SCy Schubert NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) && 61285732ac8SCy Schubert ext_feature_isset( 61385732ac8SCy Schubert ext_features, len, 61485732ac8SCy Schubert NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION)) 61585732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_OCE_STA; 61685732ac8SCy Schubert #endif /* CONFIG_MBO */ 6174bc52338SCy Schubert 6184bc52338SCy Schubert if (ext_feature_isset(ext_features, len, 6194bc52338SCy Schubert NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER)) 6204bc52338SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_FTM_RESPONDER; 621c1d255d3SCy Schubert 622c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 623c1d255d3SCy Schubert NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211)) 624c1d255d3SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_CONTROL_PORT; 625c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 626c1d255d3SCy Schubert NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH)) 627c1d255d3SCy Schubert capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_RX; 628c1d255d3SCy Schubert if (ext_feature_isset( 629c1d255d3SCy Schubert ext_features, len, 630c1d255d3SCy Schubert NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS)) 631c1d255d3SCy Schubert capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS; 632c1d255d3SCy Schubert 633c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 634c1d255d3SCy Schubert NL80211_EXT_FEATURE_VLAN_OFFLOAD)) 635c1d255d3SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_VLAN_OFFLOAD; 636c1d255d3SCy Schubert 637c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 638c1d255d3SCy Schubert NL80211_EXT_FEATURE_CAN_REPLACE_PTK0)) 639c1d255d3SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS; 640c1d255d3SCy Schubert 641c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 642c1d255d3SCy Schubert NL80211_EXT_FEATURE_BEACON_PROTECTION)) 643c1d255d3SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_BEACON_PROTECTION; 644c1d255d3SCy Schubert 645c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 646c1d255d3SCy Schubert NL80211_EXT_FEATURE_EXT_KEY_ID)) 647c1d255d3SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_EXTENDED_KEY_ID; 648c1d255d3SCy Schubert 649c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 650c1d255d3SCy Schubert NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS)) 651c1d255d3SCy Schubert info->drv->multicast_registrations = 1; 652c1d255d3SCy Schubert 653c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 654c1d255d3SCy Schubert NL80211_EXT_FEATURE_FILS_DISCOVERY)) 655c1d255d3SCy Schubert info->drv->fils_discovery = 1; 656c1d255d3SCy Schubert 657c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 658c1d255d3SCy Schubert NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP)) 659c1d255d3SCy Schubert info->drv->unsol_bcast_probe_resp = 1; 660c1d255d3SCy Schubert 661c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 662c1d255d3SCy Schubert NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT)) 663c1d255d3SCy Schubert capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT; 664c1d255d3SCy Schubert 665c1d255d3SCy Schubert if (ext_feature_isset(ext_features, len, 666c1d255d3SCy Schubert NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION)) 667c1d255d3SCy Schubert capa->flags2 |= WPA_DRIVER_FLAGS2_OCV; 668325151a3SRui Paulo } 669325151a3SRui Paulo 670325151a3SRui Paulo 6715b9c547cSRui Paulo static void wiphy_info_feature_flags(struct wiphy_info_data *info, 6725b9c547cSRui Paulo struct nlattr *tb) 6735b9c547cSRui Paulo { 6745b9c547cSRui Paulo u32 flags; 6755b9c547cSRui Paulo struct wpa_driver_capa *capa = info->capa; 6765b9c547cSRui Paulo 6775b9c547cSRui Paulo if (tb == NULL) 6785b9c547cSRui Paulo return; 6795b9c547cSRui Paulo 6805b9c547cSRui Paulo flags = nla_get_u32(tb); 6815b9c547cSRui Paulo 6825b9c547cSRui Paulo if (flags & NL80211_FEATURE_SK_TX_STATUS) 6835b9c547cSRui Paulo info->data_tx_status = 1; 6845b9c547cSRui Paulo 6855b9c547cSRui Paulo if (flags & NL80211_FEATURE_INACTIVITY_TIMER) 6865b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER; 6875b9c547cSRui Paulo 6885b9c547cSRui Paulo if (flags & NL80211_FEATURE_SAE) 6895b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_SAE; 6905b9c547cSRui Paulo 6915b9c547cSRui Paulo if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) 6925b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; 6935b9c547cSRui Paulo 6945b9c547cSRui Paulo if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE) 6955b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX; 6965b9c547cSRui Paulo 6975b9c547cSRui Paulo if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) { 6985b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch"); 6995b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH; 7005b9c547cSRui Paulo } 7015b9c547cSRui Paulo 7025b9c547cSRui Paulo if (flags & NL80211_FEATURE_P2P_GO_CTWIN) 7035b9c547cSRui Paulo info->p2p_go_ctwindow_supported = 1; 7045b9c547cSRui Paulo 7055b9c547cSRui Paulo if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN) 7065b9c547cSRui Paulo info->have_low_prio_scan = 1; 7075b9c547cSRui Paulo 7085b9c547cSRui Paulo if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR) 7095b9c547cSRui Paulo info->mac_addr_rand_scan_supported = 1; 7105b9c547cSRui Paulo 7115b9c547cSRui Paulo if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR) 7125b9c547cSRui Paulo info->mac_addr_rand_sched_scan_supported = 1; 7135b9c547cSRui Paulo 7145b9c547cSRui Paulo if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) 7155b9c547cSRui Paulo info->wmm_ac_supported = 1; 7165b9c547cSRui Paulo 7175b9c547cSRui Paulo if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) 7185b9c547cSRui Paulo capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES; 7195b9c547cSRui Paulo 7205b9c547cSRui Paulo if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES) 7215b9c547cSRui Paulo capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES; 7225b9c547cSRui Paulo 7235b9c547cSRui Paulo if (flags & NL80211_FEATURE_QUIET) 7245b9c547cSRui Paulo capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET; 7255b9c547cSRui Paulo 7265b9c547cSRui Paulo if (flags & NL80211_FEATURE_TX_POWER_INSERTION) 7275b9c547cSRui Paulo capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION; 7285b9c547cSRui Paulo 7295b9c547cSRui Paulo if (flags & NL80211_FEATURE_HT_IBSS) 7305b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS; 731780fb4a2SCy Schubert 732780fb4a2SCy Schubert if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE) 733780fb4a2SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE; 7345b9c547cSRui Paulo } 7355b9c547cSRui Paulo 7365b9c547cSRui Paulo 7375b9c547cSRui Paulo static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa, 7385b9c547cSRui Paulo struct nlattr *tb) 7395b9c547cSRui Paulo { 7405b9c547cSRui Paulo u32 protocols; 7415b9c547cSRui Paulo 7425b9c547cSRui Paulo if (tb == NULL) 7435b9c547cSRui Paulo return; 7445b9c547cSRui Paulo 7455b9c547cSRui Paulo protocols = nla_get_u32(tb); 7465b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP " 7475b9c547cSRui Paulo "mode"); 7485b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD; 7495b9c547cSRui Paulo capa->probe_resp_offloads = probe_resp_offload_support(protocols); 7505b9c547cSRui Paulo } 7515b9c547cSRui Paulo 7525b9c547cSRui Paulo 7535b9c547cSRui Paulo static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa, 7545b9c547cSRui Paulo struct nlattr *tb) 7555b9c547cSRui Paulo { 7565b9c547cSRui Paulo struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1]; 7575b9c547cSRui Paulo 7585b9c547cSRui Paulo if (tb == NULL) 7595b9c547cSRui Paulo return; 7605b9c547cSRui Paulo 7615b9c547cSRui Paulo if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG, 7625b9c547cSRui Paulo tb, NULL)) 7635b9c547cSRui Paulo return; 7645b9c547cSRui Paulo 7655b9c547cSRui Paulo if (triggers[NL80211_WOWLAN_TRIG_ANY]) 7665b9c547cSRui Paulo capa->wowlan_triggers.any = 1; 7675b9c547cSRui Paulo if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT]) 7685b9c547cSRui Paulo capa->wowlan_triggers.disconnect = 1; 7695b9c547cSRui Paulo if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT]) 7705b9c547cSRui Paulo capa->wowlan_triggers.magic_pkt = 1; 7715b9c547cSRui Paulo if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) 7725b9c547cSRui Paulo capa->wowlan_triggers.gtk_rekey_failure = 1; 7735b9c547cSRui Paulo if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) 7745b9c547cSRui Paulo capa->wowlan_triggers.eap_identity_req = 1; 7755b9c547cSRui Paulo if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) 7765b9c547cSRui Paulo capa->wowlan_triggers.four_way_handshake = 1; 7775b9c547cSRui Paulo if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) 7785b9c547cSRui Paulo capa->wowlan_triggers.rfkill_release = 1; 7795b9c547cSRui Paulo } 7805b9c547cSRui Paulo 7815b9c547cSRui Paulo 782780fb4a2SCy Schubert static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv, 783780fb4a2SCy Schubert struct nlattr *tb) 784780fb4a2SCy Schubert { 785780fb4a2SCy Schubert int rem = 0, i; 786780fb4a2SCy Schubert struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr; 787780fb4a2SCy Schubert 788780fb4a2SCy Schubert if (!tb || drv->num_iface_ext_capa == NL80211_IFTYPE_MAX) 789780fb4a2SCy Schubert return; 790780fb4a2SCy Schubert 791780fb4a2SCy Schubert nla_for_each_nested(attr, tb, rem) { 792780fb4a2SCy Schubert unsigned int len; 793780fb4a2SCy Schubert struct drv_nl80211_ext_capa *capa; 794780fb4a2SCy Schubert 795780fb4a2SCy Schubert nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr), 796780fb4a2SCy Schubert nla_len(attr), NULL); 797780fb4a2SCy Schubert 798780fb4a2SCy Schubert if (!tb1[NL80211_ATTR_IFTYPE] || 799780fb4a2SCy Schubert !tb1[NL80211_ATTR_EXT_CAPA] || 800780fb4a2SCy Schubert !tb1[NL80211_ATTR_EXT_CAPA_MASK]) 801780fb4a2SCy Schubert continue; 802780fb4a2SCy Schubert 803780fb4a2SCy Schubert capa = &drv->iface_ext_capa[drv->num_iface_ext_capa]; 804780fb4a2SCy Schubert capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]); 805780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 806780fb4a2SCy Schubert "nl80211: Driver-advertised extended capabilities for interface type %s", 807780fb4a2SCy Schubert nl80211_iftype_str(capa->iftype)); 808780fb4a2SCy Schubert 809780fb4a2SCy Schubert len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]); 81085732ac8SCy Schubert capa->ext_capa = os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA]), 81185732ac8SCy Schubert len); 812780fb4a2SCy Schubert if (!capa->ext_capa) 813780fb4a2SCy Schubert goto err; 814780fb4a2SCy Schubert 815780fb4a2SCy Schubert capa->ext_capa_len = len; 816780fb4a2SCy Schubert wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities", 817780fb4a2SCy Schubert capa->ext_capa, capa->ext_capa_len); 818780fb4a2SCy Schubert 819780fb4a2SCy Schubert len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]); 82085732ac8SCy Schubert capa->ext_capa_mask = 82185732ac8SCy Schubert os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), 82285732ac8SCy Schubert len); 823780fb4a2SCy Schubert if (!capa->ext_capa_mask) 824780fb4a2SCy Schubert goto err; 825780fb4a2SCy Schubert 826780fb4a2SCy Schubert wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask", 827780fb4a2SCy Schubert capa->ext_capa_mask, capa->ext_capa_len); 828780fb4a2SCy Schubert 829780fb4a2SCy Schubert drv->num_iface_ext_capa++; 830780fb4a2SCy Schubert if (drv->num_iface_ext_capa == NL80211_IFTYPE_MAX) 831780fb4a2SCy Schubert break; 832780fb4a2SCy Schubert } 833780fb4a2SCy Schubert 834780fb4a2SCy Schubert return; 835780fb4a2SCy Schubert 836780fb4a2SCy Schubert err: 837780fb4a2SCy Schubert /* Cleanup allocated memory on error */ 838780fb4a2SCy Schubert for (i = 0; i < NL80211_IFTYPE_MAX; i++) { 839780fb4a2SCy Schubert os_free(drv->iface_ext_capa[i].ext_capa); 840780fb4a2SCy Schubert drv->iface_ext_capa[i].ext_capa = NULL; 841780fb4a2SCy Schubert os_free(drv->iface_ext_capa[i].ext_capa_mask); 842780fb4a2SCy Schubert drv->iface_ext_capa[i].ext_capa_mask = NULL; 843780fb4a2SCy Schubert drv->iface_ext_capa[i].ext_capa_len = 0; 844780fb4a2SCy Schubert } 845780fb4a2SCy Schubert drv->num_iface_ext_capa = 0; 846780fb4a2SCy Schubert } 847780fb4a2SCy Schubert 848780fb4a2SCy Schubert 8495b9c547cSRui Paulo static int wiphy_info_handler(struct nl_msg *msg, void *arg) 8505b9c547cSRui Paulo { 8515b9c547cSRui Paulo struct nlattr *tb[NL80211_ATTR_MAX + 1]; 8525b9c547cSRui Paulo struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 8535b9c547cSRui Paulo struct wiphy_info_data *info = arg; 8545b9c547cSRui Paulo struct wpa_driver_capa *capa = info->capa; 8555b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv = info->drv; 8565b9c547cSRui Paulo 8575b9c547cSRui Paulo nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 8585b9c547cSRui Paulo genlmsg_attrlen(gnlh, 0), NULL); 8595b9c547cSRui Paulo 860780fb4a2SCy Schubert if (tb[NL80211_ATTR_WIPHY]) 861780fb4a2SCy Schubert drv->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); 862780fb4a2SCy Schubert 8635b9c547cSRui Paulo if (tb[NL80211_ATTR_WIPHY_NAME]) 8645b9c547cSRui Paulo os_strlcpy(drv->phyname, 8655b9c547cSRui Paulo nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), 8665b9c547cSRui Paulo sizeof(drv->phyname)); 8675b9c547cSRui Paulo if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) 8685b9c547cSRui Paulo capa->max_scan_ssids = 8695b9c547cSRui Paulo nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); 8705b9c547cSRui Paulo 8715b9c547cSRui Paulo if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) 8725b9c547cSRui Paulo capa->max_sched_scan_ssids = 8735b9c547cSRui Paulo nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); 8745b9c547cSRui Paulo 875780fb4a2SCy Schubert if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] && 876780fb4a2SCy Schubert tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] && 877780fb4a2SCy Schubert tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) { 878780fb4a2SCy Schubert capa->max_sched_scan_plans = 879780fb4a2SCy Schubert nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]); 880780fb4a2SCy Schubert 881780fb4a2SCy Schubert capa->max_sched_scan_plan_interval = 882780fb4a2SCy Schubert nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]); 883780fb4a2SCy Schubert 884780fb4a2SCy Schubert capa->max_sched_scan_plan_iterations = 885780fb4a2SCy Schubert nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]); 886780fb4a2SCy Schubert } 887780fb4a2SCy Schubert 8885b9c547cSRui Paulo if (tb[NL80211_ATTR_MAX_MATCH_SETS]) 8895b9c547cSRui Paulo capa->max_match_sets = 8905b9c547cSRui Paulo nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); 8915b9c547cSRui Paulo 8925b9c547cSRui Paulo if (tb[NL80211_ATTR_MAC_ACL_MAX]) 8935b9c547cSRui Paulo capa->max_acl_mac_addrs = 894c1d255d3SCy Schubert nla_get_u32(tb[NL80211_ATTR_MAC_ACL_MAX]); 8955b9c547cSRui Paulo 8965b9c547cSRui Paulo wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]); 8975b9c547cSRui Paulo wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]); 8985b9c547cSRui Paulo wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]); 8995b9c547cSRui Paulo wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]); 900c1d255d3SCy Schubert wiphy_info_akm_suites(info, tb[NL80211_ATTR_AKM_SUITES]); 901c1d255d3SCy Schubert wiphy_info_iftype_akm_suites(info, tb[NL80211_ATTR_IFTYPE_AKM_SUITES]); 9025b9c547cSRui Paulo 9035b9c547cSRui Paulo if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { 9045b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " 9055b9c547cSRui Paulo "off-channel TX"); 9065b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; 9075b9c547cSRui Paulo } 9085b9c547cSRui Paulo 9095b9c547cSRui Paulo if (tb[NL80211_ATTR_ROAM_SUPPORT]) { 9105b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); 9115b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; 9125b9c547cSRui Paulo } 9135b9c547cSRui Paulo 9145b9c547cSRui Paulo wiphy_info_max_roc(capa, 9155b9c547cSRui Paulo tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); 9165b9c547cSRui Paulo 9175b9c547cSRui Paulo if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD]) 9185b9c547cSRui Paulo capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD; 9195b9c547cSRui Paulo 9205b9c547cSRui Paulo wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT], 9215b9c547cSRui Paulo tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]); 9225b9c547cSRui Paulo 9235b9c547cSRui Paulo if (tb[NL80211_ATTR_DEVICE_AP_SME]) 9245b9c547cSRui Paulo info->device_ap_sme = 1; 9255b9c547cSRui Paulo 9265b9c547cSRui Paulo wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); 927325151a3SRui Paulo wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]); 9285b9c547cSRui Paulo wiphy_info_probe_resp_offload(capa, 9295b9c547cSRui Paulo tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); 9305b9c547cSRui Paulo 9315b9c547cSRui Paulo if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] && 9325b9c547cSRui Paulo drv->extended_capa == NULL) { 9335b9c547cSRui Paulo drv->extended_capa = 9345b9c547cSRui Paulo os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); 9355b9c547cSRui Paulo if (drv->extended_capa) { 9365b9c547cSRui Paulo os_memcpy(drv->extended_capa, 9375b9c547cSRui Paulo nla_data(tb[NL80211_ATTR_EXT_CAPA]), 9385b9c547cSRui Paulo nla_len(tb[NL80211_ATTR_EXT_CAPA])); 9395b9c547cSRui Paulo drv->extended_capa_len = 9405b9c547cSRui Paulo nla_len(tb[NL80211_ATTR_EXT_CAPA]); 941780fb4a2SCy Schubert wpa_hexdump(MSG_DEBUG, 942780fb4a2SCy Schubert "nl80211: Driver-advertised extended capabilities (default)", 943780fb4a2SCy Schubert drv->extended_capa, drv->extended_capa_len); 9445b9c547cSRui Paulo } 9455b9c547cSRui Paulo drv->extended_capa_mask = 9465b9c547cSRui Paulo os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); 9475b9c547cSRui Paulo if (drv->extended_capa_mask) { 9485b9c547cSRui Paulo os_memcpy(drv->extended_capa_mask, 9495b9c547cSRui Paulo nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]), 9505b9c547cSRui Paulo nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); 951780fb4a2SCy Schubert wpa_hexdump(MSG_DEBUG, 952780fb4a2SCy Schubert "nl80211: Driver-advertised extended capabilities mask (default)", 953780fb4a2SCy Schubert drv->extended_capa_mask, 954780fb4a2SCy Schubert drv->extended_capa_len); 9555b9c547cSRui Paulo } else { 9565b9c547cSRui Paulo os_free(drv->extended_capa); 9575b9c547cSRui Paulo drv->extended_capa = NULL; 9585b9c547cSRui Paulo drv->extended_capa_len = 0; 9595b9c547cSRui Paulo } 9605b9c547cSRui Paulo } 9615b9c547cSRui Paulo 962780fb4a2SCy Schubert wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]); 963780fb4a2SCy Schubert 9645b9c547cSRui Paulo if (tb[NL80211_ATTR_VENDOR_DATA]) { 9655b9c547cSRui Paulo struct nlattr *nl; 9665b9c547cSRui Paulo int rem; 9675b9c547cSRui Paulo 9685b9c547cSRui Paulo nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) { 9695b9c547cSRui Paulo struct nl80211_vendor_cmd_info *vinfo; 9705b9c547cSRui Paulo if (nla_len(nl) != sizeof(*vinfo)) { 9715b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); 9725b9c547cSRui Paulo continue; 9735b9c547cSRui Paulo } 9745b9c547cSRui Paulo vinfo = nla_data(nl); 975325151a3SRui Paulo if (vinfo->vendor_id == OUI_QCA) { 9765b9c547cSRui Paulo switch (vinfo->subcmd) { 9775b9c547cSRui Paulo case QCA_NL80211_VENDOR_SUBCMD_TEST: 9785b9c547cSRui Paulo drv->vendor_cmd_test_avail = 1; 9795b9c547cSRui Paulo break; 980780fb4a2SCy Schubert #ifdef CONFIG_DRIVER_NL80211_QCA 9815b9c547cSRui Paulo case QCA_NL80211_VENDOR_SUBCMD_ROAMING: 9825b9c547cSRui Paulo drv->roaming_vendor_cmd_avail = 1; 9835b9c547cSRui Paulo break; 9845b9c547cSRui Paulo case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: 9855b9c547cSRui Paulo drv->dfs_vendor_cmd_avail = 1; 9865b9c547cSRui Paulo break; 9875b9c547cSRui Paulo case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: 9885b9c547cSRui Paulo drv->get_features_vendor_cmd_avail = 1; 9895b9c547cSRui Paulo break; 990325151a3SRui Paulo case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST: 991325151a3SRui Paulo drv->get_pref_freq_list = 1; 9925b9c547cSRui Paulo break; 993325151a3SRui Paulo case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL: 994325151a3SRui Paulo drv->set_prob_oper_freq = 1; 995325151a3SRui Paulo break; 996325151a3SRui Paulo case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: 997325151a3SRui Paulo drv->capa.flags |= 998325151a3SRui Paulo WPA_DRIVER_FLAGS_ACS_OFFLOAD; 999c1d255d3SCy Schubert drv->qca_do_acs = 1; 1000325151a3SRui Paulo break; 1001325151a3SRui Paulo case QCA_NL80211_VENDOR_SUBCMD_SETBAND: 1002325151a3SRui Paulo drv->setband_vendor_cmd_avail = 1; 1003325151a3SRui Paulo break; 1004780fb4a2SCy Schubert case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN: 1005780fb4a2SCy Schubert drv->scan_vendor_cmd_avail = 1; 1006780fb4a2SCy Schubert break; 1007780fb4a2SCy Schubert case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION: 1008780fb4a2SCy Schubert drv->set_wifi_conf_vendor_cmd_avail = 1; 1009780fb4a2SCy Schubert break; 101085732ac8SCy Schubert case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: 101185732ac8SCy Schubert drv->fetch_bss_trans_status = 1; 101285732ac8SCy Schubert break; 101385732ac8SCy Schubert case QCA_NL80211_VENDOR_SUBCMD_ROAM: 101485732ac8SCy Schubert drv->roam_vendor_cmd_avail = 1; 101585732ac8SCy Schubert break; 1016c1d255d3SCy Schubert case QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE: 1017c1d255d3SCy Schubert drv->add_sta_node_vendor_cmd_avail = 1; 1018c1d255d3SCy Schubert break; 1019c1d255d3SCy Schubert case QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO: 1020c1d255d3SCy Schubert drv->get_sta_info_vendor_cmd_avail = 1; 10214bc52338SCy Schubert break; 1022780fb4a2SCy Schubert #endif /* CONFIG_DRIVER_NL80211_QCA */ 1023325151a3SRui Paulo } 1024c1d255d3SCy Schubert #ifdef CONFIG_DRIVER_NL80211_BRCM 1025c1d255d3SCy Schubert } else if (vinfo->vendor_id == OUI_BRCM) { 1026c1d255d3SCy Schubert switch (vinfo->subcmd) { 1027c1d255d3SCy Schubert case BRCM_VENDOR_SCMD_ACS: 1028c1d255d3SCy Schubert drv->capa.flags |= 1029c1d255d3SCy Schubert WPA_DRIVER_FLAGS_ACS_OFFLOAD; 1030c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 1031c1d255d3SCy Schubert "Enabled BRCM ACS"); 1032c1d255d3SCy Schubert drv->brcm_do_acs = 1; 1033c1d255d3SCy Schubert break; 1034c1d255d3SCy Schubert } 1035c1d255d3SCy Schubert #endif /* CONFIG_DRIVER_NL80211_BRCM */ 10365b9c547cSRui Paulo } 10375b9c547cSRui Paulo 10385b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", 10395b9c547cSRui Paulo vinfo->vendor_id, vinfo->subcmd); 10405b9c547cSRui Paulo } 10415b9c547cSRui Paulo } 10425b9c547cSRui Paulo 10435b9c547cSRui Paulo if (tb[NL80211_ATTR_VENDOR_EVENTS]) { 10445b9c547cSRui Paulo struct nlattr *nl; 10455b9c547cSRui Paulo int rem; 10465b9c547cSRui Paulo 10475b9c547cSRui Paulo nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) { 10485b9c547cSRui Paulo struct nl80211_vendor_cmd_info *vinfo; 10495b9c547cSRui Paulo if (nla_len(nl) != sizeof(*vinfo)) { 10505b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); 10515b9c547cSRui Paulo continue; 10525b9c547cSRui Paulo } 10535b9c547cSRui Paulo vinfo = nla_data(nl); 10545b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u", 10555b9c547cSRui Paulo vinfo->vendor_id, vinfo->subcmd); 10565b9c547cSRui Paulo } 10575b9c547cSRui Paulo } 10585b9c547cSRui Paulo 10595b9c547cSRui Paulo wiphy_info_wowlan_triggers(capa, 10605b9c547cSRui Paulo tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]); 10615b9c547cSRui Paulo 10625b9c547cSRui Paulo if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA]) 10635b9c547cSRui Paulo capa->max_stations = 10645b9c547cSRui Paulo nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]); 10655b9c547cSRui Paulo 1066780fb4a2SCy Schubert if (tb[NL80211_ATTR_MAX_CSA_COUNTERS]) 1067780fb4a2SCy Schubert capa->max_csa_counters = 1068780fb4a2SCy Schubert nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]); 1069780fb4a2SCy Schubert 107085732ac8SCy Schubert if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG]) 107185732ac8SCy Schubert capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY; 107285732ac8SCy Schubert 10735b9c547cSRui Paulo return NL_SKIP; 10745b9c547cSRui Paulo } 10755b9c547cSRui Paulo 10765b9c547cSRui Paulo 10775b9c547cSRui Paulo static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, 10785b9c547cSRui Paulo struct wiphy_info_data *info) 10795b9c547cSRui Paulo { 10805b9c547cSRui Paulo u32 feat; 10815b9c547cSRui Paulo struct nl_msg *msg; 10825b9c547cSRui Paulo int flags = 0; 10835b9c547cSRui Paulo 10845b9c547cSRui Paulo os_memset(info, 0, sizeof(*info)); 10855b9c547cSRui Paulo info->capa = &drv->capa; 10865b9c547cSRui Paulo info->drv = drv; 10875b9c547cSRui Paulo 10885b9c547cSRui Paulo feat = get_nl80211_protocol_features(drv); 10895b9c547cSRui Paulo if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) 10905b9c547cSRui Paulo flags = NLM_F_DUMP; 10915b9c547cSRui Paulo msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY); 10925b9c547cSRui Paulo if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { 10935b9c547cSRui Paulo nlmsg_free(msg); 10945b9c547cSRui Paulo return -1; 10955b9c547cSRui Paulo } 10965b9c547cSRui Paulo 1097c1d255d3SCy Schubert if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info, NULL, NULL)) 10985b9c547cSRui Paulo return -1; 10995b9c547cSRui Paulo 11005b9c547cSRui Paulo if (info->auth_supported) 11015b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_SME; 11025b9c547cSRui Paulo else if (!info->connect_supported) { 11035b9c547cSRui Paulo wpa_printf(MSG_INFO, "nl80211: Driver does not support " 11045b9c547cSRui Paulo "authentication/association or connect commands"); 11055b9c547cSRui Paulo info->error = 1; 11065b9c547cSRui Paulo } 11075b9c547cSRui Paulo 11085b9c547cSRui Paulo if (info->p2p_go_supported && info->p2p_client_supported) 11095b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; 11105b9c547cSRui Paulo if (info->p2p_concurrent) { 11115b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " 11125b9c547cSRui Paulo "interface (driver advertised support)"); 11135b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; 11145b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; 11155b9c547cSRui Paulo } 11165b9c547cSRui Paulo if (info->num_multichan_concurrent > 1) { 11175b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " 11185b9c547cSRui Paulo "concurrent (driver advertised support)"); 11195b9c547cSRui Paulo drv->capa.num_multichan_concurrent = 11205b9c547cSRui Paulo info->num_multichan_concurrent; 11215b9c547cSRui Paulo } 11225b9c547cSRui Paulo if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) 11235b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support"); 11245b9c547cSRui Paulo 11255b9c547cSRui Paulo /* default to 5000 since early versions of mac80211 don't set it */ 11265b9c547cSRui Paulo if (!drv->capa.max_remain_on_chan) 11275b9c547cSRui Paulo drv->capa.max_remain_on_chan = 5000; 11285b9c547cSRui Paulo 11295b9c547cSRui Paulo drv->capa.wmm_ac_supported = info->wmm_ac_supported; 11305b9c547cSRui Paulo 11315b9c547cSRui Paulo drv->capa.mac_addr_rand_sched_scan_supported = 11325b9c547cSRui Paulo info->mac_addr_rand_sched_scan_supported; 11335b9c547cSRui Paulo drv->capa.mac_addr_rand_scan_supported = 11345b9c547cSRui Paulo info->mac_addr_rand_scan_supported; 11355b9c547cSRui Paulo 1136780fb4a2SCy Schubert if (info->channel_switch_supported) { 1137780fb4a2SCy Schubert drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; 1138780fb4a2SCy Schubert if (!drv->capa.max_csa_counters) 1139780fb4a2SCy Schubert drv->capa.max_csa_counters = 1; 1140780fb4a2SCy Schubert } 1141780fb4a2SCy Schubert 1142780fb4a2SCy Schubert if (!drv->capa.max_sched_scan_plans) { 1143780fb4a2SCy Schubert drv->capa.max_sched_scan_plans = 1; 1144780fb4a2SCy Schubert drv->capa.max_sched_scan_plan_interval = UINT32_MAX; 1145780fb4a2SCy Schubert drv->capa.max_sched_scan_plan_iterations = 0; 1146780fb4a2SCy Schubert } 1147780fb4a2SCy Schubert 1148c1d255d3SCy Schubert if (info->update_ft_ies_supported) 1149c1d255d3SCy Schubert drv->capa.flags |= WPA_DRIVER_FLAGS_UPDATE_FT_IES; 1150c1d255d3SCy Schubert 11515b9c547cSRui Paulo return 0; 11525b9c547cSRui Paulo } 11535b9c547cSRui Paulo 11545b9c547cSRui Paulo 1155780fb4a2SCy Schubert #ifdef CONFIG_DRIVER_NL80211_QCA 1156780fb4a2SCy Schubert 11575b9c547cSRui Paulo static int dfs_info_handler(struct nl_msg *msg, void *arg) 11585b9c547cSRui Paulo { 11595b9c547cSRui Paulo struct nlattr *tb[NL80211_ATTR_MAX + 1]; 11605b9c547cSRui Paulo struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 11615b9c547cSRui Paulo int *dfs_capability_ptr = arg; 11625b9c547cSRui Paulo 11635b9c547cSRui Paulo nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 11645b9c547cSRui Paulo genlmsg_attrlen(gnlh, 0), NULL); 11655b9c547cSRui Paulo 11665b9c547cSRui Paulo if (tb[NL80211_ATTR_VENDOR_DATA]) { 11675b9c547cSRui Paulo struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; 11685b9c547cSRui Paulo struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; 11695b9c547cSRui Paulo 11705b9c547cSRui Paulo nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, 11715b9c547cSRui Paulo nla_data(nl_vend), nla_len(nl_vend), NULL); 11725b9c547cSRui Paulo 11735b9c547cSRui Paulo if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) { 11745b9c547cSRui Paulo u32 val; 11755b9c547cSRui Paulo val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]); 11765b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u", 11775b9c547cSRui Paulo val); 11785b9c547cSRui Paulo *dfs_capability_ptr = val; 11795b9c547cSRui Paulo } 11805b9c547cSRui Paulo } 11815b9c547cSRui Paulo 11825b9c547cSRui Paulo return NL_SKIP; 11835b9c547cSRui Paulo } 11845b9c547cSRui Paulo 11855b9c547cSRui Paulo 11865b9c547cSRui Paulo static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) 11875b9c547cSRui Paulo { 11885b9c547cSRui Paulo struct nl_msg *msg; 11895b9c547cSRui Paulo int dfs_capability = 0; 11905b9c547cSRui Paulo int ret; 11915b9c547cSRui Paulo 11925b9c547cSRui Paulo if (!drv->dfs_vendor_cmd_avail) 11935b9c547cSRui Paulo return; 11945b9c547cSRui Paulo 11955b9c547cSRui Paulo if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || 11965b9c547cSRui Paulo nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || 11975b9c547cSRui Paulo nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, 11985b9c547cSRui Paulo QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) { 11995b9c547cSRui Paulo nlmsg_free(msg); 12005b9c547cSRui Paulo return; 12015b9c547cSRui Paulo } 12025b9c547cSRui Paulo 1203c1d255d3SCy Schubert ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability, 1204c1d255d3SCy Schubert NULL, NULL); 12055b9c547cSRui Paulo if (!ret && dfs_capability) 12065b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD; 12075b9c547cSRui Paulo } 12085b9c547cSRui Paulo 12095b9c547cSRui Paulo 12105b9c547cSRui Paulo struct features_info { 12115b9c547cSRui Paulo u8 *flags; 12125b9c547cSRui Paulo size_t flags_len; 1213325151a3SRui Paulo struct wpa_driver_capa *capa; 12145b9c547cSRui Paulo }; 12155b9c547cSRui Paulo 12165b9c547cSRui Paulo 12175b9c547cSRui Paulo static int features_info_handler(struct nl_msg *msg, void *arg) 12185b9c547cSRui Paulo { 12195b9c547cSRui Paulo struct nlattr *tb[NL80211_ATTR_MAX + 1]; 12205b9c547cSRui Paulo struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 12215b9c547cSRui Paulo struct features_info *info = arg; 12225b9c547cSRui Paulo struct nlattr *nl_vend, *attr; 12235b9c547cSRui Paulo 12245b9c547cSRui Paulo nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 12255b9c547cSRui Paulo genlmsg_attrlen(gnlh, 0), NULL); 12265b9c547cSRui Paulo 12275b9c547cSRui Paulo nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; 12285b9c547cSRui Paulo if (nl_vend) { 12295b9c547cSRui Paulo struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; 12305b9c547cSRui Paulo 12315b9c547cSRui Paulo nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, 12325b9c547cSRui Paulo nla_data(nl_vend), nla_len(nl_vend), NULL); 12335b9c547cSRui Paulo 12345b9c547cSRui Paulo attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS]; 12355b9c547cSRui Paulo if (attr) { 1236780fb4a2SCy Schubert int len = nla_len(attr); 1237780fb4a2SCy Schubert info->flags = os_malloc(len); 1238780fb4a2SCy Schubert if (info->flags != NULL) { 1239780fb4a2SCy Schubert os_memcpy(info->flags, nla_data(attr), len); 1240780fb4a2SCy Schubert info->flags_len = len; 1241780fb4a2SCy Schubert } 12425b9c547cSRui Paulo } 1243325151a3SRui Paulo attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA]; 1244325151a3SRui Paulo if (attr) 1245325151a3SRui Paulo info->capa->conc_capab = nla_get_u32(attr); 1246325151a3SRui Paulo 1247325151a3SRui Paulo attr = tb_vendor[ 1248325151a3SRui Paulo QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND]; 1249325151a3SRui Paulo if (attr) 1250325151a3SRui Paulo info->capa->max_conc_chan_2_4 = nla_get_u32(attr); 1251325151a3SRui Paulo 1252325151a3SRui Paulo attr = tb_vendor[ 1253325151a3SRui Paulo QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND]; 1254325151a3SRui Paulo if (attr) 1255325151a3SRui Paulo info->capa->max_conc_chan_5_0 = nla_get_u32(attr); 12565b9c547cSRui Paulo } 12575b9c547cSRui Paulo 12585b9c547cSRui Paulo return NL_SKIP; 12595b9c547cSRui Paulo } 12605b9c547cSRui Paulo 12615b9c547cSRui Paulo 12625b9c547cSRui Paulo static int check_feature(enum qca_wlan_vendor_features feature, 12635b9c547cSRui Paulo struct features_info *info) 12645b9c547cSRui Paulo { 12655b9c547cSRui Paulo size_t idx = feature / 8; 12665b9c547cSRui Paulo 12675b9c547cSRui Paulo return (idx < info->flags_len) && 12685b9c547cSRui Paulo (info->flags[idx] & BIT(feature % 8)); 12695b9c547cSRui Paulo } 12705b9c547cSRui Paulo 12715b9c547cSRui Paulo 12725b9c547cSRui Paulo static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) 12735b9c547cSRui Paulo { 12745b9c547cSRui Paulo struct nl_msg *msg; 12755b9c547cSRui Paulo struct features_info info; 12765b9c547cSRui Paulo int ret; 12775b9c547cSRui Paulo 12785b9c547cSRui Paulo if (!drv->get_features_vendor_cmd_avail) 12795b9c547cSRui Paulo return; 12805b9c547cSRui Paulo 12815b9c547cSRui Paulo if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || 12825b9c547cSRui Paulo nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || 12835b9c547cSRui Paulo nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, 12845b9c547cSRui Paulo QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) { 12855b9c547cSRui Paulo nlmsg_free(msg); 12865b9c547cSRui Paulo return; 12875b9c547cSRui Paulo } 12885b9c547cSRui Paulo 12895b9c547cSRui Paulo os_memset(&info, 0, sizeof(info)); 1290325151a3SRui Paulo info.capa = &drv->capa; 1291c1d255d3SCy Schubert ret = send_and_recv_msgs(drv, msg, features_info_handler, &info, 1292c1d255d3SCy Schubert NULL, NULL); 12935b9c547cSRui Paulo if (ret || !info.flags) 12945b9c547cSRui Paulo return; 12955b9c547cSRui Paulo 12965b9c547cSRui Paulo if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info)) 12975b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD; 1298325151a3SRui Paulo 1299325151a3SRui Paulo if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info)) 1300325151a3SRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY; 1301780fb4a2SCy Schubert 1302780fb4a2SCy Schubert if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS, 1303780fb4a2SCy Schubert &info)) 1304780fb4a2SCy Schubert drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; 1305780fb4a2SCy Schubert if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info)) 1306780fb4a2SCy Schubert drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD; 130785732ac8SCy Schubert if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info)) 130885732ac8SCy Schubert drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA; 130985732ac8SCy Schubert if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info)) 131085732ac8SCy Schubert drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP; 131185732ac8SCy Schubert if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info)) 131285732ac8SCy Schubert drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON; 1313780fb4a2SCy Schubert os_free(info.flags); 13145b9c547cSRui Paulo } 13155b9c547cSRui Paulo 1316780fb4a2SCy Schubert #endif /* CONFIG_DRIVER_NL80211_QCA */ 1317780fb4a2SCy Schubert 13185b9c547cSRui Paulo 13195b9c547cSRui Paulo int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) 13205b9c547cSRui Paulo { 13215b9c547cSRui Paulo struct wiphy_info_data info; 1322c1d255d3SCy Schubert int i; 1323c1d255d3SCy Schubert 13245b9c547cSRui Paulo if (wpa_driver_nl80211_get_info(drv, &info)) 13255b9c547cSRui Paulo return -1; 13265b9c547cSRui Paulo 13275b9c547cSRui Paulo if (info.error) 13285b9c547cSRui Paulo return -1; 13295b9c547cSRui Paulo 13305b9c547cSRui Paulo drv->has_capability = 1; 1331c1d255d3SCy Schubert drv->has_driver_key_mgmt = info.has_key_mgmt | info.has_key_mgmt_iftype; 1332c1d255d3SCy Schubert 1333c1d255d3SCy Schubert /* Fallback to hardcoded defaults if the driver does nott advertize any 1334c1d255d3SCy Schubert * AKM capabilities. */ 1335c1d255d3SCy Schubert if (!drv->has_driver_key_mgmt) { 13365b9c547cSRui Paulo drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | 13375b9c547cSRui Paulo WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | 13385b9c547cSRui Paulo WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | 13395b9c547cSRui Paulo WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | 13405b9c547cSRui Paulo WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B | 134185732ac8SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_OWE | 134285732ac8SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_DPP; 134385732ac8SCy Schubert 1344c1d255d3SCy Schubert if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 | 1345c1d255d3SCy Schubert WPA_DRIVER_CAPA_ENC_GCMP_256)) 1346c1d255d3SCy Schubert drv->capa.key_mgmt |= 1347c1d255d3SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192; 1348c1d255d3SCy Schubert 134985732ac8SCy Schubert if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) 1350c1d255d3SCy Schubert drv->capa.key_mgmt |= 1351c1d255d3SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | 135285732ac8SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 | 135385732ac8SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 | 13544bc52338SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 | 13554bc52338SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_SAE; 135685732ac8SCy Schubert else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) 1357c1d255d3SCy Schubert drv->capa.key_mgmt |= 1358c1d255d3SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | 135985732ac8SCy Schubert WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384; 1360c1d255d3SCy Schubert } 136185732ac8SCy Schubert 1362c1d255d3SCy Schubert if (!info.has_key_mgmt_iftype) { 1363c1d255d3SCy Schubert /* If the driver does not advertize per interface AKM 1364c1d255d3SCy Schubert * capabilities, consider all interfaces to support default AKMs 1365c1d255d3SCy Schubert * in key_mgmt. */ 1366c1d255d3SCy Schubert for (i = 0; i < WPA_IF_MAX; i++) 1367c1d255d3SCy Schubert drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt; 1368c1d255d3SCy Schubert } else if (info.has_key_mgmt_iftype && !info.has_key_mgmt) { 1369c1d255d3SCy Schubert /* If the driver advertizes only per interface supported AKMs 1370c1d255d3SCy Schubert * but does not advertize per wiphy AKM capabilities, consider 1371c1d255d3SCy Schubert * the default key_mgmt as a mask of per interface supported 1372c1d255d3SCy Schubert * AKMs. */ 1373c1d255d3SCy Schubert drv->capa.key_mgmt = 0; 1374c1d255d3SCy Schubert for (i = 0; i < WPA_IF_MAX; i++) 1375c1d255d3SCy Schubert drv->capa.key_mgmt |= drv->capa.key_mgmt_iftype[i]; 1376c1d255d3SCy Schubert } else if (info.has_key_mgmt_iftype && info.has_key_mgmt) { 1377c1d255d3SCy Schubert /* If the driver advertizes AKM capabilities both per wiphy and 1378c1d255d3SCy Schubert * per interface, consider the interfaces for which per 1379c1d255d3SCy Schubert * interface AKM capabilities were not received to support the 1380c1d255d3SCy Schubert * default key_mgmt capabilities. 1381c1d255d3SCy Schubert */ 1382c1d255d3SCy Schubert for (i = 0; i < WPA_IF_MAX; i++) 1383c1d255d3SCy Schubert if (!drv->capa.key_mgmt_iftype[i]) 1384c1d255d3SCy Schubert drv->capa.key_mgmt_iftype[i] = 1385c1d255d3SCy Schubert drv->capa.key_mgmt; 1386c1d255d3SCy Schubert } 13874bc52338SCy Schubert 13885b9c547cSRui Paulo drv->capa.auth = WPA_DRIVER_AUTH_OPEN | 13895b9c547cSRui Paulo WPA_DRIVER_AUTH_SHARED | 13905b9c547cSRui Paulo WPA_DRIVER_AUTH_LEAP; 13915b9c547cSRui Paulo 1392*4b72b91aSCy Schubert drv->capa.flags |= WPA_DRIVER_FLAGS_VALID_ERROR_CODES; 13935b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; 13945b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; 13955b9c547cSRui Paulo 13965b9c547cSRui Paulo /* 13975b9c547cSRui Paulo * As all cfg80211 drivers must support cases where the AP interface is 13985b9c547cSRui Paulo * removed without the knowledge of wpa_supplicant/hostapd, e.g., in 13995b9c547cSRui Paulo * case that the user space daemon has crashed, they must be able to 14005b9c547cSRui Paulo * cleanup all stations and key entries in the AP tear down flow. Thus, 14015b9c547cSRui Paulo * this flag can/should always be set for cfg80211 drivers. 14025b9c547cSRui Paulo */ 14035b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT; 14045b9c547cSRui Paulo 14055b9c547cSRui Paulo if (!info.device_ap_sme) { 14065b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; 1407c1d255d3SCy Schubert drv->capa.flags2 |= WPA_DRIVER_FLAGS2_AP_SME; 14085b9c547cSRui Paulo 14095b9c547cSRui Paulo /* 14105b9c547cSRui Paulo * No AP SME is currently assumed to also indicate no AP MLME 14115b9c547cSRui Paulo * in the driver/firmware. 14125b9c547cSRui Paulo */ 14135b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; 14145b9c547cSRui Paulo } 14155b9c547cSRui Paulo 14165b9c547cSRui Paulo drv->device_ap_sme = info.device_ap_sme; 14175b9c547cSRui Paulo drv->poll_command_supported = info.poll_command_supported; 14185b9c547cSRui Paulo drv->data_tx_status = info.data_tx_status; 14195b9c547cSRui Paulo drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported; 14205b9c547cSRui Paulo if (info.set_qos_map_supported) 14215b9c547cSRui Paulo drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING; 14225b9c547cSRui Paulo drv->have_low_prio_scan = info.have_low_prio_scan; 14235b9c547cSRui Paulo 14245b9c547cSRui Paulo /* 14255b9c547cSRui Paulo * If poll command and tx status are supported, mac80211 is new enough 14265b9c547cSRui Paulo * to have everything we need to not need monitor interfaces. 14275b9c547cSRui Paulo */ 1428780fb4a2SCy Schubert drv->use_monitor = !info.device_ap_sme && 1429780fb4a2SCy Schubert (!info.poll_command_supported || !info.data_tx_status); 14305b9c547cSRui Paulo 14315b9c547cSRui Paulo /* 14325b9c547cSRui Paulo * If we aren't going to use monitor interfaces, but the 14335b9c547cSRui Paulo * driver doesn't support data TX status, we won't get TX 14345b9c547cSRui Paulo * status for EAPOL frames. 14355b9c547cSRui Paulo */ 14365b9c547cSRui Paulo if (!drv->use_monitor && !info.data_tx_status) 14375b9c547cSRui Paulo drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; 14385b9c547cSRui Paulo 1439780fb4a2SCy Schubert #ifdef CONFIG_DRIVER_NL80211_QCA 144085732ac8SCy Schubert if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) 14415b9c547cSRui Paulo qca_nl80211_check_dfs_capa(drv); 14425b9c547cSRui Paulo qca_nl80211_get_features(drv); 14435b9c547cSRui Paulo 1444780fb4a2SCy Schubert /* 1445780fb4a2SCy Schubert * To enable offchannel simultaneous support in wpa_supplicant, the 1446780fb4a2SCy Schubert * underlying driver needs to support the same along with offchannel TX. 1447780fb4a2SCy Schubert * Offchannel TX support is needed since remain_on_channel and 1448780fb4a2SCy Schubert * action_tx use some common data structures and hence cannot be 1449780fb4a2SCy Schubert * scheduled simultaneously. 1450780fb4a2SCy Schubert */ 1451780fb4a2SCy Schubert if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) 1452780fb4a2SCy Schubert drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; 1453780fb4a2SCy Schubert #endif /* CONFIG_DRIVER_NL80211_QCA */ 1454780fb4a2SCy Schubert 1455c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 1456c1d255d3SCy Schubert "nl80211: key_mgmt=0x%x enc=0x%x auth=0x%x flags=0x%llx rrm_flags=0x%x probe_resp_offloads=0x%x max_stations=%u max_remain_on_chan=%u max_scan_ssids=%d", 1457c1d255d3SCy Schubert drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth, 1458c1d255d3SCy Schubert (unsigned long long) drv->capa.flags, drv->capa.rrm_flags, 1459c1d255d3SCy Schubert drv->capa.probe_resp_offloads, drv->capa.max_stations, 1460c1d255d3SCy Schubert drv->capa.max_remain_on_chan, drv->capa.max_scan_ssids); 14615b9c547cSRui Paulo return 0; 14625b9c547cSRui Paulo } 14635b9c547cSRui Paulo 14645b9c547cSRui Paulo 14655b9c547cSRui Paulo struct phy_info_arg { 14665b9c547cSRui Paulo u16 *num_modes; 14675b9c547cSRui Paulo struct hostapd_hw_modes *modes; 14685b9c547cSRui Paulo int last_mode, last_chan_idx; 1469780fb4a2SCy Schubert int failed; 147085732ac8SCy Schubert u8 dfs_domain; 14715b9c547cSRui Paulo }; 14725b9c547cSRui Paulo 14735b9c547cSRui Paulo static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, 14745b9c547cSRui Paulo struct nlattr *ampdu_factor, 14755b9c547cSRui Paulo struct nlattr *ampdu_density, 14765b9c547cSRui Paulo struct nlattr *mcs_set) 14775b9c547cSRui Paulo { 14785b9c547cSRui Paulo if (capa) 14795b9c547cSRui Paulo mode->ht_capab = nla_get_u16(capa); 14805b9c547cSRui Paulo 14815b9c547cSRui Paulo if (ampdu_factor) 14825b9c547cSRui Paulo mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03; 14835b9c547cSRui Paulo 14845b9c547cSRui Paulo if (ampdu_density) 14855b9c547cSRui Paulo mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2; 14865b9c547cSRui Paulo 14875b9c547cSRui Paulo if (mcs_set && nla_len(mcs_set) >= 16) { 14885b9c547cSRui Paulo u8 *mcs; 14895b9c547cSRui Paulo mcs = nla_data(mcs_set); 14905b9c547cSRui Paulo os_memcpy(mode->mcs_set, mcs, 16); 14915b9c547cSRui Paulo } 14925b9c547cSRui Paulo } 14935b9c547cSRui Paulo 14945b9c547cSRui Paulo 14955b9c547cSRui Paulo static void phy_info_vht_capa(struct hostapd_hw_modes *mode, 14965b9c547cSRui Paulo struct nlattr *capa, 14975b9c547cSRui Paulo struct nlattr *mcs_set) 14985b9c547cSRui Paulo { 14995b9c547cSRui Paulo if (capa) 15005b9c547cSRui Paulo mode->vht_capab = nla_get_u32(capa); 15015b9c547cSRui Paulo 15025b9c547cSRui Paulo if (mcs_set && nla_len(mcs_set) >= 8) { 15035b9c547cSRui Paulo u8 *mcs; 15045b9c547cSRui Paulo mcs = nla_data(mcs_set); 15055b9c547cSRui Paulo os_memcpy(mode->vht_mcs_set, mcs, 8); 15065b9c547cSRui Paulo } 15075b9c547cSRui Paulo } 15085b9c547cSRui Paulo 15095b9c547cSRui Paulo 1510c1d255d3SCy Schubert static int phy_info_edmg_capa(struct hostapd_hw_modes *mode, 1511c1d255d3SCy Schubert struct nlattr *bw_config, 1512c1d255d3SCy Schubert struct nlattr *channels) 1513c1d255d3SCy Schubert { 1514c1d255d3SCy Schubert if (!bw_config || !channels) 1515c1d255d3SCy Schubert return NL_OK; 1516c1d255d3SCy Schubert 1517c1d255d3SCy Schubert mode->edmg.bw_config = nla_get_u8(bw_config); 1518c1d255d3SCy Schubert mode->edmg.channels = nla_get_u8(channels); 1519c1d255d3SCy Schubert 1520c1d255d3SCy Schubert if (!mode->edmg.channels || !mode->edmg.bw_config) 1521c1d255d3SCy Schubert return NL_STOP; 1522c1d255d3SCy Schubert 1523c1d255d3SCy Schubert return NL_OK; 1524c1d255d3SCy Schubert } 1525c1d255d3SCy Schubert 1526c1d255d3SCy Schubert 1527c1d255d3SCy Schubert static int cw2ecw(unsigned int cw) 1528c1d255d3SCy Schubert { 1529c1d255d3SCy Schubert int bit; 1530c1d255d3SCy Schubert 1531c1d255d3SCy Schubert if (cw == 0) 1532c1d255d3SCy Schubert return 0; 1533c1d255d3SCy Schubert 1534c1d255d3SCy Schubert for (bit = 1; cw != 1; bit++) 1535c1d255d3SCy Schubert cw >>= 1; 1536c1d255d3SCy Schubert 1537c1d255d3SCy Schubert return bit; 1538c1d255d3SCy Schubert } 1539c1d255d3SCy Schubert 1540c1d255d3SCy Schubert 15415b9c547cSRui Paulo static void phy_info_freq(struct hostapd_hw_modes *mode, 15425b9c547cSRui Paulo struct hostapd_channel_data *chan, 15435b9c547cSRui Paulo struct nlattr *tb_freq[]) 15445b9c547cSRui Paulo { 15455b9c547cSRui Paulo u8 channel; 1546c1d255d3SCy Schubert 1547c1d255d3SCy Schubert os_memset(chan, 0, sizeof(*chan)); 15485b9c547cSRui Paulo chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); 15495b9c547cSRui Paulo chan->flag = 0; 15504bc52338SCy Schubert chan->allowed_bw = ~0; 15515b9c547cSRui Paulo chan->dfs_cac_ms = 0; 15525b9c547cSRui Paulo if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES) 15535b9c547cSRui Paulo chan->chan = channel; 1554c1d255d3SCy Schubert else 1555c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 1556c1d255d3SCy Schubert "nl80211: No channel number found for frequency %u MHz", 1557c1d255d3SCy Schubert chan->freq); 15585b9c547cSRui Paulo 15595b9c547cSRui Paulo if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) 15605b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_DISABLED; 15615b9c547cSRui Paulo if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR]) 15625b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_NO_IR; 15635b9c547cSRui Paulo if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) 15645b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_RADAR; 15655b9c547cSRui Paulo if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY]) 15665b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY; 15675b9c547cSRui Paulo if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT]) 15685b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT; 15695b9c547cSRui Paulo 15704bc52338SCy Schubert if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ]) 15714bc52338SCy Schubert chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_10; 15724bc52338SCy Schubert if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ]) 15734bc52338SCy Schubert chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_20; 15744bc52338SCy Schubert if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS]) 15754bc52338SCy Schubert chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40P; 15764bc52338SCy Schubert if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS]) 15774bc52338SCy Schubert chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40M; 15784bc52338SCy Schubert if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ]) 15794bc52338SCy Schubert chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80; 15804bc52338SCy Schubert if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ]) 15814bc52338SCy Schubert chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160; 15824bc52338SCy Schubert 15835b9c547cSRui Paulo if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { 15845b9c547cSRui Paulo enum nl80211_dfs_state state = 15855b9c547cSRui Paulo nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); 15865b9c547cSRui Paulo 15875b9c547cSRui Paulo switch (state) { 15885b9c547cSRui Paulo case NL80211_DFS_USABLE: 15895b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_DFS_USABLE; 15905b9c547cSRui Paulo break; 15915b9c547cSRui Paulo case NL80211_DFS_AVAILABLE: 15925b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE; 15935b9c547cSRui Paulo break; 15945b9c547cSRui Paulo case NL80211_DFS_UNAVAILABLE: 15955b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE; 15965b9c547cSRui Paulo break; 15975b9c547cSRui Paulo } 15985b9c547cSRui Paulo } 15995b9c547cSRui Paulo 16005b9c547cSRui Paulo if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) { 16015b9c547cSRui Paulo chan->dfs_cac_ms = nla_get_u32( 16025b9c547cSRui Paulo tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]); 16035b9c547cSRui Paulo } 1604206b73d0SCy Schubert 1605206b73d0SCy Schubert chan->wmm_rules_valid = 0; 1606206b73d0SCy Schubert if (tb_freq[NL80211_FREQUENCY_ATTR_WMM]) { 1607206b73d0SCy Schubert static struct nla_policy wmm_policy[NL80211_WMMR_MAX + 1] = { 1608206b73d0SCy Schubert [NL80211_WMMR_CW_MIN] = { .type = NLA_U16 }, 1609206b73d0SCy Schubert [NL80211_WMMR_CW_MAX] = { .type = NLA_U16 }, 1610206b73d0SCy Schubert [NL80211_WMMR_AIFSN] = { .type = NLA_U8 }, 1611206b73d0SCy Schubert [NL80211_WMMR_TXOP] = { .type = NLA_U16 }, 1612206b73d0SCy Schubert }; 1613c1d255d3SCy Schubert static const u8 wmm_map[4] = { 1614c1d255d3SCy Schubert [NL80211_AC_BE] = WMM_AC_BE, 1615c1d255d3SCy Schubert [NL80211_AC_BK] = WMM_AC_BK, 1616c1d255d3SCy Schubert [NL80211_AC_VI] = WMM_AC_VI, 1617c1d255d3SCy Schubert [NL80211_AC_VO] = WMM_AC_VO, 1618c1d255d3SCy Schubert }; 1619206b73d0SCy Schubert struct nlattr *nl_wmm; 1620206b73d0SCy Schubert struct nlattr *tb_wmm[NL80211_WMMR_MAX + 1]; 1621206b73d0SCy Schubert int rem_wmm, ac, count = 0; 1622206b73d0SCy Schubert 1623206b73d0SCy Schubert nla_for_each_nested(nl_wmm, tb_freq[NL80211_FREQUENCY_ATTR_WMM], 1624206b73d0SCy Schubert rem_wmm) { 1625206b73d0SCy Schubert if (nla_parse_nested(tb_wmm, NL80211_WMMR_MAX, nl_wmm, 1626206b73d0SCy Schubert wmm_policy)) { 1627206b73d0SCy Schubert wpa_printf(MSG_DEBUG, 1628206b73d0SCy Schubert "nl80211: Failed to parse WMM rules attribute"); 1629206b73d0SCy Schubert return; 1630206b73d0SCy Schubert } 1631206b73d0SCy Schubert if (!tb_wmm[NL80211_WMMR_CW_MIN] || 1632206b73d0SCy Schubert !tb_wmm[NL80211_WMMR_CW_MAX] || 1633206b73d0SCy Schubert !tb_wmm[NL80211_WMMR_AIFSN] || 1634206b73d0SCy Schubert !tb_wmm[NL80211_WMMR_TXOP]) { 1635206b73d0SCy Schubert wpa_printf(MSG_DEBUG, 1636206b73d0SCy Schubert "nl80211: Channel is missing WMM rule attribute"); 1637206b73d0SCy Schubert return; 1638206b73d0SCy Schubert } 1639206b73d0SCy Schubert ac = nl_wmm->nla_type; 1640c1d255d3SCy Schubert if ((unsigned int) ac >= ARRAY_SIZE(wmm_map)) { 1641206b73d0SCy Schubert wpa_printf(MSG_DEBUG, 1642206b73d0SCy Schubert "nl80211: Invalid AC value %d", ac); 1643206b73d0SCy Schubert return; 1644206b73d0SCy Schubert } 1645206b73d0SCy Schubert 1646c1d255d3SCy Schubert ac = wmm_map[ac]; 1647206b73d0SCy Schubert chan->wmm_rules[ac].min_cwmin = 1648c1d255d3SCy Schubert cw2ecw(nla_get_u16( 1649c1d255d3SCy Schubert tb_wmm[NL80211_WMMR_CW_MIN])); 1650206b73d0SCy Schubert chan->wmm_rules[ac].min_cwmax = 1651c1d255d3SCy Schubert cw2ecw(nla_get_u16( 1652c1d255d3SCy Schubert tb_wmm[NL80211_WMMR_CW_MAX])); 1653206b73d0SCy Schubert chan->wmm_rules[ac].min_aifs = 1654206b73d0SCy Schubert nla_get_u8(tb_wmm[NL80211_WMMR_AIFSN]); 1655206b73d0SCy Schubert chan->wmm_rules[ac].max_txop = 1656206b73d0SCy Schubert nla_get_u16(tb_wmm[NL80211_WMMR_TXOP]) / 32; 1657206b73d0SCy Schubert count++; 1658206b73d0SCy Schubert } 1659206b73d0SCy Schubert 1660206b73d0SCy Schubert /* Set valid flag if all the AC rules are present */ 1661206b73d0SCy Schubert if (count == WMM_AC_NUM) 1662206b73d0SCy Schubert chan->wmm_rules_valid = 1; 1663206b73d0SCy Schubert } 16645b9c547cSRui Paulo } 16655b9c547cSRui Paulo 16665b9c547cSRui Paulo 16675b9c547cSRui Paulo static int phy_info_freqs(struct phy_info_arg *phy_info, 16685b9c547cSRui Paulo struct hostapd_hw_modes *mode, struct nlattr *tb) 16695b9c547cSRui Paulo { 16705b9c547cSRui Paulo static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { 16715b9c547cSRui Paulo [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, 16725b9c547cSRui Paulo [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, 16735b9c547cSRui Paulo [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, 16745b9c547cSRui Paulo [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, 16755b9c547cSRui Paulo [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, 16765b9c547cSRui Paulo [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 }, 16774bc52338SCy Schubert [NL80211_FREQUENCY_ATTR_NO_10MHZ] = { .type = NLA_FLAG }, 16784bc52338SCy Schubert [NL80211_FREQUENCY_ATTR_NO_20MHZ] = { .type = NLA_FLAG }, 16794bc52338SCy Schubert [NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = { .type = NLA_FLAG }, 16804bc52338SCy Schubert [NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG }, 16814bc52338SCy Schubert [NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG }, 16824bc52338SCy Schubert [NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG }, 16835b9c547cSRui Paulo }; 16845b9c547cSRui Paulo int new_channels = 0; 16855b9c547cSRui Paulo struct hostapd_channel_data *channel; 16865b9c547cSRui Paulo struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; 16875b9c547cSRui Paulo struct nlattr *nl_freq; 16885b9c547cSRui Paulo int rem_freq, idx; 16895b9c547cSRui Paulo 16905b9c547cSRui Paulo if (tb == NULL) 16915b9c547cSRui Paulo return NL_OK; 16925b9c547cSRui Paulo 16935b9c547cSRui Paulo nla_for_each_nested(nl_freq, tb, rem_freq) { 16945b9c547cSRui Paulo nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, 16955b9c547cSRui Paulo nla_data(nl_freq), nla_len(nl_freq), freq_policy); 16965b9c547cSRui Paulo if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) 16975b9c547cSRui Paulo continue; 16985b9c547cSRui Paulo new_channels++; 16995b9c547cSRui Paulo } 17005b9c547cSRui Paulo 17015b9c547cSRui Paulo channel = os_realloc_array(mode->channels, 17025b9c547cSRui Paulo mode->num_channels + new_channels, 17035b9c547cSRui Paulo sizeof(struct hostapd_channel_data)); 17045b9c547cSRui Paulo if (!channel) 1705780fb4a2SCy Schubert return NL_STOP; 17065b9c547cSRui Paulo 17075b9c547cSRui Paulo mode->channels = channel; 17085b9c547cSRui Paulo mode->num_channels += new_channels; 17095b9c547cSRui Paulo 17105b9c547cSRui Paulo idx = phy_info->last_chan_idx; 17115b9c547cSRui Paulo 17125b9c547cSRui Paulo nla_for_each_nested(nl_freq, tb, rem_freq) { 17135b9c547cSRui Paulo nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, 17145b9c547cSRui Paulo nla_data(nl_freq), nla_len(nl_freq), freq_policy); 17155b9c547cSRui Paulo if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) 17165b9c547cSRui Paulo continue; 17175b9c547cSRui Paulo phy_info_freq(mode, &mode->channels[idx], tb_freq); 17185b9c547cSRui Paulo idx++; 17195b9c547cSRui Paulo } 17205b9c547cSRui Paulo phy_info->last_chan_idx = idx; 17215b9c547cSRui Paulo 17225b9c547cSRui Paulo return NL_OK; 17235b9c547cSRui Paulo } 17245b9c547cSRui Paulo 17255b9c547cSRui Paulo 17265b9c547cSRui Paulo static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) 17275b9c547cSRui Paulo { 17285b9c547cSRui Paulo static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { 17295b9c547cSRui Paulo [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, 17305b9c547cSRui Paulo [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = 17315b9c547cSRui Paulo { .type = NLA_FLAG }, 17325b9c547cSRui Paulo }; 17335b9c547cSRui Paulo struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; 17345b9c547cSRui Paulo struct nlattr *nl_rate; 17355b9c547cSRui Paulo int rem_rate, idx; 17365b9c547cSRui Paulo 17375b9c547cSRui Paulo if (tb == NULL) 17385b9c547cSRui Paulo return NL_OK; 17395b9c547cSRui Paulo 17405b9c547cSRui Paulo nla_for_each_nested(nl_rate, tb, rem_rate) { 17415b9c547cSRui Paulo nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, 17425b9c547cSRui Paulo nla_data(nl_rate), nla_len(nl_rate), 17435b9c547cSRui Paulo rate_policy); 17445b9c547cSRui Paulo if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) 17455b9c547cSRui Paulo continue; 17465b9c547cSRui Paulo mode->num_rates++; 17475b9c547cSRui Paulo } 17485b9c547cSRui Paulo 17495b9c547cSRui Paulo mode->rates = os_calloc(mode->num_rates, sizeof(int)); 17505b9c547cSRui Paulo if (!mode->rates) 1751780fb4a2SCy Schubert return NL_STOP; 17525b9c547cSRui Paulo 17535b9c547cSRui Paulo idx = 0; 17545b9c547cSRui Paulo 17555b9c547cSRui Paulo nla_for_each_nested(nl_rate, tb, rem_rate) { 17565b9c547cSRui Paulo nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, 17575b9c547cSRui Paulo nla_data(nl_rate), nla_len(nl_rate), 17585b9c547cSRui Paulo rate_policy); 17595b9c547cSRui Paulo if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) 17605b9c547cSRui Paulo continue; 17615b9c547cSRui Paulo mode->rates[idx] = nla_get_u32( 17625b9c547cSRui Paulo tb_rate[NL80211_BITRATE_ATTR_RATE]); 17635b9c547cSRui Paulo idx++; 17645b9c547cSRui Paulo } 17655b9c547cSRui Paulo 17665b9c547cSRui Paulo return NL_OK; 17675b9c547cSRui Paulo } 17685b9c547cSRui Paulo 17695b9c547cSRui Paulo 1770206b73d0SCy Schubert static void phy_info_iftype_copy(struct he_capabilities *he_capab, 1771206b73d0SCy Schubert enum ieee80211_op_mode opmode, 1772206b73d0SCy Schubert struct nlattr **tb, struct nlattr **tb_flags) 1773206b73d0SCy Schubert { 1774206b73d0SCy Schubert enum nl80211_iftype iftype; 1775206b73d0SCy Schubert size_t len; 1776206b73d0SCy Schubert 1777206b73d0SCy Schubert switch (opmode) { 1778206b73d0SCy Schubert case IEEE80211_MODE_INFRA: 1779206b73d0SCy Schubert iftype = NL80211_IFTYPE_STATION; 1780206b73d0SCy Schubert break; 1781206b73d0SCy Schubert case IEEE80211_MODE_IBSS: 1782206b73d0SCy Schubert iftype = NL80211_IFTYPE_ADHOC; 1783206b73d0SCy Schubert break; 1784206b73d0SCy Schubert case IEEE80211_MODE_AP: 1785206b73d0SCy Schubert iftype = NL80211_IFTYPE_AP; 1786206b73d0SCy Schubert break; 1787206b73d0SCy Schubert case IEEE80211_MODE_MESH: 1788206b73d0SCy Schubert iftype = NL80211_IFTYPE_MESH_POINT; 1789206b73d0SCy Schubert break; 1790206b73d0SCy Schubert default: 1791206b73d0SCy Schubert return; 1792206b73d0SCy Schubert } 1793206b73d0SCy Schubert 1794206b73d0SCy Schubert if (!nla_get_flag(tb_flags[iftype])) 1795206b73d0SCy Schubert return; 1796206b73d0SCy Schubert 1797206b73d0SCy Schubert he_capab->he_supported = 1; 1798206b73d0SCy Schubert 1799206b73d0SCy Schubert if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) { 1800206b73d0SCy Schubert len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]); 1801206b73d0SCy Schubert 1802206b73d0SCy Schubert if (len > sizeof(he_capab->phy_cap)) 1803206b73d0SCy Schubert len = sizeof(he_capab->phy_cap); 1804206b73d0SCy Schubert os_memcpy(he_capab->phy_cap, 1805206b73d0SCy Schubert nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]), 1806206b73d0SCy Schubert len); 1807206b73d0SCy Schubert } 1808206b73d0SCy Schubert 1809206b73d0SCy Schubert if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]) { 1810206b73d0SCy Schubert len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]); 1811206b73d0SCy Schubert 1812206b73d0SCy Schubert if (len > sizeof(he_capab->mac_cap)) 1813206b73d0SCy Schubert len = sizeof(he_capab->mac_cap); 1814206b73d0SCy Schubert os_memcpy(he_capab->mac_cap, 1815206b73d0SCy Schubert nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]), 1816206b73d0SCy Schubert len); 1817206b73d0SCy Schubert } 1818206b73d0SCy Schubert 1819206b73d0SCy Schubert if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]) { 1820206b73d0SCy Schubert len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]); 1821206b73d0SCy Schubert 1822206b73d0SCy Schubert if (len > sizeof(he_capab->mcs)) 1823206b73d0SCy Schubert len = sizeof(he_capab->mcs); 1824206b73d0SCy Schubert os_memcpy(he_capab->mcs, 1825206b73d0SCy Schubert nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]), 1826206b73d0SCy Schubert len); 1827206b73d0SCy Schubert } 1828206b73d0SCy Schubert 1829206b73d0SCy Schubert if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]) { 1830206b73d0SCy Schubert len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]); 1831206b73d0SCy Schubert 1832206b73d0SCy Schubert if (len > sizeof(he_capab->ppet)) 1833206b73d0SCy Schubert len = sizeof(he_capab->ppet); 1834206b73d0SCy Schubert os_memcpy(&he_capab->ppet, 1835206b73d0SCy Schubert nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]), 1836206b73d0SCy Schubert len); 1837206b73d0SCy Schubert } 1838c1d255d3SCy Schubert 1839c1d255d3SCy Schubert if (tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]) { 1840c1d255d3SCy Schubert u16 capa; 1841c1d255d3SCy Schubert 1842c1d255d3SCy Schubert capa = nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]); 1843c1d255d3SCy Schubert he_capab->he_6ghz_capa = le_to_host16(capa); 1844c1d255d3SCy Schubert } 1845206b73d0SCy Schubert } 1846206b73d0SCy Schubert 1847206b73d0SCy Schubert 1848206b73d0SCy Schubert static int phy_info_iftype(struct hostapd_hw_modes *mode, 1849206b73d0SCy Schubert struct nlattr *nl_iftype) 1850206b73d0SCy Schubert { 1851206b73d0SCy Schubert struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1]; 1852206b73d0SCy Schubert struct nlattr *tb_flags[NL80211_IFTYPE_MAX + 1]; 1853206b73d0SCy Schubert unsigned int i; 1854206b73d0SCy Schubert 1855206b73d0SCy Schubert nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX, 1856206b73d0SCy Schubert nla_data(nl_iftype), nla_len(nl_iftype), NULL); 1857206b73d0SCy Schubert 1858206b73d0SCy Schubert if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES]) 1859206b73d0SCy Schubert return NL_STOP; 1860206b73d0SCy Schubert 1861206b73d0SCy Schubert if (nla_parse_nested(tb_flags, NL80211_IFTYPE_MAX, 1862206b73d0SCy Schubert tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES], NULL)) 1863206b73d0SCy Schubert return NL_STOP; 1864206b73d0SCy Schubert 1865206b73d0SCy Schubert for (i = 0; i < IEEE80211_MODE_NUM; i++) 1866206b73d0SCy Schubert phy_info_iftype_copy(&mode->he_capab[i], i, tb, tb_flags); 1867206b73d0SCy Schubert 1868206b73d0SCy Schubert return NL_OK; 1869206b73d0SCy Schubert } 1870206b73d0SCy Schubert 1871206b73d0SCy Schubert 18725b9c547cSRui Paulo static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) 18735b9c547cSRui Paulo { 18745b9c547cSRui Paulo struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; 18755b9c547cSRui Paulo struct hostapd_hw_modes *mode; 18765b9c547cSRui Paulo int ret; 18775b9c547cSRui Paulo 18785b9c547cSRui Paulo if (phy_info->last_mode != nl_band->nla_type) { 18795b9c547cSRui Paulo mode = os_realloc_array(phy_info->modes, 18805b9c547cSRui Paulo *phy_info->num_modes + 1, 18815b9c547cSRui Paulo sizeof(*mode)); 1882780fb4a2SCy Schubert if (!mode) { 1883780fb4a2SCy Schubert phy_info->failed = 1; 1884780fb4a2SCy Schubert return NL_STOP; 1885780fb4a2SCy Schubert } 18865b9c547cSRui Paulo phy_info->modes = mode; 18875b9c547cSRui Paulo 18885b9c547cSRui Paulo mode = &phy_info->modes[*(phy_info->num_modes)]; 18895b9c547cSRui Paulo os_memset(mode, 0, sizeof(*mode)); 18905b9c547cSRui Paulo mode->mode = NUM_HOSTAPD_MODES; 18915b9c547cSRui Paulo mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN | 18925b9c547cSRui Paulo HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN; 18935b9c547cSRui Paulo 18945b9c547cSRui Paulo /* 18955b9c547cSRui Paulo * Unsupported VHT MCS stream is defined as value 3, so the VHT 18965b9c547cSRui Paulo * MCS RX/TX map must be initialized with 0xffff to mark all 8 18975b9c547cSRui Paulo * possible streams as unsupported. This will be overridden if 18985b9c547cSRui Paulo * driver advertises VHT support. 18995b9c547cSRui Paulo */ 19005b9c547cSRui Paulo mode->vht_mcs_set[0] = 0xff; 19015b9c547cSRui Paulo mode->vht_mcs_set[1] = 0xff; 19025b9c547cSRui Paulo mode->vht_mcs_set[4] = 0xff; 19035b9c547cSRui Paulo mode->vht_mcs_set[5] = 0xff; 19045b9c547cSRui Paulo 19055b9c547cSRui Paulo *(phy_info->num_modes) += 1; 19065b9c547cSRui Paulo phy_info->last_mode = nl_band->nla_type; 19075b9c547cSRui Paulo phy_info->last_chan_idx = 0; 19085b9c547cSRui Paulo } else 19095b9c547cSRui Paulo mode = &phy_info->modes[*(phy_info->num_modes) - 1]; 19105b9c547cSRui Paulo 19115b9c547cSRui Paulo nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), 19125b9c547cSRui Paulo nla_len(nl_band), NULL); 19135b9c547cSRui Paulo 19145b9c547cSRui Paulo phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA], 19155b9c547cSRui Paulo tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR], 19165b9c547cSRui Paulo tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY], 19175b9c547cSRui Paulo tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); 19185b9c547cSRui Paulo phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA], 19195b9c547cSRui Paulo tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); 1920c1d255d3SCy Schubert ret = phy_info_edmg_capa(mode, 1921c1d255d3SCy Schubert tb_band[NL80211_BAND_ATTR_EDMG_BW_CONFIG], 1922c1d255d3SCy Schubert tb_band[NL80211_BAND_ATTR_EDMG_CHANNELS]); 1923c1d255d3SCy Schubert if (ret == NL_OK) 1924c1d255d3SCy Schubert ret = phy_info_freqs(phy_info, mode, 1925c1d255d3SCy Schubert tb_band[NL80211_BAND_ATTR_FREQS]); 1926780fb4a2SCy Schubert if (ret == NL_OK) 19275b9c547cSRui Paulo ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); 1928780fb4a2SCy Schubert if (ret != NL_OK) { 1929780fb4a2SCy Schubert phy_info->failed = 1; 19305b9c547cSRui Paulo return ret; 1931780fb4a2SCy Schubert } 19325b9c547cSRui Paulo 1933206b73d0SCy Schubert if (tb_band[NL80211_BAND_ATTR_IFTYPE_DATA]) { 1934206b73d0SCy Schubert struct nlattr *nl_iftype; 1935206b73d0SCy Schubert int rem_band; 1936206b73d0SCy Schubert 1937206b73d0SCy Schubert nla_for_each_nested(nl_iftype, 1938206b73d0SCy Schubert tb_band[NL80211_BAND_ATTR_IFTYPE_DATA], 1939206b73d0SCy Schubert rem_band) { 1940206b73d0SCy Schubert ret = phy_info_iftype(mode, nl_iftype); 1941206b73d0SCy Schubert if (ret != NL_OK) 1942206b73d0SCy Schubert return ret; 1943206b73d0SCy Schubert } 1944206b73d0SCy Schubert } 1945206b73d0SCy Schubert 19465b9c547cSRui Paulo return NL_OK; 19475b9c547cSRui Paulo } 19485b9c547cSRui Paulo 19495b9c547cSRui Paulo 19505b9c547cSRui Paulo static int phy_info_handler(struct nl_msg *msg, void *arg) 19515b9c547cSRui Paulo { 19525b9c547cSRui Paulo struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; 19535b9c547cSRui Paulo struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 19545b9c547cSRui Paulo struct phy_info_arg *phy_info = arg; 19555b9c547cSRui Paulo struct nlattr *nl_band; 19565b9c547cSRui Paulo int rem_band; 19575b9c547cSRui Paulo 19585b9c547cSRui Paulo nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 19595b9c547cSRui Paulo genlmsg_attrlen(gnlh, 0), NULL); 19605b9c547cSRui Paulo 19615b9c547cSRui Paulo if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) 19625b9c547cSRui Paulo return NL_SKIP; 19635b9c547cSRui Paulo 19645b9c547cSRui Paulo nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) 19655b9c547cSRui Paulo { 19665b9c547cSRui Paulo int res = phy_info_band(phy_info, nl_band); 19675b9c547cSRui Paulo if (res != NL_OK) 19685b9c547cSRui Paulo return res; 19695b9c547cSRui Paulo } 19705b9c547cSRui Paulo 19715b9c547cSRui Paulo return NL_SKIP; 19725b9c547cSRui Paulo } 19735b9c547cSRui Paulo 19745b9c547cSRui Paulo 19755b9c547cSRui Paulo static struct hostapd_hw_modes * 19765b9c547cSRui Paulo wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes, 19775b9c547cSRui Paulo u16 *num_modes) 19785b9c547cSRui Paulo { 19795b9c547cSRui Paulo u16 m; 19805b9c547cSRui Paulo struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; 19815b9c547cSRui Paulo int i, mode11g_idx = -1; 19825b9c547cSRui Paulo 19835b9c547cSRui Paulo /* heuristic to set up modes */ 19845b9c547cSRui Paulo for (m = 0; m < *num_modes; m++) { 19855b9c547cSRui Paulo if (!modes[m].num_channels) 19865b9c547cSRui Paulo continue; 1987c1d255d3SCy Schubert if (modes[m].channels[0].freq < 2000) { 1988c1d255d3SCy Schubert modes[m].num_channels = 0; 1989c1d255d3SCy Schubert continue; 1990c1d255d3SCy Schubert } else if (modes[m].channels[0].freq < 4000) { 19915b9c547cSRui Paulo modes[m].mode = HOSTAPD_MODE_IEEE80211B; 19925b9c547cSRui Paulo for (i = 0; i < modes[m].num_rates; i++) { 19935b9c547cSRui Paulo if (modes[m].rates[i] > 200) { 19945b9c547cSRui Paulo modes[m].mode = HOSTAPD_MODE_IEEE80211G; 19955b9c547cSRui Paulo break; 19965b9c547cSRui Paulo } 19975b9c547cSRui Paulo } 19985b9c547cSRui Paulo } else if (modes[m].channels[0].freq > 50000) 19995b9c547cSRui Paulo modes[m].mode = HOSTAPD_MODE_IEEE80211AD; 20005b9c547cSRui Paulo else 20015b9c547cSRui Paulo modes[m].mode = HOSTAPD_MODE_IEEE80211A; 20025b9c547cSRui Paulo } 20035b9c547cSRui Paulo 2004c1d255d3SCy Schubert /* Remove unsupported bands */ 2005c1d255d3SCy Schubert m = 0; 2006c1d255d3SCy Schubert while (m < *num_modes) { 2007c1d255d3SCy Schubert if (modes[m].mode == NUM_HOSTAPD_MODES) { 2008c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 2009c1d255d3SCy Schubert "nl80211: Remove unsupported mode"); 2010c1d255d3SCy Schubert os_free(modes[m].channels); 2011c1d255d3SCy Schubert os_free(modes[m].rates); 2012c1d255d3SCy Schubert if (m + 1 < *num_modes) 2013c1d255d3SCy Schubert os_memmove(&modes[m], &modes[m + 1], 2014c1d255d3SCy Schubert sizeof(struct hostapd_hw_modes) * 2015c1d255d3SCy Schubert (*num_modes - (m + 1))); 2016c1d255d3SCy Schubert (*num_modes)--; 2017c1d255d3SCy Schubert continue; 2018c1d255d3SCy Schubert } 2019c1d255d3SCy Schubert m++; 2020c1d255d3SCy Schubert } 2021c1d255d3SCy Schubert 20225b9c547cSRui Paulo /* If only 802.11g mode is included, use it to construct matching 20235b9c547cSRui Paulo * 802.11b mode data. */ 20245b9c547cSRui Paulo 20255b9c547cSRui Paulo for (m = 0; m < *num_modes; m++) { 20265b9c547cSRui Paulo if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) 20275b9c547cSRui Paulo return modes; /* 802.11b already included */ 20285b9c547cSRui Paulo if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) 20295b9c547cSRui Paulo mode11g_idx = m; 20305b9c547cSRui Paulo } 20315b9c547cSRui Paulo 20325b9c547cSRui Paulo if (mode11g_idx < 0) 20335b9c547cSRui Paulo return modes; /* 2.4 GHz band not supported at all */ 20345b9c547cSRui Paulo 20355b9c547cSRui Paulo nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes)); 20365b9c547cSRui Paulo if (nmodes == NULL) 20375b9c547cSRui Paulo return modes; /* Could not add 802.11b mode */ 20385b9c547cSRui Paulo 20395b9c547cSRui Paulo mode = &nmodes[*num_modes]; 20405b9c547cSRui Paulo os_memset(mode, 0, sizeof(*mode)); 20415b9c547cSRui Paulo (*num_modes)++; 20425b9c547cSRui Paulo modes = nmodes; 20435b9c547cSRui Paulo 20445b9c547cSRui Paulo mode->mode = HOSTAPD_MODE_IEEE80211B; 20455b9c547cSRui Paulo 20465b9c547cSRui Paulo mode11g = &modes[mode11g_idx]; 20475b9c547cSRui Paulo mode->num_channels = mode11g->num_channels; 204885732ac8SCy Schubert mode->channels = os_memdup(mode11g->channels, 204985732ac8SCy Schubert mode11g->num_channels * 20505b9c547cSRui Paulo sizeof(struct hostapd_channel_data)); 20515b9c547cSRui Paulo if (mode->channels == NULL) { 20525b9c547cSRui Paulo (*num_modes)--; 20535b9c547cSRui Paulo return modes; /* Could not add 802.11b mode */ 20545b9c547cSRui Paulo } 20555b9c547cSRui Paulo 20565b9c547cSRui Paulo mode->num_rates = 0; 20575b9c547cSRui Paulo mode->rates = os_malloc(4 * sizeof(int)); 20585b9c547cSRui Paulo if (mode->rates == NULL) { 20595b9c547cSRui Paulo os_free(mode->channels); 20605b9c547cSRui Paulo (*num_modes)--; 20615b9c547cSRui Paulo return modes; /* Could not add 802.11b mode */ 20625b9c547cSRui Paulo } 20635b9c547cSRui Paulo 20645b9c547cSRui Paulo for (i = 0; i < mode11g->num_rates; i++) { 20655b9c547cSRui Paulo if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 && 20665b9c547cSRui Paulo mode11g->rates[i] != 55 && mode11g->rates[i] != 110) 20675b9c547cSRui Paulo continue; 20685b9c547cSRui Paulo mode->rates[mode->num_rates] = mode11g->rates[i]; 20695b9c547cSRui Paulo mode->num_rates++; 20705b9c547cSRui Paulo if (mode->num_rates == 4) 20715b9c547cSRui Paulo break; 20725b9c547cSRui Paulo } 20735b9c547cSRui Paulo 20745b9c547cSRui Paulo if (mode->num_rates == 0) { 20755b9c547cSRui Paulo os_free(mode->channels); 20765b9c547cSRui Paulo os_free(mode->rates); 20775b9c547cSRui Paulo (*num_modes)--; 20785b9c547cSRui Paulo return modes; /* No 802.11b rates */ 20795b9c547cSRui Paulo } 20805b9c547cSRui Paulo 20815b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " 20825b9c547cSRui Paulo "information"); 20835b9c547cSRui Paulo 20845b9c547cSRui Paulo return modes; 20855b9c547cSRui Paulo } 20865b9c547cSRui Paulo 20875b9c547cSRui Paulo 20885b9c547cSRui Paulo static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start, 20895b9c547cSRui Paulo int end) 20905b9c547cSRui Paulo { 20915b9c547cSRui Paulo int c; 20925b9c547cSRui Paulo 20935b9c547cSRui Paulo for (c = 0; c < mode->num_channels; c++) { 20945b9c547cSRui Paulo struct hostapd_channel_data *chan = &mode->channels[c]; 20955b9c547cSRui Paulo if (chan->freq - 10 >= start && chan->freq + 10 <= end) 20965b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_HT40; 20975b9c547cSRui Paulo } 20985b9c547cSRui Paulo } 20995b9c547cSRui Paulo 21005b9c547cSRui Paulo 21015b9c547cSRui Paulo static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start, 21025b9c547cSRui Paulo int end) 21035b9c547cSRui Paulo { 21045b9c547cSRui Paulo int c; 21055b9c547cSRui Paulo 21065b9c547cSRui Paulo for (c = 0; c < mode->num_channels; c++) { 21075b9c547cSRui Paulo struct hostapd_channel_data *chan = &mode->channels[c]; 21085b9c547cSRui Paulo if (!(chan->flag & HOSTAPD_CHAN_HT40)) 21095b9c547cSRui Paulo continue; 21105b9c547cSRui Paulo if (chan->freq - 30 >= start && chan->freq - 10 <= end) 21115b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_HT40MINUS; 21125b9c547cSRui Paulo if (chan->freq + 10 >= start && chan->freq + 30 <= end) 21135b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_HT40PLUS; 21145b9c547cSRui Paulo } 21155b9c547cSRui Paulo } 21165b9c547cSRui Paulo 21175b9c547cSRui Paulo 21185b9c547cSRui Paulo static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp, 21195b9c547cSRui Paulo struct phy_info_arg *results) 21205b9c547cSRui Paulo { 21215b9c547cSRui Paulo u16 m; 21225b9c547cSRui Paulo 21235b9c547cSRui Paulo for (m = 0; m < *results->num_modes; m++) { 21245b9c547cSRui Paulo int c; 21255b9c547cSRui Paulo struct hostapd_hw_modes *mode = &results->modes[m]; 21265b9c547cSRui Paulo 21275b9c547cSRui Paulo for (c = 0; c < mode->num_channels; c++) { 21285b9c547cSRui Paulo struct hostapd_channel_data *chan = &mode->channels[c]; 21295b9c547cSRui Paulo if ((u32) chan->freq - 10 >= start && 21305b9c547cSRui Paulo (u32) chan->freq + 10 <= end) 21315b9c547cSRui Paulo chan->max_tx_power = max_eirp; 21325b9c547cSRui Paulo } 21335b9c547cSRui Paulo } 21345b9c547cSRui Paulo } 21355b9c547cSRui Paulo 21365b9c547cSRui Paulo 21375b9c547cSRui Paulo static void nl80211_reg_rule_ht40(u32 start, u32 end, 21385b9c547cSRui Paulo struct phy_info_arg *results) 21395b9c547cSRui Paulo { 21405b9c547cSRui Paulo u16 m; 21415b9c547cSRui Paulo 21425b9c547cSRui Paulo for (m = 0; m < *results->num_modes; m++) { 21435b9c547cSRui Paulo if (!(results->modes[m].ht_capab & 21445b9c547cSRui Paulo HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) 21455b9c547cSRui Paulo continue; 21465b9c547cSRui Paulo nl80211_set_ht40_mode(&results->modes[m], start, end); 21475b9c547cSRui Paulo } 21485b9c547cSRui Paulo } 21495b9c547cSRui Paulo 21505b9c547cSRui Paulo 21515b9c547cSRui Paulo static void nl80211_reg_rule_sec(struct nlattr *tb[], 21525b9c547cSRui Paulo struct phy_info_arg *results) 21535b9c547cSRui Paulo { 21545b9c547cSRui Paulo u32 start, end, max_bw; 21555b9c547cSRui Paulo u16 m; 21565b9c547cSRui Paulo 21575b9c547cSRui Paulo if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || 21585b9c547cSRui Paulo tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || 21595b9c547cSRui Paulo tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) 21605b9c547cSRui Paulo return; 21615b9c547cSRui Paulo 21625b9c547cSRui Paulo start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; 21635b9c547cSRui Paulo end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; 21645b9c547cSRui Paulo max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; 21655b9c547cSRui Paulo 21665b9c547cSRui Paulo if (max_bw < 20) 21675b9c547cSRui Paulo return; 21685b9c547cSRui Paulo 21695b9c547cSRui Paulo for (m = 0; m < *results->num_modes; m++) { 21705b9c547cSRui Paulo if (!(results->modes[m].ht_capab & 21715b9c547cSRui Paulo HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) 21725b9c547cSRui Paulo continue; 21735b9c547cSRui Paulo nl80211_set_ht40_mode_sec(&results->modes[m], start, end); 21745b9c547cSRui Paulo } 21755b9c547cSRui Paulo } 21765b9c547cSRui Paulo 21775b9c547cSRui Paulo 21785b9c547cSRui Paulo static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, 2179780fb4a2SCy Schubert int end, int max_bw) 21805b9c547cSRui Paulo { 21815b9c547cSRui Paulo int c; 21825b9c547cSRui Paulo 21835b9c547cSRui Paulo for (c = 0; c < mode->num_channels; c++) { 21845b9c547cSRui Paulo struct hostapd_channel_data *chan = &mode->channels[c]; 21855b9c547cSRui Paulo if (chan->freq - 10 >= start && chan->freq + 70 <= end) 21865b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_VHT_10_70; 21875b9c547cSRui Paulo 21885b9c547cSRui Paulo if (chan->freq - 30 >= start && chan->freq + 50 <= end) 21895b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_VHT_30_50; 21905b9c547cSRui Paulo 21915b9c547cSRui Paulo if (chan->freq - 50 >= start && chan->freq + 30 <= end) 21925b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_VHT_50_30; 21935b9c547cSRui Paulo 21945b9c547cSRui Paulo if (chan->freq - 70 >= start && chan->freq + 10 <= end) 21955b9c547cSRui Paulo chan->flag |= HOSTAPD_CHAN_VHT_70_10; 2196780fb4a2SCy Schubert 2197780fb4a2SCy Schubert if (max_bw >= 160) { 2198780fb4a2SCy Schubert if (chan->freq - 10 >= start && chan->freq + 150 <= end) 2199780fb4a2SCy Schubert chan->flag |= HOSTAPD_CHAN_VHT_10_150; 2200780fb4a2SCy Schubert 2201780fb4a2SCy Schubert if (chan->freq - 30 >= start && chan->freq + 130 <= end) 2202780fb4a2SCy Schubert chan->flag |= HOSTAPD_CHAN_VHT_30_130; 2203780fb4a2SCy Schubert 2204780fb4a2SCy Schubert if (chan->freq - 50 >= start && chan->freq + 110 <= end) 2205780fb4a2SCy Schubert chan->flag |= HOSTAPD_CHAN_VHT_50_110; 2206780fb4a2SCy Schubert 2207780fb4a2SCy Schubert if (chan->freq - 70 >= start && chan->freq + 90 <= end) 2208780fb4a2SCy Schubert chan->flag |= HOSTAPD_CHAN_VHT_70_90; 2209780fb4a2SCy Schubert 2210780fb4a2SCy Schubert if (chan->freq - 90 >= start && chan->freq + 70 <= end) 2211780fb4a2SCy Schubert chan->flag |= HOSTAPD_CHAN_VHT_90_70; 2212780fb4a2SCy Schubert 2213780fb4a2SCy Schubert if (chan->freq - 110 >= start && chan->freq + 50 <= end) 2214780fb4a2SCy Schubert chan->flag |= HOSTAPD_CHAN_VHT_110_50; 2215780fb4a2SCy Schubert 2216780fb4a2SCy Schubert if (chan->freq - 130 >= start && chan->freq + 30 <= end) 2217780fb4a2SCy Schubert chan->flag |= HOSTAPD_CHAN_VHT_130_30; 2218780fb4a2SCy Schubert 2219780fb4a2SCy Schubert if (chan->freq - 150 >= start && chan->freq + 10 <= end) 2220780fb4a2SCy Schubert chan->flag |= HOSTAPD_CHAN_VHT_150_10; 2221780fb4a2SCy Schubert } 22225b9c547cSRui Paulo } 22235b9c547cSRui Paulo } 22245b9c547cSRui Paulo 22255b9c547cSRui Paulo 22265b9c547cSRui Paulo static void nl80211_reg_rule_vht(struct nlattr *tb[], 22275b9c547cSRui Paulo struct phy_info_arg *results) 22285b9c547cSRui Paulo { 22295b9c547cSRui Paulo u32 start, end, max_bw; 22305b9c547cSRui Paulo u16 m; 22315b9c547cSRui Paulo 22325b9c547cSRui Paulo if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || 22335b9c547cSRui Paulo tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || 22345b9c547cSRui Paulo tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) 22355b9c547cSRui Paulo return; 22365b9c547cSRui Paulo 22375b9c547cSRui Paulo start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; 22385b9c547cSRui Paulo end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; 22395b9c547cSRui Paulo max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; 22405b9c547cSRui Paulo 22415b9c547cSRui Paulo if (max_bw < 80) 22425b9c547cSRui Paulo return; 22435b9c547cSRui Paulo 22445b9c547cSRui Paulo for (m = 0; m < *results->num_modes; m++) { 22455b9c547cSRui Paulo if (!(results->modes[m].ht_capab & 22465b9c547cSRui Paulo HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) 22475b9c547cSRui Paulo continue; 22485b9c547cSRui Paulo /* TODO: use a real VHT support indication */ 22495b9c547cSRui Paulo if (!results->modes[m].vht_capab) 22505b9c547cSRui Paulo continue; 22515b9c547cSRui Paulo 2252780fb4a2SCy Schubert nl80211_set_vht_mode(&results->modes[m], start, end, max_bw); 22535b9c547cSRui Paulo } 22545b9c547cSRui Paulo } 22555b9c547cSRui Paulo 22565b9c547cSRui Paulo 225785732ac8SCy Schubert static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region, 225885732ac8SCy Schubert u8 *dfs_domain) 225985732ac8SCy Schubert { 226085732ac8SCy Schubert if (region == NL80211_DFS_FCC) 226185732ac8SCy Schubert *dfs_domain = HOSTAPD_DFS_REGION_FCC; 226285732ac8SCy Schubert else if (region == NL80211_DFS_ETSI) 226385732ac8SCy Schubert *dfs_domain = HOSTAPD_DFS_REGION_ETSI; 226485732ac8SCy Schubert else if (region == NL80211_DFS_JP) 226585732ac8SCy Schubert *dfs_domain = HOSTAPD_DFS_REGION_JP; 226685732ac8SCy Schubert else 226785732ac8SCy Schubert *dfs_domain = 0; 226885732ac8SCy Schubert } 226985732ac8SCy Schubert 227085732ac8SCy Schubert 22715b9c547cSRui Paulo static const char * dfs_domain_name(enum nl80211_dfs_regions region) 22725b9c547cSRui Paulo { 22735b9c547cSRui Paulo switch (region) { 22745b9c547cSRui Paulo case NL80211_DFS_UNSET: 22755b9c547cSRui Paulo return "DFS-UNSET"; 22765b9c547cSRui Paulo case NL80211_DFS_FCC: 22775b9c547cSRui Paulo return "DFS-FCC"; 22785b9c547cSRui Paulo case NL80211_DFS_ETSI: 22795b9c547cSRui Paulo return "DFS-ETSI"; 22805b9c547cSRui Paulo case NL80211_DFS_JP: 22815b9c547cSRui Paulo return "DFS-JP"; 22825b9c547cSRui Paulo default: 22835b9c547cSRui Paulo return "DFS-invalid"; 22845b9c547cSRui Paulo } 22855b9c547cSRui Paulo } 22865b9c547cSRui Paulo 22875b9c547cSRui Paulo 22885b9c547cSRui Paulo static int nl80211_get_reg(struct nl_msg *msg, void *arg) 22895b9c547cSRui Paulo { 22905b9c547cSRui Paulo struct phy_info_arg *results = arg; 22915b9c547cSRui Paulo struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; 22925b9c547cSRui Paulo struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 22935b9c547cSRui Paulo struct nlattr *nl_rule; 22945b9c547cSRui Paulo struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1]; 22955b9c547cSRui Paulo int rem_rule; 22965b9c547cSRui Paulo static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { 22975b9c547cSRui Paulo [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, 22985b9c547cSRui Paulo [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, 22995b9c547cSRui Paulo [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, 23005b9c547cSRui Paulo [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, 23015b9c547cSRui Paulo [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, 23025b9c547cSRui Paulo [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, 23035b9c547cSRui Paulo }; 23045b9c547cSRui Paulo 23055b9c547cSRui Paulo nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 23065b9c547cSRui Paulo genlmsg_attrlen(gnlh, 0), NULL); 23075b9c547cSRui Paulo if (!tb_msg[NL80211_ATTR_REG_ALPHA2] || 23085b9c547cSRui Paulo !tb_msg[NL80211_ATTR_REG_RULES]) { 23095b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: No regulatory information " 23105b9c547cSRui Paulo "available"); 23115b9c547cSRui Paulo return NL_SKIP; 23125b9c547cSRui Paulo } 23135b9c547cSRui Paulo 23145b9c547cSRui Paulo if (tb_msg[NL80211_ATTR_DFS_REGION]) { 23155b9c547cSRui Paulo enum nl80211_dfs_regions dfs_domain; 23165b9c547cSRui Paulo dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]); 231785732ac8SCy Schubert nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain); 23185b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)", 23195b9c547cSRui Paulo (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 23205b9c547cSRui Paulo dfs_domain_name(dfs_domain)); 23215b9c547cSRui Paulo } else { 23225b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", 23235b9c547cSRui Paulo (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); 23245b9c547cSRui Paulo } 23255b9c547cSRui Paulo 23265b9c547cSRui Paulo nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) 23275b9c547cSRui Paulo { 23285b9c547cSRui Paulo u32 start, end, max_eirp = 0, max_bw = 0, flags = 0; 23295b9c547cSRui Paulo nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, 23305b9c547cSRui Paulo nla_data(nl_rule), nla_len(nl_rule), reg_policy); 23315b9c547cSRui Paulo if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL || 23325b9c547cSRui Paulo tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL) 23335b9c547cSRui Paulo continue; 23345b9c547cSRui Paulo start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000; 23355b9c547cSRui Paulo end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000; 23365b9c547cSRui Paulo if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) 23375b9c547cSRui Paulo max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100; 23385b9c547cSRui Paulo if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) 23395b9c547cSRui Paulo max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; 23405b9c547cSRui Paulo if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS]) 23415b9c547cSRui Paulo flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]); 23425b9c547cSRui Paulo 23435b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s", 23445b9c547cSRui Paulo start, end, max_bw, max_eirp, 23455b9c547cSRui Paulo flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "", 23465b9c547cSRui Paulo flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "", 23475b9c547cSRui Paulo flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "", 23485b9c547cSRui Paulo flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" : 23495b9c547cSRui Paulo "", 23505b9c547cSRui Paulo flags & NL80211_RRF_DFS ? " (DFS)" : "", 23515b9c547cSRui Paulo flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "", 23525b9c547cSRui Paulo flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "", 23535b9c547cSRui Paulo flags & NL80211_RRF_NO_IR ? " (no IR)" : ""); 23545b9c547cSRui Paulo if (max_bw >= 40) 23555b9c547cSRui Paulo nl80211_reg_rule_ht40(start, end, results); 23565b9c547cSRui Paulo if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) 23575b9c547cSRui Paulo nl80211_reg_rule_max_eirp(start, end, max_eirp, 23585b9c547cSRui Paulo results); 23595b9c547cSRui Paulo } 23605b9c547cSRui Paulo 23615b9c547cSRui Paulo nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) 23625b9c547cSRui Paulo { 23635b9c547cSRui Paulo nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, 23645b9c547cSRui Paulo nla_data(nl_rule), nla_len(nl_rule), reg_policy); 23655b9c547cSRui Paulo nl80211_reg_rule_sec(tb_rule, results); 23665b9c547cSRui Paulo } 23675b9c547cSRui Paulo 23685b9c547cSRui Paulo nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) 23695b9c547cSRui Paulo { 23705b9c547cSRui Paulo nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, 23715b9c547cSRui Paulo nla_data(nl_rule), nla_len(nl_rule), reg_policy); 23725b9c547cSRui Paulo nl80211_reg_rule_vht(tb_rule, results); 23735b9c547cSRui Paulo } 23745b9c547cSRui Paulo 23755b9c547cSRui Paulo return NL_SKIP; 23765b9c547cSRui Paulo } 23775b9c547cSRui Paulo 23785b9c547cSRui Paulo 23795b9c547cSRui Paulo static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, 23805b9c547cSRui Paulo struct phy_info_arg *results) 23815b9c547cSRui Paulo { 23825b9c547cSRui Paulo struct nl_msg *msg; 23835b9c547cSRui Paulo 23845b9c547cSRui Paulo msg = nlmsg_alloc(); 23855b9c547cSRui Paulo if (!msg) 23865b9c547cSRui Paulo return -ENOMEM; 23875b9c547cSRui Paulo 23885b9c547cSRui Paulo nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); 238985732ac8SCy Schubert if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) { 239085732ac8SCy Schubert if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) { 239185732ac8SCy Schubert nlmsg_free(msg); 239285732ac8SCy Schubert return -1; 239385732ac8SCy Schubert } 239485732ac8SCy Schubert } 239585732ac8SCy Schubert 2396c1d255d3SCy Schubert return send_and_recv_msgs(drv, msg, nl80211_get_reg, results, 2397c1d255d3SCy Schubert NULL, NULL); 23985b9c547cSRui Paulo } 23995b9c547cSRui Paulo 24005b9c547cSRui Paulo 24014bc52338SCy Schubert static const char * modestr(enum hostapd_hw_mode mode) 24024bc52338SCy Schubert { 24034bc52338SCy Schubert switch (mode) { 24044bc52338SCy Schubert case HOSTAPD_MODE_IEEE80211B: 24054bc52338SCy Schubert return "802.11b"; 24064bc52338SCy Schubert case HOSTAPD_MODE_IEEE80211G: 24074bc52338SCy Schubert return "802.11g"; 24084bc52338SCy Schubert case HOSTAPD_MODE_IEEE80211A: 24094bc52338SCy Schubert return "802.11a"; 24104bc52338SCy Schubert case HOSTAPD_MODE_IEEE80211AD: 24114bc52338SCy Schubert return "802.11ad"; 24124bc52338SCy Schubert default: 24134bc52338SCy Schubert return "?"; 24144bc52338SCy Schubert } 24154bc52338SCy Schubert } 24164bc52338SCy Schubert 24174bc52338SCy Schubert 24184bc52338SCy Schubert static void nl80211_dump_chan_list(struct hostapd_hw_modes *modes, 24194bc52338SCy Schubert u16 num_modes) 24204bc52338SCy Schubert { 24214bc52338SCy Schubert int i; 24224bc52338SCy Schubert 24234bc52338SCy Schubert if (!modes) 24244bc52338SCy Schubert return; 24254bc52338SCy Schubert 24264bc52338SCy Schubert for (i = 0; i < num_modes; i++) { 24274bc52338SCy Schubert struct hostapd_hw_modes *mode = &modes[i]; 24284bc52338SCy Schubert char str[200]; 24294bc52338SCy Schubert char *pos = str; 24304bc52338SCy Schubert char *end = pos + sizeof(str); 24314bc52338SCy Schubert int j, res; 24324bc52338SCy Schubert 24334bc52338SCy Schubert for (j = 0; j < mode->num_channels; j++) { 24344bc52338SCy Schubert struct hostapd_channel_data *chan = &mode->channels[j]; 24354bc52338SCy Schubert 24364bc52338SCy Schubert res = os_snprintf(pos, end - pos, " %d%s%s%s", 24374bc52338SCy Schubert chan->freq, 24384bc52338SCy Schubert (chan->flag & HOSTAPD_CHAN_DISABLED) ? 24394bc52338SCy Schubert "[DISABLED]" : "", 24404bc52338SCy Schubert (chan->flag & HOSTAPD_CHAN_NO_IR) ? 24414bc52338SCy Schubert "[NO_IR]" : "", 24424bc52338SCy Schubert (chan->flag & HOSTAPD_CHAN_RADAR) ? 24434bc52338SCy Schubert "[RADAR]" : ""); 24444bc52338SCy Schubert if (os_snprintf_error(end - pos, res)) 24454bc52338SCy Schubert break; 24464bc52338SCy Schubert pos += res; 24474bc52338SCy Schubert } 24484bc52338SCy Schubert 24494bc52338SCy Schubert *pos = '\0'; 24504bc52338SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Mode IEEE %s:%s", 24514bc52338SCy Schubert modestr(mode->mode), str); 24524bc52338SCy Schubert } 24534bc52338SCy Schubert } 24544bc52338SCy Schubert 24554bc52338SCy Schubert 24565b9c547cSRui Paulo struct hostapd_hw_modes * 245785732ac8SCy Schubert nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, 245885732ac8SCy Schubert u8 *dfs_domain) 24595b9c547cSRui Paulo { 24605b9c547cSRui Paulo u32 feat; 24615b9c547cSRui Paulo struct i802_bss *bss = priv; 24625b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv = bss->drv; 24635b9c547cSRui Paulo int nl_flags = 0; 24645b9c547cSRui Paulo struct nl_msg *msg; 24655b9c547cSRui Paulo struct phy_info_arg result = { 24665b9c547cSRui Paulo .num_modes = num_modes, 24675b9c547cSRui Paulo .modes = NULL, 24685b9c547cSRui Paulo .last_mode = -1, 2469780fb4a2SCy Schubert .failed = 0, 247085732ac8SCy Schubert .dfs_domain = 0, 24715b9c547cSRui Paulo }; 24725b9c547cSRui Paulo 24735b9c547cSRui Paulo *num_modes = 0; 24745b9c547cSRui Paulo *flags = 0; 247585732ac8SCy Schubert *dfs_domain = 0; 24765b9c547cSRui Paulo 24775b9c547cSRui Paulo feat = get_nl80211_protocol_features(drv); 24785b9c547cSRui Paulo if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) 24795b9c547cSRui Paulo nl_flags = NLM_F_DUMP; 24805b9c547cSRui Paulo if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) || 24815b9c547cSRui Paulo nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { 24825b9c547cSRui Paulo nlmsg_free(msg); 24835b9c547cSRui Paulo return NULL; 24845b9c547cSRui Paulo } 24855b9c547cSRui Paulo 2486c1d255d3SCy Schubert if (send_and_recv_msgs(drv, msg, phy_info_handler, &result, 2487c1d255d3SCy Schubert NULL, NULL) == 0) { 24884bc52338SCy Schubert struct hostapd_hw_modes *modes; 24894bc52338SCy Schubert 24905b9c547cSRui Paulo nl80211_set_regulatory_flags(drv, &result); 2491780fb4a2SCy Schubert if (result.failed) { 2492780fb4a2SCy Schubert int i; 2493780fb4a2SCy Schubert 2494780fb4a2SCy Schubert for (i = 0; result.modes && i < *num_modes; i++) { 2495780fb4a2SCy Schubert os_free(result.modes[i].channels); 2496780fb4a2SCy Schubert os_free(result.modes[i].rates); 2497780fb4a2SCy Schubert } 2498780fb4a2SCy Schubert os_free(result.modes); 249985732ac8SCy Schubert *num_modes = 0; 2500780fb4a2SCy Schubert return NULL; 2501780fb4a2SCy Schubert } 250285732ac8SCy Schubert 250385732ac8SCy Schubert *dfs_domain = result.dfs_domain; 250485732ac8SCy Schubert 25054bc52338SCy Schubert modes = wpa_driver_nl80211_postprocess_modes(result.modes, 25065b9c547cSRui Paulo num_modes); 25074bc52338SCy Schubert nl80211_dump_chan_list(modes, *num_modes); 25084bc52338SCy Schubert return modes; 25095b9c547cSRui Paulo } 25105b9c547cSRui Paulo 25115b9c547cSRui Paulo return NULL; 25125b9c547cSRui Paulo } 2513