qcacmn: Fix connection issue with hidden ssid in DFS channel

Repeater AP failed to associate to Root AP with
hidden ssid in DFS channel.

Normally probe request is sent in passive channels if beacon
from AP is found. With strict passive scan, probe request is
not sent in passive channels even after beacon is found in that
channel.

If AP is configured in hidden ssid, Client/STA does not get the
Probe Response and thus, no SSID information is present in the
scan table to start authentication.

To overcome this, user needs to set the desired {BSSID, SSID} pair,
Example: Desired pair = {00:03:7F:AA:BB:CC, MOON1},
so that when driver receives a beacon it compares the
BSSID and updates the SSID field to update the scan table.

In general, we already set the ssid in sta configuration
But in this case, we also need to configure BSSID of the hidden AP.
This can be configured through iwpriv cmd.
Example: iwpriv athx conf_bssid 11:03:7F:46:03:17

Change-Id: I03e1efed1168c911fc8f6358d6ad8a8c0b40ba61
CRs-Fixed: 2274105
diff --git a/umac/scan/core/src/wlan_scan_cache_db.c b/umac/scan/core/src/wlan_scan_cache_db.c
index 9e1f4d6..1f70382 100644
--- a/umac/scan/core/src/wlan_scan_cache_db.c
+++ b/umac/scan/core/src/wlan_scan_cache_db.c
@@ -50,6 +50,7 @@
 #include "wlan_scan_cache_db_i.h"
 #include "wlan_reg_services_api.h"
 #include "wlan_reg_ucfg_api.h"
+#include <wlan_dfs_utils_api.h>
 
 /**
  * scm_del_scan_node() - API to remove scan node from the list
@@ -747,6 +748,11 @@
 		goto free_nbuf;
 	}
 
+	if (bcn->frm_type == MGMT_SUBTYPE_BEACON &&
+	    utils_is_dfs_ch(pdev, bcn->rx_data->channel)) {
+		util_scan_add_hidden_ssid(pdev, bcn->buf);
+	}
+
 	scan_list =
 		 util_scan_unpack_beacon_frame(pdev, qdf_nbuf_data(bcn->buf),
 			qdf_nbuf_len(bcn->buf), bcn->frm_type,
diff --git a/umac/scan/core/src/wlan_scan_main.h b/umac/scan/core/src/wlan_scan_main.h
index d138799..4a29335 100644
--- a/umac/scan/core/src/wlan_scan_main.h
+++ b/umac/scan/core/src/wlan_scan_main.h
@@ -200,11 +200,15 @@
  * @wide_band_scan: wide band scan capability
  * @last_scan_time: time of last scan start on this pdev
  * @custom_chan_list: scan only these channels
+ * @conf_bssid: configured bssid of the hidden AP
+ * @conf_ssid: configured desired ssid
  */
 struct pdev_scan_info {
 	bool wide_band_scan;
 	qdf_time_t last_scan_time;
 	struct chan_list custom_chan_list;
+	uint8_t conf_bssid[QDF_MAC_ADDR_SIZE];
+	struct wlan_ssid conf_ssid;
 };
 
 /**
diff --git a/umac/scan/dispatcher/inc/wlan_scan_ucfg_api.h b/umac/scan/dispatcher/inc/wlan_scan_ucfg_api.h
index 79be46a..2a9b550 100644
--- a/umac/scan/dispatcher/inc/wlan_scan_ucfg_api.h
+++ b/umac/scan/dispatcher/inc/wlan_scan_ucfg_api.h
@@ -252,7 +252,29 @@
  */
 QDF_STATUS ucfg_scan_set_custom_scan_chan_list(
 		struct wlan_objmgr_pdev *pdev, struct chan_list *chan_list);
