diff --git a/src/drivers/driver.h b/src/drivers/driver.h index d73e6fdcd..3cdab5a7a 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1404,6 +1404,16 @@ enum wpa_driver_if_type { * WPA_IF_MESH - Mesh interface */ WPA_IF_MESH, + + /* + * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only) + */ + WPA_IF_TDLS, + + /* + * WPA_IF_IBSS - IBSS interface (used for pref freq only) + */ + WPA_IF_IBSS, }; struct wpa_init_params { @@ -3404,6 +3414,20 @@ struct wpa_driver_ops { */ int (*set_band)(void *priv, enum set_band band); + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @priv: Private driver interface data + * @if_type: Interface type + * @num: Number of channels + * @freq_list: Preferred channel frequency list encoded in MHz values + * Returns 0 on success, -1 on failure + * + * This command can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type, + unsigned int *num, unsigned int *freq_list); + /** * set_prob_oper_freq - Indicate probable P2P operating channel * @priv: Private driver interface data diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index fd598901b..96eb90b5f 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -5804,8 +5804,9 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( return NL80211_IFTYPE_P2P_DEVICE; case WPA_IF_MESH: return NL80211_IFTYPE_MESH_POINT; + default: + return -1; } - return -1; } @@ -8509,6 +8510,147 @@ static int nl80211_set_band(void *priv, enum set_band band) } +struct nl80211_pcl { + unsigned int num; + unsigned int *freq_list; +}; + +static int preferred_freq_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nl80211_pcl *param = arg; + struct nlattr *nl_vend, *attr; + enum qca_iface_type iface_type; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + unsigned int num, max_num; + u32 *freqs; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + if (!nl_vend) + return NL_SKIP; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE]; + if (!attr) { + wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found"); + param->num = 0; + return NL_SKIP; + } + + iface_type = (enum qca_iface_type) nla_get_u32(attr); + wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d", + iface_type); + + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]; + if (!attr) { + wpa_printf(MSG_ERROR, + "nl80211: preferred_freq_list couldn't be found"); + param->num = 0; + return NL_SKIP; + } + + /* + * param->num has the maximum number of entries for which there + * is room in the freq_list provided by the caller. + */ + freqs = nla_data(attr); + max_num = nla_len(attr) / sizeof(u32); + if (max_num > param->num) + max_num = param->num; + for (num = 0; num < max_num; num++) + param->freq_list[num] = freqs[num]; + param->num = num; + + return NL_SKIP; +} + + +static int nl80211_get_pref_freq_list(void *priv, + enum wpa_driver_if_type if_type, + unsigned int *num, + unsigned int *freq_list) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + unsigned int i; + struct nlattr *params; + struct nl80211_pcl param; + enum qca_iface_type iface_type; + + if (!drv->get_pref_freq_list) + return -1; + + switch (if_type) { + case WPA_IF_STATION: + iface_type = QCA_IFACE_TYPE_STA; + break; + case WPA_IF_AP_BSS: + iface_type = QCA_IFACE_TYPE_AP; + break; + case WPA_IF_P2P_GO: + iface_type = QCA_IFACE_TYPE_P2P_GO; + break; + case WPA_IF_P2P_CLIENT: + iface_type = QCA_IFACE_TYPE_P2P_CLIENT; + break; + case WPA_IF_IBSS: + iface_type = QCA_IFACE_TYPE_IBSS; + break; + case WPA_IF_TDLS: + iface_type = QCA_IFACE_TYPE_TDLS; + break; + default: + return -1; + } + + param.num = *num; + param.freq_list = freq_list; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, + iface_type)) { + wpa_printf(MSG_ERROR, + "%s: err in adding vendor_cmd and vendor_data", + __func__); + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, params); + + os_memset(freq_list, 0, *num * sizeof(freq_list[0])); + ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m); + if (ret) { + wpa_printf(MSG_ERROR, + "%s: err in send_and_recv_msgs", __func__); + return ret; + } + + *num = param.num; + + for (i = 0; i < *num; i++) { + wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d", + i, freq_list[i]); + } + + return 0; +} + + static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq) { struct i802_bss *bss = priv; @@ -8663,5 +8805,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .del_tx_ts = nl80211_del_ts, .do_acs = wpa_driver_do_acs, .set_band = nl80211_set_band, + .get_pref_freq_list = nl80211_get_pref_freq_list, .set_prob_oper_freq = nl80211_set_prob_oper_freq, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 078df4c0f..5c21e0faf 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -146,6 +146,7 @@ struct wpa_driver_nl80211_data { unsigned int set_rekey_offload:1; unsigned int p2p_go_ctwindow_supported:1; unsigned int setband_vendor_cmd_avail:1; + unsigned int get_pref_freq_list:1; unsigned int set_prob_oper_freq:1; u64 remain_on_chan_cookie; diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index e56f11138..4cf31238a 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -589,6 +589,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: drv->get_features_vendor_cmd_avail = 1; break; + case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST: + drv->get_pref_freq_list = 1; + break; case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL: drv->set_prob_oper_freq = 1; break; diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 41f9090ef..6dca2527f 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -6654,6 +6654,53 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, } +static int wpas_ctrl_iface_get_pref_freq_list( + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) +{ + unsigned int freq_list[100], num = 100, i; + int ret; + enum wpa_driver_if_type iface_type; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + /* buf: "" */ + if (os_strcmp(cmd, "STATION") == 0) + iface_type = WPA_IF_STATION; + else if (os_strcmp(cmd, "AP") == 0) + iface_type = WPA_IF_AP_BSS; + else if (os_strcmp(cmd, "P2P_GO") == 0) + iface_type = WPA_IF_P2P_GO; + else if (os_strcmp(cmd, "P2P_CLIENT") == 0) + iface_type = WPA_IF_P2P_CLIENT; + else if (os_strcmp(cmd, "IBSS") == 0) + iface_type = WPA_IF_IBSS; + else if (os_strcmp(cmd, "TDLS") == 0) + iface_type = WPA_IF_TDLS; + else + return -1; + + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)", + iface_type, buf); + + ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list); + if (ret) + return -1; + + for (i = 0; i < num; i++) { + ret = os_snprintf(pos, end - pos, "%s%u", + i > 0 ? "," : "", freq_list[i]); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + + return pos - buf; +} + + static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { @@ -8750,6 +8797,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) { if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) { + reply_len = wpas_ctrl_iface_get_pref_freq_list( + wpa_s, buf + 19, reply, reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 6ce4c2727..909515b35 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -893,6 +893,17 @@ static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s, return wpa_s->driver->set_band(wpa_s->drv_priv, band); } +static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type if_type, + unsigned int *num, + unsigned int *freq_list) +{ + if (!wpa_s->driver->get_pref_freq_list) + return 0; + return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type, + num, freq_list); +} + static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s, unsigned int freq) { diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 2d1adc9d9..48897d4f2 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -2801,6 +2801,13 @@ static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -3355,6 +3362,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { " enable=<0/1> [addr=mac-address " "mask=mac-address-mask] = scan MAC randomization" }, + { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL, + cli_cmd_flag_none, + " = retrieve preferred freq list for the specified interface type" }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } };