Added BRCM vendor command to trigger ACS scan.

After ACS is finished,
DHD driver will send results by event BRCM_VENDOR_EVENT_ACS.

Bug:149885852
Test: Manual-WiFi hotspot turn on-turn off multiple times

Signed-off-by: Ajay Davanageri <ajay.davanageri@broadcom.com>
Change-Id: Iae8a7ad73ce7d78d024a692da31c7fea6d8bf134
diff --git a/hostapd/android.config b/hostapd/android.config
index 5b46bc4..bf21fc6 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -25,9 +25,13 @@
 #LIBS += -L$(LIBNL)/lib
 CONFIG_LIBNL20=y
 
+# BCM vendor extensions to nl80211
+ifeq ($(BOARD_WLAN_DEVICE),bcmdhd)
+CONFIG_DRIVER_NL80211_BRCM=y
+else
 # QCA vendor extensions to nl80211
 CONFIG_DRIVER_NL80211_QCA=y
-
+endif
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
diff --git a/src/common/brcm_vendor.h b/src/common/brcm_vendor.h
new file mode 100644
index 0000000..cb5b348
--- /dev/null
+++ b/src/common/brcm_vendor.h
@@ -0,0 +1,65 @@
+/*
+ * Broadcom Corporation OUI and vendor specific assignments
+ * Copyright (c) 2015, Broadcom Corporation.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BRCM_VENDOR_H
+#define BRCM_VENDOR_H
+
+/*
+ * This file is a registry of identifier assignments from the Broadcom
+ * OUI 00:10:18 for purposes other than MAC address assignment. New identifiers
+ * can be assigned through normal review process for changes to the upstream
+ * hostap.git repository.
+ */
+
+#define OUI_BRCM  0x001018
+
+/**
+ * enum brcm_nl80211_vendor_subcmds - BRCM nl80211 vendor command identifiers
+ *
+ * @BRCM_VENDOR_SUBCMD_UNSPEC: Reserved value 0
+ *
+ * @BRCM_VENDOR_SUBCMD_PRIV_STR: String command/event
+ */
+enum brcm_nl80211_vendor_subcmds {
+	BRCM_VENDOR_SUBCMD_UNSPEC		= 0,
+	BRCM_VENDOR_SCMD_ACS			= 9,
+	BRCM_VENDOR_SCMD_MAX			= 10
+};
+
+/**
+ * enum brcm_nl80211_vendor_events - BRCM nl80211 asynchoronous event identifiers
+ *
+ * @BRCM_VENDOR_EVENT_UNSPEC: Reserved value 0
+ *
+ * @BRCM_VENDOR_EVENT_PRIV_STR: String command/event
+ */
+enum brcm_nl80211_vendor_events {
+        BRCM_VENDOR_EVENT_UNSPEC,
+	BRCM_VENDOR_EVENT_ACS			= 42,
+	BRCM_VENDOR_EVENT_LAST			= 44
+};
+
+enum wl_vendor_attr_acs_offload {
+	BRCM_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
+	BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ,
+	BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ,
+	BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
+	BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
+
+	BRCM_VENDOR_ATTR_ACS_HW_MODE,
+	BRCM_VENDOR_ATTR_ACS_HT_ENABLED,
+	BRCM_VENDOR_ATTR_ACS_HT40_ENABLED,
+	BRCM_VENDOR_ATTR_ACS_VHT_ENABLED,
+	BRCM_VENDOR_ATTR_ACS_CHWIDTH,
+	BRCM_VENDOR_ATTR_ACS_CH_LIST,
+	BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
+
+	BRCM_VENDOR_ATTR_ACS_LAST
+};
+#endif /* BRCM_VENDOR_H */
+
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 204099b..5a760ff 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -37,7 +37,9 @@
 #include "radiotap_iter.h"
 #include "rfkill.h"
 #include "driver_nl80211.h"
-
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+#include "common/brcm_vendor.h"
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
 
 #ifndef NETLINK_CAP_ACK
 #define NETLINK_CAP_ACK 10