-
+/**
+ * ucfg_scan_set_ssid_bssid_hidden_ssid_beacon() - API to configure
+ * ssid, bssid of hidden beacon
+ * @pdev: psoc on which ssid bssid need to configure
+ * @bssid: bssid of the hidden AP
+ * @ssid: desired ssid
+ *
+ * Return: QDF_STATUS.
+ */
+#ifdef WLAN_DFS_CHAN_HIDDEN_SSID
+QDF_STATUS
+ucfg_scan_config_hidden_ssid_for_bssid(struct wlan_objmgr_pdev *pdev,
+				       uint8_t *bssid,
+				       struct wlan_ssid *ssid);
+#else
+static inline QDF_STATUS
+ucfg_scan_config_hidden_ssid_for_bssid(struct wlan_objmgr_pdev *pdev,
+				       uint8_t *bssid,
+				       struct wlan_ssid *ssid)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif /* WLAN_DFS_CHAN_HIDDEN_SSID */
 /**
  * ucfg_scan_cancel() - Public API to stop a scan
  * @req: stop scan request params
diff --git a/umac/scan/dispatcher/inc/wlan_scan_utils_api.h b/umac/scan/dispatcher/inc/wlan_scan_utils_api.h
index 0dbe8d3..d682466 100644
--- a/umac/scan/dispatcher/inc/wlan_scan_utils_api.h
+++ b/umac/scan/dispatcher/inc/wlan_scan_utils_api.h
@@ -63,6 +63,24 @@
 	struct mgmt_rx_event_params *rx_param);
 
 /**
+ * util_scan_add_hidden_ssid() - func to add hidden ssid
+ * @pdev: pdev pointer
+ * @frame: beacon buf
+ *
+ * Return:
+ */
+#ifdef WLAN_DFS_CHAN_HIDDEN_SSID
+QDF_STATUS
+util_scan_add_hidden_ssid(struct wlan_objmgr_pdev *pdev, qdf_nbuf_t bcnbuf);
+#else
+static inline QDF_STATUS
+util_scan_add_hidden_ssid(struct wlan_objmgr_pdev *pdev, qdf_nbuf_t bcnbuf)
+{
+	return  QDF_STATUS_SUCCESS;
+}
+#endif /* WLAN_DFS_CHAN_HIDDEN_SSID */
+
+/**
  * util_scan_get_ev_type_name() - converts enum event to printable string
  * @event:      event of type scan_event_type
  *
diff --git a/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c b/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c
index fb7b45b..87f66e7 100644
--- a/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c
+++ b/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c
@@ -1041,6 +1041,34 @@
 	return scan_obj->pdev_info[pdev_id].wide_band_scan;
 }
 
+#ifdef WLAN_DFS_CHAN_HIDDEN_SSID
+QDF_STATUS
+ucfg_scan_config_hidden_ssid_for_bssid(struct wlan_objmgr_pdev *pdev,
+				       uint8_t *bssid, struct wlan_ssid *ssid)
+{
+	uint8_t pdev_id;
+	struct wlan_scan_obj *scan_obj;
+
+	if (!pdev) {
+		scm_warn("null vdev");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+	pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
+	scan_obj = wlan_pdev_get_scan_obj(pdev);
+
+	scm_debug("Configure bsssid:%pM ssid:%.*s",
+		  bssid, ssid->length, ssid->ssid);
+	qdf_mem_copy(scan_obj->pdev_info[pdev_id].conf_bssid,
+		     bssid, QDF_MAC_ADDR_SIZE);
+	scan_obj->pdev_info[pdev_id].conf_ssid.length = ssid->length;
+	qdf_mem_copy(scan_obj->pdev_info[pdev_id].conf_ssid.ssid,
+		     ssid->ssid,
+		     scan_obj->pdev_info[pdev_id].conf_ssid.length);
+
+	return QDF_STATUS_SUCCESS;
+}
+#endif /* WLAN_DFS_CHAN_HIDDEN_SSID */
+
 QDF_STATUS
 ucfg_scan_cancel(struct scan_cancel_request *req)
 {
diff --git a/umac/scan/dispatcher/src/wlan_scan_utils_api.c b/umac/scan/dispatcher/src/wlan_scan_utils_api.c
index bad4ec4..f89c61a 100644
--- a/umac/scan/dispatcher/src/wlan_scan_utils_api.c
+++ b/umac/scan/dispatcher/src/wlan_scan_utils_api.c
@@ -826,6 +826,141 @@
 	return 1;
 }
 
