qcacmn: RNR IE parsing support

Add RNR IE parsing support on scan module.

Change-Id: I7a0685f16a61a45910212b2d8fe5cbc12b5bd92e
CRs-Fixed: 2557271
diff --git a/umac/scan/dispatcher/src/wlan_scan_utils_api.c b/umac/scan/dispatcher/src/wlan_scan_utils_api.c
index e7a8ce1..034237d 100644
--- a/umac/scan/dispatcher/src/wlan_scan_utils_api.c
+++ b/umac/scan/dispatcher/src/wlan_scan_utils_api.c
@@ -28,6 +28,9 @@
 #include <wlan_reg_services_api.h>
 
 #define MAX_IE_LEN 1024
+#define SHORT_SSID_LEN 4
+#define NEIGHBOR_AP_LEN 1
+#define BSS_PARAMS_LEN 1
 
 const char*
 util_scan_get_ev_type_name(enum scan_event_type type)
@@ -560,6 +563,116 @@
 }
 
 static QDF_STATUS
+util_scan_update_rnr(struct rnr_bss_info *rnr,
+		     struct neighbor_ap_info_field *ap_info,
+		     uint8_t **data)
+{
+	uint16_t fieldtype;
+	uint8_t *tmp = *data;
+
+	fieldtype = ap_info->tbtt_header.tbbt_info_fieldtype;
+
+	switch (fieldtype) {
+	case TBTT_NEIGHBOR_AP_OFFSET_ONLY:
+		/* Dont store it skip*/
+		*data = tmp + NEIGHBOR_AP_LEN;
+		break;
+
+	case TBTT_NEIGHBOR_AP_BSS_PARAM:
+		/* Dont store it skip*/
+		break;
+
+	case TBTT_NEIGHBOR_AP_SHORTSSID:
+		rnr->channel_number = ap_info->channel_number;
+		rnr->operating_class = ap_info->operting_class;
+		qdf_mem_copy(&rnr->short_ssid, &tmp[1], SHORT_SSID_LEN);
+		*data = tmp + NEIGHBOR_AP_LEN + SHORT_SSID_LEN;
+		break;
+
+	case TBTT_NEIGHBOR_AP_S_SSID_BSS_PARAM:
+		rnr->channel_number = ap_info->channel_number;
+		rnr->operating_class = ap_info->operting_class;
+		qdf_mem_copy(&rnr->short_ssid, &tmp[1], SHORT_SSID_LEN);
+		rnr->bss_params = tmp[5];
+		*data = tmp + NEIGHBOR_AP_LEN + SHORT_SSID_LEN + BSS_PARAMS_LEN;
+		break;
+
+	case TBTT_NEIGHBOR_AP_BSSID:
+		rnr->channel_number = ap_info->channel_number;
+		rnr->operating_class = ap_info->operting_class;
+		qdf_mem_copy(&rnr->bssid, &tmp[1], QDF_MAC_ADDR_SIZE);
+		*data = tmp + NEIGHBOR_AP_LEN + QDF_MAC_ADDR_SIZE;
+		break;
+
+	case TBTT_NEIGHBOR_AP_BSSID_BSS_PARAM:
+		rnr->channel_number = ap_info->channel_number;
+		rnr->operating_class = ap_info->operting_class;
+		qdf_mem_copy(&rnr->bssid, &tmp[1], QDF_MAC_ADDR_SIZE);
+		rnr->bss_params = tmp[7];
+		*data = tmp + NEIGHBOR_AP_LEN + QDF_MAC_ADDR_SIZE
+			+ BSS_PARAMS_LEN;
+		break;
+
+	case TBTT_NEIGHBOR_AP_BSSSID_S_SSID:
+		rnr->channel_number = ap_info->channel_number;
+		rnr->operating_class = ap_info->operting_class;
+		qdf_mem_copy(&rnr->bssid, &tmp[1], QDF_MAC_ADDR_SIZE);
+		qdf_mem_copy(&rnr->short_ssid, &tmp[7], SHORT_SSID_LEN);
+		*data = tmp + NEIGHBOR_AP_LEN + QDF_MAC_ADDR_SIZE
+			+ SHORT_SSID_LEN;
+		break;
+
+	case TBTT_NEIGHBOR_AP_BSSID_S_SSID_BSS_PARAM:
+		rnr->channel_number = ap_info->channel_number;
+		rnr->operating_class = ap_info->operting_class;
+		qdf_mem_copy(&rnr->bssid, &tmp[1], QDF_MAC_ADDR_SIZE);
+		qdf_mem_copy(&rnr->short_ssid, &tmp[7], SHORT_SSID_LEN);
+		rnr->bss_params = tmp[11];
+		*data = tmp + NEIGHBOR_AP_LEN + QDF_MAC_ADDR_SIZE
+			+ SHORT_SSID_LEN + BSS_PARAMS_LEN;
+		break;
+
+	default:
+		scm_debug("Wrong fieldtype");
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS
+util_scan_parse_rnr_ie(struct scan_cache_entry *scan_entry,
+		       struct ie_header *ie)
+{
+	uint32_t rnr_ie_len;
+	uint16_t tbtt_count, tbtt_length, i, fieldtype;
+	uint8_t *data;
+	struct neighbor_ap_info_field *neighbor_ap_info;
+
+	rnr_ie_len = ie->ie_len;
+	data = (uint8_t *)ie + sizeof(struct ie_header);
+
+	while (data < (uint8_t *)ie + rnr_ie_len + 2) {
+		neighbor_ap_info = (struct neighbor_ap_info_field *)data;
+		tbtt_count = neighbor_ap_info->tbtt_header.tbtt_info_count;
+		tbtt_length = neighbor_ap_info->tbtt_header.tbtt_info_length;
+		fieldtype = neighbor_ap_info->tbtt_header.tbbt_info_fieldtype;
+		scm_debug("channel number %d, op class %d",
+			  neighbor_ap_info->channel_number,
+			  neighbor_ap_info->operting_class);
+		scm_debug("tbtt_count %d, tbtt_length %d, fieldtype %d",
+			  tbtt_count, tbtt_length, fieldtype);
+		for (i = 0; i < tbtt_count && i < MAX_RNR_BSS; i++) {
+			data = data + sizeof(struct neighbor_ap_info_field);
+			util_scan_update_rnr(&scan_entry->rnr.bss_info[i],
+					     neighbor_ap_info,
+					     &data);
+		}
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS
 util_scan_parse_extn_ie(struct scan_cache_entry *scan_params,
 	struct ie_header *ie)
 {
@@ -877,6 +990,14 @@
 			if (QDF_IS_STATUS_ERROR(status))
 				goto err_status;
 			break;
+		case WLAN_ELEMID_REDUCED_NEIGHBOR_REPORT:
+			if (ie->ie_len < WLAN_RNR_IE_MIN_LEN)
+				goto err;
+			scan_params->ie_list.rnrie = (uint8_t *)ie;
+			status = util_scan_parse_rnr_ie(scan_params, ie);
+			if (QDF_IS_STATUS_ERROR(status))
+				goto err_status;
+			break;
 		default:
 			break;
 		}