@@ -3037,6 +3039,98 @@
 	return num_suites;
 }
 
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+static int wpa_driver_do_broadcom_acs(void *priv, struct drv_acs_params
+	*params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *data;
+	int  freq_list_len;
+	int  ret = 0;
+	do {
+		freq_list_len =
+			int_array_len(params->freq_list);
+		wpa_printf(MSG_DEBUG, "%s: freq_list_len=%d",
+				__FUNCTION__, freq_list_len);
+		msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+		if (!msg) {
+			wpa_printf(MSG_ERROR, "%s: *errof, no memory for msg", __FUNCTION__);
+			return ret;
+		}
+		if ((nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM))) {
+			wpa_printf(MSG_ERROR, "%s: *errof, NL80211_ATTR_VENDOR_ID",
+					__FUNCTION__);
+			nlmsg_free(msg);
+			return ret;
+		}
+
+		if ((nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, BRCM_VENDOR_SCMD_ACS))) {
+			wpa_printf(MSG_ERROR, "%s: *errof, NL80211_ATTR_VENDOR_SUBCMD",
+				__FUNCTION__);
+			nlmsg_free(msg);
+			return ret;
+		}
+		if (!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA))) {
+			wpa_printf(MSG_ERROR, "%s: *errof, NL80211_ATTR_VENDOR_DATA", __FUNCTION__);
+			nlmsg_free(msg);
+			return ret;
+		}
+		if ((nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HW_MODE, params->hw_mode))) {
+			wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_HW_MODE",
+				__FUNCTION__);
+			nlmsg_free(msg);
+			return ret;
+		}
+		if ((nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT_ENABLED, params->ht_enabled))) {
+			wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_HT_ENABLED",
+				__FUNCTION__);
+			nlmsg_free(msg);
+			return ret;
+		}
+		if ((nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT40_ENABLED, params->ht40_enabled))) {
+			wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_HT40_ENABLED",
+				__FUNCTION__);
+			nlmsg_free(msg);
+			return ret;
+		}
+		if ((nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_VHT_ENABLED, params->vht_enabled))) {
+			wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_VHT_ENABLED",
+				__FUNCTION__);
+			nlmsg_free(msg);
+			return ret;
+		}
+		if ((nla_put_u16(msg, BRCM_VENDOR_ATTR_ACS_CHWIDTH, params->ch_width))) {
+			wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_CHWIDTH",
+				__FUNCTION__);
+			nlmsg_free(msg);
+			return ret;
+		}
+		wpa_printf(MSG_DEBUG, "%s: ht40=%d, ch_width=%d\n",
+			__FUNCTION__, params->ht40_enabled, params->ch_width);
+		if ((freq_list_len > 0)	&& (nla_put(msg, BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
+				sizeof(int) * freq_list_len, params->freq_list))) {
+			wpa_printf(MSG_ERROR, "%s: *error, BRCM_VENDOR_ATTR_ACS_FREQ_LIST,"
+				"list_len=%d\n", __FUNCTION__, freq_list_len);
+			nlmsg_free(msg);
+			return ret;
+		}
+		nla_nest_end(msg, data);
+		wpa_printf(MSG_DEBUG, "nl80211: ACS Params: HW_MODE: %d HT: %d HT40:"
+			" %d VHT: %d BW: %d\n",
+			params->hw_mode, params->ht_enabled, params->ht40_enabled,
+			params->vht_enabled, params->ch_width);
+		ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+		if (ret) {
+			wpa_printf(MSG_ERROR, "nl80211:	Failed to invoke vendor"
+				" driver ACS function: %s\n",
+				strerror(errno));
+		}
+	} while (0);
+	return ret;
+}
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
 
 #ifdef CONFIG_DRIVER_NL80211_QCA
 static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
@@ -11641,6 +11735,9 @@
 	.add_sta_node = nl80211_add_sta_node,
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
+#if defined(CONFIG_DRIVER_NL80211_BRCM)
+	.do_acs = wpa_driver_do_broadcom_acs,
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
 	.get_ext_capab = nl80211_get_ext_capab,
 	.update_connect_params = nl80211_update_connection_params,
 	.send_external_auth_status = nl80211_send_external_auth_status,
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 3e8dcef..435df11 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -17,7 +17,9 @@
 #include "common/qca-vendor.h"
 #include "common/qca-vendor-attr.h"
 #include "driver_nl80211.h"
-
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+#include "common/brcm_vendor.h"
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
 
 static int protocol_feature_handler(struct nl_msg *msg, void *arg)
 {
@@ -992,7 +994,21 @@
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 				}
 			}
-
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+			if (vinfo->vendor_id == OUI_BRCM) {
+				wpa_printf(MSG_MSGDUMP, "vendor:%x cmd:0x%x\n",
+						vinfo->vendor_id, vinfo->subcmd);
+				switch (vinfo->subcmd) {
+					case BRCM_VENDOR_SCMD_ACS:
+						drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+						drv->capa.flags |=
+							WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+						break;
+					default:
+						break;
+				}
+			}
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
 				   vinfo->vendor_id, vinfo->subcmd);
 		}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 6a2de1f..0454d29 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -18,7 +18,9 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "driver_nl80211.h"