+#ifdef WLAN_DFS_CHAN_HIDDEN_SSID
+QDF_STATUS
+util_scan_add_hidden_ssid(struct wlan_objmgr_pdev *pdev, qdf_nbuf_t bcnbuf)
+{
+	struct wlan_frame_hdr *hdr;
+	struct wlan_bcn_frame *bcn;
+	struct wlan_scan_obj *scan_obj;
+	struct wlan_ssid *conf_ssid;
+	struct  ie_header *ie;
+	uint32_t frame_len = qdf_nbuf_len(bcnbuf);
+	uint16_t bcn_ie_offset, ssid_ie_start_offset, ssid_ie_end_offset;
+	uint16_t tmplen, ie_length;
+	uint8_t *pbeacon, *tmp;
+	bool     set_ssid_flag = false;
+	struct ie_ssid *ssid;
+	uint8_t pdev_id;
+
+	if (!pdev) {
+		scm_warn("pdev: 0x%pK is NULL", pdev);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+	pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
+	scan_obj = wlan_pdev_get_scan_obj(pdev);
+
+	conf_ssid = &scan_obj->pdev_info[pdev_id].conf_ssid;
+
+	hdr = (struct wlan_frame_hdr *)qdf_nbuf_data(bcnbuf);
+
+	/* received bssid does not match configured bssid */
+	if (qdf_mem_cmp(hdr->i_addr3, scan_obj->pdev_info[pdev_id].conf_bssid,
+			QDF_MAC_ADDR_SIZE) ||
+			conf_ssid->length == 0) {
+		return QDF_STATUS_SUCCESS;
+	}
+
+	bcn = (struct wlan_bcn_frame *)(qdf_nbuf_data(bcnbuf) + sizeof(*hdr));
+	pbeacon = (uint8_t *)bcn;
+
+	ie = (struct ie_header *)(pbeacon +
+				  offsetof(struct wlan_bcn_frame, ie));
+
+	bcn_ie_offset = offsetof(struct wlan_bcn_frame, ie);
+	ie_length = (uint16_t)(frame_len - sizeof(*hdr) -
+			       bcn_ie_offset);
+
+	while (ie_length >=  sizeof(struct ie_header)) {
+		ie_length -= sizeof(struct ie_header);
+
+		bcn_ie_offset += sizeof(struct ie_header);
+
+		if (ie_length < ie->ie_len) {
+			scm_debug("Incomplete corrupted IE:%x", ie->ie_id);
+			return QDF_STATUS_E_INVAL;
+		}
+		if (ie->ie_id == WLAN_ELEMID_SSID) {
+			if (ie->ie_len > (sizeof(struct ie_ssid) -
+						 sizeof(struct ie_header))) {
+				return QDF_STATUS_E_INVAL;
+			}
+			ssid = (struct ie_ssid *)ie;
+			if (util_scan_is_hidden_ssid(ssid)) {
+				set_ssid_flag  = true;
+				ssid_ie_start_offset = bcn_ie_offset -
+					sizeof(struct ie_header);
+				ssid_ie_end_offset = bcn_ie_offset +
+					ie->ie_len;
+			}
+		}
+		if (ie->ie_len == 0) {
+			ie += 1;    /* next IE */
+			continue;
+		}
+		if (ie->ie_id == WLAN_ELEMID_VENDOR &&
+		    is_wps_oui((uint8_t *)ie)) {
+			set_ssid_flag = false;
+			break;
+		}
+		/* Consume info element */
+		ie_length -=  ie->ie_len;
+		/* Go to next IE */
+		ie = (struct ie_header *)(((uint8_t *)ie) +
+				sizeof(struct ie_header) +
+				ie->ie_len);
+	}
+
+	if (set_ssid_flag) {
+		/* Hidden SSID if the Length is 0 */
+		if (!ssid->ssid_len) {
+			/* increase the taillength by length of ssid */
+			if (qdf_nbuf_put_tail(bcnbuf,
+					      conf_ssid->length) == NULL) {
+				scm_debug("No enough tailroom");
+				return  QDF_STATUS_E_NOMEM;
+			}
+			/* length of the buffer to be copied */
+			tmplen = frame_len -
+				sizeof(*hdr) - ssid_ie_end_offset;
+			/*
+			 * tmp memory to copy the beacon info
+			 * after ssid ie.
+			 */
+			tmp = qdf_mem_malloc(tmplen * sizeof(u_int8_t));
+			if (!tmp) {
+				scm_debug("tmp memory alloc failed");
+				return  QDF_STATUS_E_NOMEM;
+			}
+			/* Copy beacon data after ssid ie to tmp */
+			qdf_nbuf_copy_bits(bcnbuf, (sizeof(*hdr) +
+					   ssid_ie_end_offset), tmplen, tmp);
+			/* Add ssid length */
+			*(pbeacon + (ssid_ie_start_offset + 1))
+				= conf_ssid->length;
+			/* Insert the  SSID string */
+			qdf_mem_copy((pbeacon + ssid_ie_end_offset),
+				     conf_ssid->ssid, conf_ssid->length);
+			/* Copy rest of the beacon data */
+			qdf_mem_copy((pbeacon + ssid_ie_end_offset +
+				      conf_ssid->length), tmp, tmplen);
+			qdf_mem_free(tmp);
+
+			/* Hidden ssid with all 0's */
+		} else if (ssid->ssid_len == conf_ssid->length) {
+			/* Insert the  SSID string */
+			qdf_mem_copy((pbeacon + ssid_ie_start_offset +
+				      sizeof(struct ie_header)),
+				      conf_ssid->ssid, conf_ssid->length);
+		} else {
+			scm_debug("mismatch in hidden ssid length");
+			return QDF_STATUS_E_INVAL;
+		}
+	}
+	return QDF_STATUS_SUCCESS;
+}
+#endif /* WLAN_DFS_CHAN_HIDDEN_SSID */
+
 qdf_list_t *
 util_scan_unpack_beacon_frame(struct wlan_objmgr_pdev *pdev, uint8_t *frame,
 	qdf_size_t frame_len, uint32_t frm_subtype,