Просмотр исходного кода

qcacmn: Spectral changes to support big-endian Host platforms

WLAN chip components are little-endian based. When such a chip is attached
to a big-endian Host platform, there will be a mismatch in the order of
bytes for the data that is transferred between the Host and target.

Spectral HW module transfers the Spectral report directly to the Host DDR.
This transfer doesn't go through any byte-order conversion at the HW side.
So, to avoid invalid reads at the Host side on a big-endian platform,
convert the Spectral report to the Host byte-order before using it.

Change-Id: I742537f3a95ffca2e12b83535e83e2870ad06b10
CRs-Fixed: 2838371
Shiva Krishna Pittala 4 лет назад
Родитель
Сommit
8f049e633b

+ 41 - 21
target_if/spectral/target_if_spectral.c

@@ -1718,6 +1718,44 @@ target_if_init_spectral_ops_gen2(void)
 	p_sops->spectral_process_phyerr = target_if_process_phyerr_gen2;
 }
 
+#ifdef BIG_ENDIAN_HOST
+/**
+ * target_if_spectral_init_byte_swap_funcs_gen3() - Initialize byte-swap
+ * operations for Spectral chipset generation 3.
+ *
+ * @p_sops: Spectral function pointer table
+ *
+ * Return: None
+ */
+static void
+target_if_spectral_init_byte_swap_funcs_gen3(
+	struct target_if_spectral_ops *p_sops)
+{
+	qdf_assert_always(p_sops);
+
+	/* None of current Gen3 chipsets support byte-swap inside the target.
+	 * so, Host would have to implement the byte-swap for these chipsets.
+	 *
+	 * If a chipset supports byte-swap inside the target itself in future,
+	 * then, for that chipset, initialize these function pointers with
+	 * NULL based on the capability advertisement.
+	 */
+	p_sops->byte_swap_headers = target_if_byte_swap_spectral_headers_gen3;
+	p_sops->byte_swap_fft_bins = target_if_byte_swap_spectral_fft_bins_gen3;
+}
+#else
+static void
+target_if_spectral_init_byte_swap_funcs_gen3(
+	struct target_if_spectral_ops *p_sops)
+{
+	qdf_assert_always(p_sops);
+
+	/* Byte-swap is not required for little-endian Hosts */
+	p_sops->byte_swap_headers = NULL;
+	p_sops->byte_swap_fft_bins = NULL;
+}
+#endif /* BIG_ENDIAN_HOST */
+
 /**
  * target_if_init_spectral_ops_gen3() - Initialize Spectral target_if internal
  * operations specific to Spectral chipset generation 3.
@@ -1733,7 +1771,8 @@ target_if_init_spectral_ops_gen3(void)
 
 	p_sops->process_spectral_report =
 			target_if_spectral_process_report_gen3;
-	return;
+
+	target_if_spectral_init_byte_swap_funcs_gen3(p_sops);
 }
 
 /**
@@ -1973,26 +2012,7 @@ target_if_spectral_register_funcs(struct target_if_spectral *spectral,
 	struct target_if_spectral_ops *p_sops =
 		GET_TARGET_IF_SPECTRAL_OPS(spectral);
 
-	p_sops->get_tsf64 = p->get_tsf64;
-	p_sops->get_capability = p->get_capability;
-	p_sops->set_rxfilter = p->set_rxfilter;
-	p_sops->get_rxfilter = p->get_rxfilter;
-	p_sops->is_spectral_enabled = p->is_spectral_enabled;
-	p_sops->is_spectral_active = p->is_spectral_active;
-	p_sops->start_spectral_scan = p->start_spectral_scan;
-	p_sops->stop_spectral_scan = p->stop_spectral_scan;
-	p_sops->get_extension_channel = p->get_extension_channel;
-	p_sops->get_ctl_noisefloor = p->get_ctl_noisefloor;
-	p_sops->get_ext_noisefloor = p->get_ext_noisefloor;
-	p_sops->configure_spectral = p->configure_spectral;
-	p_sops->get_spectral_config = p->get_spectral_config;
-	p_sops->get_ent_spectral_mask = p->get_ent_spectral_mask;
-	p_sops->get_mac_address = p->get_mac_address;
-	p_sops->get_current_channel = p->get_current_channel;
-	p_sops->reset_hw = p->reset_hw;
-	p_sops->get_chain_noise_floor = p->get_chain_noise_floor;
-	p_sops->spectral_process_phyerr = p->spectral_process_phyerr;
-	p_sops->process_spectral_report = p->process_spectral_report;
+	*p_sops = *p;
 }
 
 /**

+ 61 - 28
target_if/spectral/target_if_spectral.h

@@ -263,6 +263,12 @@ struct spectral_phyerr_fft_gen2 {
 #define SSCAN_SUMMARY_REPORT_HDR_B_GAINCHANGE_SIZE_GEN3_V1      (1)
 #define SSCAN_SUMMARY_REPORT_HDR_C_GAINCHANGE_POS_GEN3_V2       (16)
 #define SSCAN_SUMMARY_REPORT_HDR_C_GAINCHANGE_SIZE_GEN3_V2      (1)
+#define SPECTRAL_REPORT_LTS_HDR_LENGTH_POS_GEN3                 (0)
+#define SPECTRAL_REPORT_LTS_HDR_LENGTH_SIZE_GEN3                (16)
+#define SPECTRAL_REPORT_LTS_TAG_POS_GEN3                        (16)
+#define SPECTRAL_REPORT_LTS_TAG_SIZE_GEN3                       (8)
+#define SPECTRAL_REPORT_LTS_SIGNATURE_POS_GEN3                  (24)
+#define SPECTRAL_REPORT_LTS_SIGNATURE_SIZE_GEN3                 (8)
 
 #define SPECTRAL_PHYERR_SIGNATURE_GEN3                          (0xFA)
 #define TLV_TAG_SPECTRAL_SUMMARY_REPORT_GEN3                    (0x02)
@@ -276,10 +282,8 @@ struct spectral_phyerr_fft_gen2 {
 #define NUM_PADDING_BYTES_SSCAN_SUMARY_REPORT_GEN3_V1      (0)
 #define NUM_PADDING_BYTES_SSCAN_SUMARY_REPORT_GEN3_V2      (16)
 
-#define PHYERR_HDR_SIG_POS    \
-	(offsetof(struct spectral_phyerr_fft_report_gen3, fft_hdr_sig))
-#define PHYERR_HDR_TAG_POS    \
-	(offsetof(struct spectral_phyerr_fft_report_gen3, fft_hdr_tag))
+#define SPECTRAL_PHYERR_HDR_LTS_POS \
+	(offsetof(struct spectral_phyerr_fft_report_gen3, fft_hdr_lts))
 #define SPECTRAL_FFT_BINS_POS \
 	(offsetof(struct spectral_phyerr_fft_report_gen3, buf))
 
@@ -334,9 +338,7 @@ struct spectral_search_fft_info_gen3 {
 /**
  * struct spectral_phyerr_sfftreport_gen3 - fft info in phyerr event
  * @fft_timestamp:  Timestamp at which fft report was generated
- * @fft_hdr_sig:    signature
- * @fft_hdr_tag:    tag
- * @fft_hdr_length: length
+ * @fft_hdr_lts:    length, tag, signature fields
  * @hdr_a:          Header[0:31]
  * @hdr_b:          Header[32:63]
  * @hdr_c:          Header[64:95]
@@ -345,15 +347,7 @@ struct spectral_search_fft_info_gen3 {
  */
 struct spectral_phyerr_fft_report_gen3 {
 	uint32_t fft_timestamp;
-#ifdef BIG_ENDIAN_HOST
-	uint8_t  fft_hdr_sig;
-	uint8_t  fft_hdr_tag;
-	uint16_t fft_hdr_length;
-#else
-	uint16_t fft_hdr_length;
-	uint8_t  fft_hdr_tag;
-	uint8_t  fft_hdr_sig;
-#endif /* BIG_ENDIAN_HOST */
+	uint32_t fft_hdr_lts;
 	uint32_t hdr_a;
 	uint32_t hdr_b;
 	uint32_t hdr_c;
@@ -384,9 +378,7 @@ struct sscan_report_fields_gen3 {
  * struct spectral_sscan_summary_report_gen3 - Spectral summary report
  * event
  * @sscan_timestamp:  Timestamp at which fft report was generated
- * @sscan_hdr_sig:    signature
- * @sscan_hdr_tag:    tag
- * @sscan_hdr_length: length
+ * @sscan_hdr_lts:    length, tag, signature fields
  * @hdr_a:          Header[0:31]
  * @resv:           Header[32:63]
  * @hdr_b:          Header[64:95]
@@ -394,15 +386,7 @@ struct sscan_report_fields_gen3 {
  */
 struct spectral_sscan_summary_report_gen3 {
 	u_int32_t sscan_timestamp;
-#ifdef BIG_ENDIAN_HOST
-	u_int8_t  sscan_hdr_sig;
-	u_int8_t  sscan_hdr_tag;
-	u_int16_t sscan_hdr_length;
-#else
-	u_int16_t sscan_hdr_length;
-	u_int8_t  sscan_hdr_tag;
-	u_int8_t  sscan_hdr_sig;
-#endif /* BIG_ENDIAN_HOST */
+	u_int32_t sscan_hdr_lts;
 	u_int32_t hdr_a;
 	u_int32_t res1;
 	u_int32_t hdr_b;
@@ -654,6 +638,8 @@ struct target_if_spectral_rfqual_info {
  * @get_chain_noise_floor:   Get Channel noise floor
  * @spectral_process_phyerr: Process phyerr event
  * @process_spectral_report: Process spectral report
+ * @byte_swap_headers:       Apply byte-swap on report headers
+ * @byte_swap_fft_bins:      Apply byte-swap on FFT bins
  */
 struct target_if_spectral_ops {
 	uint64_t (*get_tsf64)(void *arg);
@@ -696,6 +682,12 @@ struct target_if_spectral_ops {
 			struct target_if_spectral_acs_stats *acs_stats);
 	int (*process_spectral_report)(struct wlan_objmgr_pdev *pdev,
 				       void *payload);
+	QDF_STATUS (*byte_swap_headers)(
+		struct target_if_spectral *spectral,
+		void *data);
+	QDF_STATUS (*byte_swap_fft_bins)(
+		struct spectral_fft_bin_len_adj_swar *swar,
+		void *bin_pwr_data, size_t num_fftbins);
 };
 
 /**
@@ -2324,6 +2316,47 @@ target_if_spectral_is_finite_scan(struct target_if_spectral *spectral,
 				  enum spectral_scan_mode smode,
 				  bool *finite_spectral_scan);
 
+#ifdef BIG_ENDIAN_HOST
+/**
+ * target_if_byte_swap_spectral_headers_gen3() - Apply byte-swap on headers
+ * @spectral: Pointer to Spectral target_if internal private data
+ * @data: Pointer to the start of Spectral Scan Summary report
+ *
+ * This API is only required for Big-endian Host platforms.
+ * It applies 32-bit byte-swap on Spectral Scan Summary and Search FFT reports
+ * and copies them back to the source location.
+ * Padding bytes that lie between the reports won't be touched.
+ *
+ * Return: QDF_STATUS_SUCCESS in case of success, else QDF_STATUS_E_FAILURE
+ */
+QDF_STATUS target_if_byte_swap_spectral_headers_gen3(
+	 struct target_if_spectral *spectral,
+	 void *data);
+
+/**
+ * target_if_byte_swap_spectral_fft_bins_gen3() - Apply byte-swap on FFT bins
+ * @spectral: Pointer to Spectral FFT bin length adjustment WAR
+ * @bin_pwr_data: Pointer to the start of FFT bins
+ * @pwr_count: Number of FFT bins
+ *
+ * This API is only required for Big-endian Host platforms.
+ * It applies pack-mode-aware byte-swap on the FFT bins as below:
+ *   1. pack-mode 0 (i.e., 1 FFT bin per DWORD):
+ *        Reads the least significant 2 bytes of each DWORD, applies 16-bit
+ *        byte-swap on that value, and copies it back to the source location.
+ *   2. pack-mode 1 (i.e., 2 FFT bins per DWORD):
+ *        Reads each FFT bin, applies 16-bit byte-swap on that value,
+ *        and copies it back to the source location.
+ *   3. pack-mode 2 (4 FFT bins per DWORD):
+ *        Nothing
+ *
+ * Return: QDF_STATUS_SUCCESS in case of success, else QDF_STATUS_E_FAILURE
+ */
+QDF_STATUS target_if_byte_swap_spectral_fft_bins_gen3(
+	struct spectral_fft_bin_len_adj_swar *swar,
+	void *bin_pwr_data, size_t pwr_count);
+#endif /* BIG_ENDIAN_HOST */
+
 #ifdef WIN32
 #pragma pack(pop, target_if_spectral)
 #endif

+ 162 - 19
target_if/spectral/target_if_spectral_phyerr.c

@@ -1387,15 +1387,30 @@ target_if_dump_fft_report_gen3(struct target_if_spectral *spectral,
 			struct spectral_phyerr_fft_report_gen3 *p_fft_report,
 			struct spectral_search_fft_info_gen3 *p_sfft)
 {
-	size_t fft_hdr_length = (p_fft_report->fft_hdr_length * 4);
-	size_t report_len = (fft_hdr_length + 8);
+	size_t fft_hdr_length;
+	size_t report_len;
 	size_t fft_bin_len;
 	size_t fft_bin_count;
 	size_t fft_bin_size;
 	size_t fft_bin_len_inband_tfer = 0;
 	uint8_t *fft_bin_buf = NULL;
 	size_t fft_bin_buf_size;
+	uint8_t tag, signature;
 
+	fft_hdr_length = get_bitfield(
+				p_fft_report->fft_hdr_lts,
+				SPECTRAL_REPORT_LTS_HDR_LENGTH_SIZE_GEN3,
+				SPECTRAL_REPORT_LTS_HDR_LENGTH_POS_GEN3) * 4;
+
+	tag = get_bitfield(p_fft_report->fft_hdr_lts,
+			   SPECTRAL_REPORT_LTS_TAG_SIZE_GEN3,
+			   SPECTRAL_REPORT_LTS_TAG_POS_GEN3);
+
+	signature = get_bitfield(p_fft_report->fft_hdr_lts,
+				 SPECTRAL_REPORT_LTS_SIGNATURE_SIZE_GEN3,
+				 SPECTRAL_REPORT_LTS_SIGNATURE_POS_GEN3);
+
+	report_len = (fft_hdr_length + 8);
 	fft_bin_len = fft_hdr_length - spectral->rparams.fft_report_hdr_len;
 	fft_bin_count = target_if_spectral_get_bin_count_after_len_adj(
 			fft_bin_len,
@@ -1408,10 +1423,10 @@ target_if_dump_fft_report_gen3(struct target_if_spectral *spectral,
 
 	spectral_debug("Spectral FFT Report");
 	spectral_debug("fft_timestamp = 0x%x", p_fft_report->fft_timestamp);
-	spectral_debug("fft_hdr_length = %u(32 bit words)",
-		       p_fft_report->fft_hdr_length);
-	spectral_debug("fft_hdr_tag = 0x%x", p_fft_report->fft_hdr_tag);
-	spectral_debug("fft_hdr_sig = 0x%x", p_fft_report->fft_hdr_sig);
+	spectral_debug("fft_hdr_length = %zu(32 bit words)",
+		       fft_hdr_length >> 2);
+	spectral_debug("fft_hdr_tag = 0x%x", tag);
+	spectral_debug("fft_hdr_sig = 0x%x", signature);
 
 	spectral_debug("Length field in search fft report is %zu(0x%zx) bytes",
 		       fft_hdr_length, fft_hdr_length);
@@ -1450,7 +1465,6 @@ target_if_dump_fft_report_gen3(struct target_if_spectral *spectral,
 	if (fft_bin_count > 0) {
 		int idx;
 
-		spectral_debug("FFT bins:");
 		if (spectral->len_adj_swar.fftbin_size_war ==
 				SPECTRAL_FFTBIN_SIZE_WAR_4BYTE_TO_1BYTE) {
 			uint32_t *binptr_32 = (uint32_t *)&p_fft_report->buf;
@@ -1503,6 +1517,7 @@ target_if_dump_fft_report_gen3(struct target_if_spectral *spectral,
 		}
 
 		spectral_debug("FFT bin buffer size = %zu", fft_bin_buf_size);
+		spectral_debug("FFT bins:");
 		target_if_spectral_hexdump(fft_bin_buf, fft_bin_buf_size);
 		if ((spectral->len_adj_swar.fftbin_size_war !=
 				SPECTRAL_FFTBIN_SIZE_NO_WAR) && fft_bin_buf)
@@ -1668,13 +1683,21 @@ target_if_verify_sig_and_tag_gen3(struct target_if_spectral *spectral,
 {
 	uint8_t tag = 0;
 	uint8_t signature = 0;
+	uint32_t lts;
 
+	lts = *((uint32_t *)(data + SPECTRAL_PHYERR_HDR_LTS_POS));
 	/* Peek into the data to figure out whether
 	 *      1) Signature matches the expected value
 	 *      2) What is inside the package (TAG ID is used for finding this)
 	 */
-	tag = *(data + PHYERR_HDR_TAG_POS);
-	signature = *(data + PHYERR_HDR_SIG_POS);
+	tag = get_bitfield(lts,
+			   SPECTRAL_REPORT_LTS_TAG_SIZE_GEN3,
+			   SPECTRAL_REPORT_LTS_TAG_POS_GEN3);
+
+	signature = get_bitfield(lts,
+				 SPECTRAL_REPORT_LTS_SIGNATURE_SIZE_GEN3,
+				 SPECTRAL_REPORT_LTS_SIGNATURE_POS_GEN3);
+
 
 	if (signature != SPECTRAL_PHYERR_SIGNATURE_GEN3) {
 		spectral->diag_stats.spectral_mismatch++;
@@ -1838,6 +1861,77 @@ target_if_spectral_get_adjusted_timestamp(struct spectral_timestamp_war *twar,
 	return raw_timestamp + twar->timestamp_war_offset[smode];
 }
 
+#ifdef BIG_ENDIAN_HOST
+QDF_STATUS target_if_byte_swap_spectral_headers_gen3(
+	 struct target_if_spectral *spectral,
+	 void *data)
+{
+	int i;
+	uint32_t *ptr32;
+	size_t words32;
+
+	qdf_assert_always(data);
+	qdf_assert_always(spectral);
+
+	ptr32 = (uint32_t *)data;
+
+	/* Summary Report */
+	words32 = sizeof(struct spectral_sscan_summary_report_gen3) >> 2;
+	for (i = 0; i < words32; ++i) {
+		*ptr32 = qdf_le32_to_cpu(*ptr32);
+		++ptr32;
+	}
+
+	/* No need to swap the padding bytes */
+	ptr32 += (spectral->rparams.ssumaary_padding_bytes >> 2);
+
+	/* Search FFT Report */
+	words32 = sizeof(struct spectral_phyerr_fft_report_gen3) >> 2;
+	for (i = 0; i < words32; ++i) {
+		*ptr32 = qdf_le32_to_cpu(*ptr32);
+		++ptr32;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS target_if_byte_swap_spectral_fft_bins_gen3(
+	struct spectral_fft_bin_len_adj_swar *swar,
+	void *bin_pwr_data, size_t num_fftbins)
+{
+	int i;
+	uint16_t *binptr_16;
+	uint32_t *binptr_32;
+
+	qdf_assert_always(bin_pwr_data);
+	qdf_assert_always(swar);
+
+	if (swar->fftbin_size_war ==
+			SPECTRAL_FFTBIN_SIZE_WAR_4BYTE_TO_1BYTE) {
+		binptr_32 = (uint32_t *)bin_pwr_data;
+
+		for (i = 0; i < num_fftbins; i++) {
+			/* Get the useful first 2 bytes of the DWORD */
+			binptr_16 = ((uint16_t *)binptr_32);
+			/* Byteswap and copy it back */
+			*binptr_16 = qdf_le16_to_cpu(*binptr_16);
+			++binptr_32; /* Go to next DWORD */
+		}
+	} else if (swar->fftbin_size_war ==
+			SPECTRAL_FFTBIN_SIZE_WAR_2BYTE_TO_1BYTE) {
+		binptr_16 = (uint16_t *)bin_pwr_data;
+
+		for (i = 0; i < num_fftbins; i++) {
+			/* Byteswap the FFT bin and copy it back */
+			*binptr_16 = qdf_le16_to_cpu(*binptr_16);
+			++binptr_16;
+		}
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+#endif /* BIG_ENDIAN_HOST */
+
 int
 target_if_consume_spectral_report_gen3(
 	 struct target_if_spectral *spectral,
@@ -1890,6 +1984,15 @@ target_if_consume_spectral_report_gen3(
 	uint8_t *temp;
 	bool finite_scan = false;
 
+	/* Apply byte-swap on the headers */
+	if (p_sops->byte_swap_headers) {
+		ret = p_sops->byte_swap_headers(spectral, data);
+		if (QDF_IS_STATUS_ERROR(ret)) {
+			spectral_err_rl("Byte-swap on Spectral headers failed");
+			goto fail;
+		}
+	}
+
 	/* Process Spectral scan summary report */
 	if (target_if_verify_sig_and_tag_gen3(
 			spectral, data,
@@ -1959,7 +2062,10 @@ target_if_consume_spectral_report_gen3(
 			goto fail;
 		}
 		p_fft_report = (struct spectral_phyerr_fft_report_gen3 *)data;
-		fft_hdr_length = p_fft_report->fft_hdr_length * 4;
+		fft_hdr_length = get_bitfield(
+				p_fft_report->fft_hdr_lts,
+				SPECTRAL_REPORT_LTS_HDR_LENGTH_SIZE_GEN3,
+				SPECTRAL_REPORT_LTS_HDR_LENGTH_POS_GEN3) * 4;
 		if (fft_hdr_length < 16) {
 			spectral_err("Wrong TLV length %u, detector id = %d",
 				     fft_hdr_length, detector_id);
@@ -2016,10 +2122,6 @@ target_if_consume_spectral_report_gen3(
 				goto fail;
 		}
 
-		if (spectral_debug_level & (DEBUG_SPECTRAL2 | DEBUG_SPECTRAL4))
-			target_if_dump_fft_report_gen3(spectral, spectral_mode,
-						       p_fft_report, p_sfft);
-
 		params.rssi         = rssi;
 
 		vdev = target_if_spectral_get_vdev(spectral, spectral_mode);
@@ -2098,6 +2200,26 @@ target_if_consume_spectral_report_gen3(
 			params.datalen = (fft_hdr_length * 4);
 		}
 
+		/* Apply byte-swap on the FFT bins.
+		 * NOTE: Until this point, bytes of the FFT bins could be in
+		 *       reverse order on a big-endian machine. If the consumers
+		 *       of FFT bins expects bytes in the correct order,
+		 *       they should use them only after this point.
+		 */
+		if (p_sops->byte_swap_fft_bins) {
+			ret = p_sops->byte_swap_fft_bins(
+						&spectral->len_adj_swar,
+						temp, fft_bin_count);
+			if (QDF_IS_STATUS_ERROR(ret)) {
+				spectral_err_rl("Byte-swap on the FFT bins failed");
+				goto fail;
+			}
+		}
+
+		if (spectral_debug_level & (DEBUG_SPECTRAL2 | DEBUG_SPECTRAL4))
+			target_if_dump_fft_report_gen3(spectral, spectral_mode,
+						       p_fft_report, p_sfft);
+
 		target_if_spectral_verify_ts(spectral, report->data,
 					     params.tstamp);
 	} else if (is_secondaryseg_expected(spectral, spectral_mode)) {
@@ -2117,7 +2239,10 @@ target_if_consume_spectral_report_gen3(
 			goto fail;
 		}
 		p_fft_report = (struct spectral_phyerr_fft_report_gen3 *)data;
-		fft_hdr_length = p_fft_report->fft_hdr_length * 4;
+		fft_hdr_length = get_bitfield(
+				p_fft_report->fft_hdr_lts,
+				SPECTRAL_REPORT_LTS_HDR_LENGTH_SIZE_GEN3,
+				SPECTRAL_REPORT_LTS_HDR_LENGTH_POS_GEN3) * 4;
 		if (fft_hdr_length < 16) {
 			spectral_err("Wrong TLV length %u, detector id = %u",
 				     fft_hdr_length, detector_id);
@@ -2162,10 +2287,6 @@ target_if_consume_spectral_report_gen3(
 				goto fail;
 		}
 
-		if (spectral_debug_level & (DEBUG_SPECTRAL2 | DEBUG_SPECTRAL4))
-			target_if_dump_fft_report_gen3(spectral, spectral_mode,
-						       p_fft_report, p_sfft);
-
 		params.rssi_sec80 = rssi;
 
 		vdev = target_if_spectral_get_vdev(spectral, spectral_mode);
@@ -2198,6 +2319,28 @@ target_if_consume_spectral_report_gen3(
 		params.bin_pwr_data_sec80   =
 			(uint8_t *)((uint8_t *)p_fft_report +
 			 SPECTRAL_FFT_BINS_POS);
+
+		/* Apply byte-swap on the FFT bins.
+		 * NOTE: Until this point, bytes of the FFT bins could be in
+		 *       reverse order on a big-endian machine. If the consumers
+		 *       of FFT bins expects bytes in the correct order,
+		 *       they should use them only after this point.
+		 */
+		if (p_sops->byte_swap_fft_bins) {
+			ret = p_sops->byte_swap_fft_bins(
+					&spectral->len_adj_swar,
+					params.bin_pwr_data_sec80,
+					fft_bin_count);
+			if (QDF_IS_STATUS_ERROR(ret)) {
+				spectral_err_rl("Byte-swap on the FFT bins failed");
+				goto fail;
+			}
+		}
+
+		if (spectral_debug_level & (DEBUG_SPECTRAL2 | DEBUG_SPECTRAL4))
+			target_if_dump_fft_report_gen3(spectral, spectral_mode,
+						       p_fft_report, p_sfft);
+
 	} else {
 		spectral_err("Spectral state machine in undefined state");
 		goto fail;