-
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+#include "common/brcm_vendor.h"
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
 
 static const char * nl80211_command_to_string(enum nl80211_commands cmd)
 {
@@ -1705,7 +1707,96 @@
 	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
 }
 
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+static void brcm_nl80211_acs_select_ch(struct wpa_driver_nl80211_data
+	*drv, const u8 *data, size_t len)
+{
+	struct nlattr *tb[BRCM_VENDOR_ATTR_ACS_LAST + 1];
+	union wpa_event_data event;
 
+	wpa_printf(MSG_DEBUG, "nl80211: vendor ACS channel selection vendor even received");
+
+	if (nla_parse(tb, BRCM_VENDOR_ATTR_ACS_LAST, (struct nlattr*) data, len, NULL)
+		|| (!tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ])
+			|| (!tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ])) {
+			return;
+		}
+
+	os_memset(&event, 0, sizeof(event));
+	if (tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ]) {
+		event.acs_selected_channels.pri_freq =
+				nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ]);
+	}
+
+	wpa_printf(MSG_MSGDUMP, "got pri_freq=%d\n",
+		event.acs_selected_channels.pri_freq );
+
+	if (tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ]) {
+		event.acs_selected_channels.sec_freq =
+			nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ]);
+	}
+
+	wpa_printf(MSG_MSGDUMP, "got sec_freq=%d\n",
+		event.acs_selected_channels.sec_freq );
+	if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) {
+		event.acs_selected_channels.vht_seg0_center_ch =
+			nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+	}
+
+	if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) {
+		event.acs_selected_channels.vht_seg1_center_ch =
+			nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+	}
+	if (tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH]) {
+		event.acs_selected_channels.ch_width =
+			nla_get_u16(tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH]);
+	}
+	if (tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]) {
+		event.acs_selected_channels.hw_mode =
+		nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]);
+		if ((event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES)
+			|| (event.acs_selected_channels.hw_mode
+				== HOSTAPD_MODE_IEEE80211ANY)) {
+			wpa_printf(MSG_ERROR, "nl80211: Invalid hw_mode %d in ACS selection event",
+				event.acs_selected_channels.hw_mode);
+			return;
+		}
+	}
+
+	wpa_printf(MSG_INFO, "nl80211: ACS Results: PCH: %d SCH: %d BW: %d"
+		" VHT0: %d VHT1: %d HW_MODE: %d",
+		event.acs_selected_channels.pri_freq,
+		event.acs_selected_channels.sec_freq,
+		event.acs_selected_channels.ch_width,
+		event.acs_selected_channels.vht_seg0_center_ch,
+		event.acs_selected_channels.vht_seg1_center_ch,
+		event.acs_selected_channels.hw_mode);
+	wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED,
+		&event);
+	return;
+}
+
+static void nl80211_vendor_event_brcm( struct wpa_driver_nl80211_data *drv,
+	u32 subcmd, u8 *data, size_t len)
+{
+	union wpa_event_data event;
+        const struct nlattr *iter;
+        int rem = len;
+
+	wpa_printf(MSG_MSGDUMP, "got vendor event %d", subcmd);
+	memset(&event, 0, sizeof(event));
+	switch (subcmd) {
+	case BRCM_VENDOR_EVENT_ACS:
+		wpa_printf(MSG_DEBUG, "nl80211: Received VENDOR_EVENT_ACS");
+		brcm_nl80211_acs_select_ch(drv, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event %u", subcmd);
+		break;
+	}
+
+}
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
 #ifdef CONFIG_DRIVER_NL80211_QCA
 
 static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
@@ -2267,6 +2358,11 @@
 	case OUI_QCA:
 		nl80211_vendor_event_qca(drv, subcmd, data, len);
 		break;
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+	case OUI_BRCM:
+	        nl80211_vendor_event_brcm(drv, subcmd, data, len);
+	        break;
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
 	default:
 		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
 		break;
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 55a98ef..a03d4a0 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -26,6 +26,10 @@
 CONFIG_LIBNL3_ROUTE=y
 endif
 
+ifdef CONFIG_DRIVER_NL80211_BRCM
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
+endif
+
 ifdef CONFIG_DRIVER_MACSEC_QCA
 DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
 DRV_OBJS += ../src/drivers/driver_macsec_qca.o
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 5a32a24..10eab6a 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -41,6 +41,9 @@
 ifdef CONFIG_DRIVER_NL80211_QCA
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
 endif
+ifdef CONFIG_DRIVER_NL80211_BRCM
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
+endif
 NEED_SME=y
 NEED_AP_MLME=y
 NEED_NETLINK=y