am e6ccb164: Add QCA vendor command support to set band to driver

* commit 'e6ccb16448fd0d5cf080fcb534266797855428d6':
  Add QCA vendor command support to set band to driver
diff --git a/src/common/defs.h b/src/common/defs.h
index 5b2d7c4..eb080ea 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -328,4 +328,10 @@
 	PLINK_BLOCKED,
 };
 
+enum set_band {
+	WPA_SETBAND_AUTO,
+	WPA_SETBAND_5G,
+	WPA_SETBAND_2G
+};
+
 #endif /* DEFS_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 3c35e79..2a6e242 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -152,6 +152,10 @@
 	QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99,
 	QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100,
 	QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101,
+	QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102,
+	QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
+	QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
+	QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
 };
 
 
@@ -172,6 +176,15 @@
 	/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
 	QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7,
 	QCA_WLAN_VENDOR_ATTR_TEST = 8,
+	/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
+	/* Unsigned 32-bit value. */
+	QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
+	/* Unsigned 32-bit value from enum qca_set_band. */
+	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_MAX	= QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@@ -262,4 +275,52 @@
 	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX =
 	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
 };
+
+enum qca_vendor_attr_get_preferred_freq_list {
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID,
+	/* A 32-unsigned value; the interface type/mode for which the preferred
+	 * frequency list is requested (see enum qca_iface_type for possible
+	 * values); used in GET_PREFERRED_FREQ_LIST command from user-space to
+	 * kernel and in the kernel response back to user-space.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+	/* An array of 32-unsigned values; values are frequency (MHz); sent
+	 * from kernel space to user space.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX =
+	QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1
+};
+
+enum qca_vendor_attr_probable_oper_channel {
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID,
+	/* 32-bit unsigned value; indicates the connection/iface type likely to
+	 * come on this channel (see enum qca_iface_type).
+	 */
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+	/* 32-bit unsigned value; the frequency (MHz) of the probable channel */
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX =
+	QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1
+};
+
+enum qca_iface_type {
+	QCA_IFACE_TYPE_STA,
+	QCA_IFACE_TYPE_AP,
+	QCA_IFACE_TYPE_P2P_CLIENT,
+	QCA_IFACE_TYPE_P2P_GO,
+	QCA_IFACE_TYPE_IBSS,
+	QCA_IFACE_TYPE_TDLS,
+};
+
+enum qca_set_band {
+	QCA_SETBAND_AUTO,
+	QCA_SETBAND_5G,
+	QCA_SETBAND_2G,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index d452d8c..7fe736b 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -3386,6 +3386,14 @@
 	 * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD).
 	 */
 	int (*do_acs)(void *priv, struct drv_acs_params *params);
+
+	/**
+	 * set_band - Notify driver of band selection
+	 * @priv: Private driver interface data
+	 * @band: The selected band(s)
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*set_band)(void *priv, enum set_band band);
 };
 
 
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index f74422b..2a05a3b 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -8423,6 +8423,53 @@
 }
 
 
+static int nl80211_set_band(void *priv, enum set_band band)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *data;
+	int ret;
+	enum qca_set_band qca_band;
+
+	if (!drv->setband_vendor_cmd_avail)
+		return -1;
+
+	switch (band) {
+	case WPA_SETBAND_AUTO:
+		qca_band = QCA_SETBAND_AUTO;
+		break;
+	case WPA_SETBAND_5G:
+		qca_band = QCA_SETBAND_5G;
+		break;
+	case WPA_SETBAND_2G:
+		qca_band = QCA_SETBAND_2G;
+		break;
+	default:
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SETBAND) ||
+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE, qca_band)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+	nla_nest_end(msg, data);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver setband function failed: %s",
+			   strerror(errno));
+	}
+	return ret;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
 	.desc = "Linux nl80211/cfg80211",
@@ -8530,4 +8577,5 @@
 	.add_tx_ts = nl80211_add_ts,
 	.del_tx_ts = nl80211_del_ts,
 	.do_acs = wpa_driver_do_acs,
+	.set_band = nl80211_set_band,
 };
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index acfe959..1536d2f 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -145,6 +145,7 @@
 	unsigned int get_features_vendor_cmd_avail:1;
 	unsigned int set_rekey_offload:1;
 	unsigned int p2p_go_ctwindow_supported:1;
+	unsigned int setband_vendor_cmd_avail:1;
 
 	u64 remain_on_chan_cookie;
 	u64 send_action_cookie;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index e23c57e..4929cea 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -593,6 +593,9 @@
 					drv->capa.flags |=
 						WPA_DRIVER_FLAGS_ACS_OFFLOAD;
 					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
+					drv->setband_vendor_cmd_avail = 1;
+					break;
 				}
 			}
 
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index d0d70e9..3d9936e 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -284,6 +284,30 @@
 }
 
 
+static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band)
+{
+	union wpa_event_data event;
+
+	if (os_strcmp(band, "AUTO") == 0)
+		wpa_s->setband = WPA_SETBAND_AUTO;
+	else if (os_strcmp(band, "5G") == 0)
+		wpa_s->setband = WPA_SETBAND_5G;
+	else if (os_strcmp(band, "2G") == 0)
+		wpa_s->setband = WPA_SETBAND_2G;
+	else
+		return -1;
+
+	if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) {
+		os_memset(&event, 0, sizeof(event));
+		event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+		event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
+		wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event);
+	}
+
+	return 0;
+}
+
+
 static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
 					 char *cmd)
 {
@@ -447,14 +471,7 @@
 		ret = wpas_ctrl_set_blob(wpa_s, value);
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 	} else if (os_strcasecmp(cmd, "setband") == 0) {
-		if (os_strcmp(value, "AUTO") == 0)
-			wpa_s->setband = WPA_SETBAND_AUTO;
-		else if (os_strcmp(value, "5G") == 0)
-			wpa_s->setband = WPA_SETBAND_5G;
-		else if (os_strcmp(value, "2G") == 0)
-			wpa_s->setband = WPA_SETBAND_2G;
-		else
-			ret = -1;
+		ret = wpas_ctrl_set_band(wpa_s, value);
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 1fcb180..d1f9f8b 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -885,4 +885,12 @@
 }
 #endif /* CONFIG_MACSEC */
 
+static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s,
+				  enum set_band band)
+{
+	if (!wpa_s->driver->set_band)
+		return -1;
+	return wpa_s->driver->set_band(wpa_s->drv_priv, band);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index dd5b245..4f63456 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -482,7 +482,7 @@
 	struct wpa_ssid_value *disallow_aps_ssid;
 	size_t disallow_aps_ssid_count;
 
-	enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband;
+	enum set_band setband;
 
 	/* Preferred network for the next connection attempt */
 	struct wpa_ssid *next_ssid;