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/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,