/*
 * @note
 * Copyright(C) NXP Semiconductors, 2012
 * All rights reserved.
 *
 * @par
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * LPC products.  This software is supplied "AS IS" without any warranties of
 * any kind, and NXP Semiconductors and its licensor disclaim any and
 * all warranties, express or implied, including all implied warranties of
 * merchantability, fitness for a particular purpose and non-infringement of
 * intellectual property rights.  NXP Semiconductors assumes no responsibility
 * or liability for the use of the software, conveys no license or rights under any
 * patent, copyright, mask work right, or any other intellectual property rights in
 * or to any products. NXP Semiconductors reserves the right to make changes
 * in the software without notification. NXP Semiconductors also makes no
 * representation or warranty that such application will be suitable for the
 * specified use without further testing or modification.
 *
 * @par
 * Permission to use, copy, modify, and distribute this software and its
 * documentation is hereby granted, under NXP Semiconductors' and its
 * licensor's relevant copyrights in the software, without fee, provided that it
 * is used in conjunction with NXP Semiconductors microcontrollers.  This
 * copyright, permission, and disclaimer notice must appear in all copies of
 * this code.
 */

/** @file
 *
 *  Functions to manage the physical Dataflash media, including reading and writing of
 *  blocks of data. These functions are called by the SCSI layer when data must be stored
 *  or retrieved to/from the physical storage media. If a different media is used (such
 *  as a SD card or EEPROM), functions similar to these will need to be generated.
 */

#define  INCLUDE_FROM_DATAFLASHMANAGER_C
#include "DataflashManager.h"

/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/

static int InitializedFlag = 1;

/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/

/*****************************************************************************
 * Private functions
 ****************************************************************************/

/*****************************************************************************
 * Public functions
 ****************************************************************************/

/** Writes blocks (OS blocks, not Dataflash pages) to the storage medium, the board Dataflash IC(s), from
 *  the pre-selected data OUT endpoint. This routine reads in OS sized blocks from the endpoint and writes
 *  them to the Dataflash in Dataflash page sized blocks.
 *
 *  @param	MSInterfaceInfo	: Pointer to a structure containing a Mass Storage Class configuration and state
 *  @param	BlockAddress	: Data block starting address for the write sequence
 *  @param	TotalBlocks		: Number of blocks of data to write
 */
void DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo,
								  const uint32_t BlockAddress,
								  uint16_t TotalBlocks)
{
	uint16_t CurrDFPage          = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE);
	uint16_t CurrDFPageByte      = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE);
	uint8_t  CurrDFPageByteDiv16 = (CurrDFPageByte >> 4);
	bool     UsingSecondBuffer   = false;

	/* Select the correct starting Dataflash IC for the block requested */
	Dataflash_SelectChipFromPage(CurrDFPage);

#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE)
	/* Copy selected dataflash's current page contents to the Dataflash buffer */
	Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1);
	Dataflash_SendAddressBytes(CurrDFPage, 0);
	Dataflash_WaitWhileBusy();
#endif

	/* Send the Dataflash buffer write command */
	Dataflash_SendByte(DF_CMD_BUFF1WRITE);
	Dataflash_SendAddressBytes(0, CurrDFPageByte);

	/* Wait until endpoint is ready before continuing */
	if (Endpoint_WaitUntilReady()) {
		return;
	}

	while (TotalBlocks) {
		uint8_t BytesInBlockDiv16 = 0;

		/* Write an endpoint packet sized data block to the Dataflash */
		while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) {
			/* Check if the endpoint is currently empty */
			if (!(Endpoint_IsReadWriteAllowed())) {
				/* Clear the current endpoint bank */
				Endpoint_ClearOUT();

				/* Wait until the host has sent another packet */
				if (Endpoint_WaitUntilReady()) {
					return;
				}
			}

			/* Check if end of Dataflash page reached */
			if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) {
				/* Write the Dataflash buffer contents back to the Dataflash page */
				Dataflash_WaitWhileBusy();
				Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE);
				Dataflash_SendAddressBytes(CurrDFPage, 0);

				/* Reset the Dataflash buffer counter, increment the page counter */
				CurrDFPageByteDiv16 = 0;
				CurrDFPage++;

				/* Once all the Dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */
				if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) {
					UsingSecondBuffer = !(UsingSecondBuffer);
				}

				/* Select the next Dataflash chip based on the new Dataflash page index */
				Dataflash_SelectChipFromPage(CurrDFPage);

#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE)
				/* If less than one Dataflash page remaining, copy over the existing page to preserve trailing data */
				if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) {
					/* Copy selected dataflash's current page contents to the Dataflash buffer */
					Dataflash_WaitWhileBusy();
					Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1);
					Dataflash_SendAddressBytes(CurrDFPage, 0);
					Dataflash_WaitWhileBusy();
				}
#endif

				/* Send the Dataflash buffer write command */
				Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2WRITE : DF_CMD_BUFF1WRITE);
				Dataflash_SendAddressBytes(0, 0);
			}

			/* Write one 16-byte chunk of data to the Dataflash */
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));
			Dataflash_SendByte(Endpoint_Read_8(MSInterfaceInfo->Config.PortNumber));

			/* Increment the Dataflash page 16 byte block counter */
			CurrDFPageByteDiv16++;

			/* Increment the block 16 byte block counter */
			BytesInBlockDiv16++;

			/* Check if the current command is being aborted by the host */
			if (MSInterfaceInfo->State.IsMassStoreReset) {
				return;
			}
		}

		/* Decrement the blocks remaining counter */
		TotalBlocks--;
	}

	/* Write the Dataflash buffer contents back to the Dataflash page */
	Dataflash_WaitWhileBusy();
	Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE);
	Dataflash_SendAddressBytes(CurrDFPage, 0x00);
	Dataflash_WaitWhileBusy();

	/* If the endpoint is empty, clear it ready for the next packet from the host */
	if (!(Endpoint_IsReadWriteAllowed())) {
		Endpoint_ClearOUT();
	}

	/* Deselect all Dataflash chips */
	Dataflash_DeselectChip();
}

/** Reads blocks (OS blocks, not Dataflash pages) from the storage medium, the board Dataflash IC(s), into
 *  the pre-selected data IN endpoint. This routine reads in Dataflash page sized blocks from the Dataflash
 *  and writes them in OS sized blocks to the endpoint.
 *
 *  \param[in] MSInterfaceInfo  Pointer to a structure containing a Mass Storage Class configuration and state
 *  \param[in] BlockAddress  Data block starting address for the read sequence
 *  \param[in] TotalBlocks   Number of blocks of data to read
 */
void DataflashManager_ReadBlocks(USB_ClassInfo_MS_Device_t *const MSInterfaceInfo,
								 const uint32_t BlockAddress,
								 uint16_t TotalBlocks)
{
	uint16_t CurrDFPage          = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE);
	uint16_t CurrDFPageByte      = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE);
	uint8_t  CurrDFPageByteDiv16 = (CurrDFPageByte >> 4);

	/* Select the correct starting Dataflash IC for the block requested */
	Dataflash_SelectChipFromPage(CurrDFPage);

	/* Send the Dataflash main memory page read command */
	Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD);
	Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte);
	Dataflash_SendByte(0x00);
	Dataflash_SendByte(0x00);
	Dataflash_SendByte(0x00);
	Dataflash_SendByte(0x00);

	/* Wait until endpoint is ready before continuing */
	if (Endpoint_WaitUntilReady()) {
		return;
	}

	while (TotalBlocks) {
		uint8_t BytesInBlockDiv16 = 0;

		/* Write an endpoint packet sized data block to the Dataflash */
		while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) {
			/* Check if the endpoint is currently full */
			if (!(Endpoint_IsReadWriteAllowed())) {
				/* Clear the endpoint bank to send its contents to the host */
				Endpoint_ClearIN();

				/* Wait until the endpoint is ready for more data */
				if (Endpoint_WaitUntilReady()) {
					return;
				}
			}

			/* Check if end of Dataflash page reached */
			if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) {
				/* Reset the Dataflash buffer counter, increment the page counter */
				CurrDFPageByteDiv16 = 0;
				CurrDFPage++;

				/* Select the next Dataflash chip based on the new Dataflash page index */
				Dataflash_SelectChipFromPage(CurrDFPage);

				/* Send the Dataflash main memory page read command */
				Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD);
				Dataflash_SendAddressBytes(CurrDFPage, 0);
				Dataflash_SendByte(0x00);
				Dataflash_SendByte(0x00);
				Dataflash_SendByte(0x00);
				Dataflash_SendByte(0x00);
			}

			/* Read one 16-byte chunk of data from the Dataflash */
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());
			Endpoint_Write_8(MSInterfaceInfo->Config.PortNumber, Dataflash_ReceiveByte());

			/* Increment the Dataflash page 16 byte block counter */
			CurrDFPageByteDiv16++;

			/* Increment the block 16 byte block counter */
			BytesInBlockDiv16++;

			/* Check if the current command is being aborted by the host */
			if (MSInterfaceInfo->State.IsMassStoreReset) {
				return;
			}
		}

		/* Decrement the blocks remaining counter */
		TotalBlocks--;
	}

	/* If the endpoint is full, send its contents to the host */
	if (!(Endpoint_IsReadWriteAllowed())) {
		Endpoint_ClearIN(MSInterfaceInfo->Config.PortNumber);
	}

	/* Deselect all Dataflash chips */
	Dataflash_DeselectChip();
}

/** Writes blocks (OS blocks, not Dataflash pages) to the storage medium, the board Dataflash IC(s), from
 *  the given RAM buffer. This routine reads in OS sized blocks from the buffer and writes them to the
 *  Dataflash in Dataflash page sized blocks. This can be linked to FAT libraries to write files to the
 *  Dataflash.
 *
 *  \param[in] BlockAddress  Data block starting address for the write sequence
 *  \param[in] TotalBlocks   Number of blocks of data to write
 *  \param[in] BufferPtr     Pointer to the data source RAM buffer
 */
void DataflashManager_WriteBlocks_RAM(const uint32_t BlockAddress,
									  uint16_t TotalBlocks,
									  uint8_t *BufferPtr)
{
	uint16_t CurrDFPage          = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE);
	uint16_t CurrDFPageByte      = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE);
	uint8_t  CurrDFPageByteDiv16 = (CurrDFPageByte >> 4);
	bool     UsingSecondBuffer   = false;

	/* Select the correct starting Dataflash IC for the block requested */
	Dataflash_SelectChipFromPage(CurrDFPage);

#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE)
	/* Copy selected dataflash's current page contents to the Dataflash buffer */
	Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1);
	Dataflash_SendAddressBytes(CurrDFPage, 0);
	Dataflash_WaitWhileBusy();
#endif

	/* Send the Dataflash buffer write command */
	Dataflash_SendByte(DF_CMD_BUFF1WRITE);
	Dataflash_SendAddressBytes(0, CurrDFPageByte);

	while (TotalBlocks) {
		uint8_t BytesInBlockDiv16 = 0;

		/* Write an endpoint packet sized data block to the Dataflash */
		while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) {
			/* Check if end of Dataflash page reached */
			if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) {
				/* Write the Dataflash buffer contents back to the Dataflash page */
				Dataflash_WaitWhileBusy();
				Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE);
				Dataflash_SendAddressBytes(CurrDFPage, 0);

				/* Reset the Dataflash buffer counter, increment the page counter */
				CurrDFPageByteDiv16 = 0;
				CurrDFPage++;

				/* Once all the Dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */
				if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) {
					UsingSecondBuffer = !(UsingSecondBuffer);
				}

				/* Select the next Dataflash chip based on the new Dataflash page index */
				Dataflash_SelectChipFromPage(CurrDFPage);

#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE)
				/* If less than one Dataflash page remaining, copy over the existing page to preserve trailing data */
				if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) {
					/* Copy selected dataflash's current page contents to the Dataflash buffer */
					Dataflash_WaitWhileBusy();
					Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1);
					Dataflash_SendAddressBytes(CurrDFPage, 0);
					Dataflash_WaitWhileBusy();
				}
#endif

				/* Send the Dataflash buffer write command */
				Dataflash_ToggleSelectedChipCS();
				Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2WRITE : DF_CMD_BUFF1WRITE);
				Dataflash_SendAddressBytes(0, 0);
			}

			/* Write one 16-byte chunk of data to the Dataflash */
			for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) {
				Dataflash_SendByte(*(BufferPtr++));
			}

			/* Increment the Dataflash page 16 byte block counter */
			CurrDFPageByteDiv16++;

			/* Increment the block 16 byte block counter */
			BytesInBlockDiv16++;
		}

		/* Decrement the blocks remaining counter */
		TotalBlocks--;
	}

	/* Write the Dataflash buffer contents back to the Dataflash page */
	Dataflash_WaitWhileBusy();
	Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE);
	Dataflash_SendAddressBytes(CurrDFPage, 0x00);
	Dataflash_WaitWhileBusy();

	/* Deselect all Dataflash chips */
	Dataflash_DeselectChip();
}

/** Reads blocks (OS blocks, not Dataflash pages) from the storage medium, the board Dataflash IC(s), into
 *  the preallocated RAM buffer. This routine reads in Dataflash page sized blocks from the Dataflash
 *  and writes them in OS sized blocks to the given buffer. This can be linked to FAT libraries to read
 *  the files stored on the Dataflash.
 *
 *  \param[in] BlockAddress  Data block starting address for the read sequence
 *  \param[in] TotalBlocks   Number of blocks of data to read
 *  \param[out] BufferPtr    Pointer to the data destination RAM buffer
 */
void DataflashManager_ReadBlocks_RAM(const uint32_t BlockAddress,
									 uint16_t TotalBlocks,
									 uint8_t *BufferPtr)
{
	uint16_t CurrDFPage          = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE);
	uint16_t CurrDFPageByte      = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE);
	uint8_t  CurrDFPageByteDiv16 = (CurrDFPageByte >> 4);

	/* Select the correct starting Dataflash IC for the block requested */
	Dataflash_SelectChipFromPage(CurrDFPage);

	/* Send the Dataflash main memory page read command */
	Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD);
	Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte);
	Dataflash_SendByte(0x00);
	Dataflash_SendByte(0x00);
	Dataflash_SendByte(0x00);
	Dataflash_SendByte(0x00);

	while (TotalBlocks) {
		uint8_t BytesInBlockDiv16 = 0;

		/* Write an endpoint packet sized data block to the Dataflash */
		while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) {
			/* Check if end of Dataflash page reached */
			if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) {
				/* Reset the Dataflash buffer counter, increment the page counter */
				CurrDFPageByteDiv16 = 0;
				CurrDFPage++;

				/* Select the next Dataflash chip based on the new Dataflash page index */
				Dataflash_SelectChipFromPage(CurrDFPage);

				/* Send the Dataflash main memory page read command */
				Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD);
				Dataflash_SendAddressBytes(CurrDFPage, 0);
				Dataflash_SendByte(0x00);
				Dataflash_SendByte(0x00);
				Dataflash_SendByte(0x00);
				Dataflash_SendByte(0x00);
			}

			/* Read one 16-byte chunk of data from the Dataflash */
			for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) {
				*(BufferPtr++) = Dataflash_ReceiveByte();
			}

			/* Increment the Dataflash page 16 byte block counter */
			CurrDFPageByteDiv16++;

			/* Increment the block 16 byte block counter */
			BytesInBlockDiv16++;
		}

		/* Decrement the blocks remaining counter */
		TotalBlocks--;
	}

	/* Deselect all Dataflash chips */
	Dataflash_DeselectChip();
}

/** Disables the Dataflash memory write protection bits on the board Dataflash ICs, if enabled. */
void DataflashManager_ResetDataflashProtections(void)
{
	/* Select first Dataflash chip, send the read status register command */
	Dataflash_SelectChip(DATAFLASH_CHIP1);
	Dataflash_SendByte(DF_CMD_GETSTATUS);

	/* Check if sector protection is enabled */
	if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) {
		Dataflash_ToggleSelectedChipCS();

		/* Send the commands to disable sector protection */
		Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]);
		Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]);
		Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]);
		Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]);
	}

	/* Select second Dataflash chip (if present on selected board), send read status register command */
#if (DATAFLASH_TOTALCHIPS == 2)
	Dataflash_SelectChip(DATAFLASH_CHIP2);
	Dataflash_SendByte(DF_CMD_GETSTATUS);

	/* Check if sector protection is enabled */
	if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) {
		Dataflash_ToggleSelectedChipCS();

		/* Send the commands to disable sector protection */
		Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]);
		Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]);
		Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]);
		Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]);
	}
#endif

	/* Deselect current Dataflash chip */
	Dataflash_DeselectChip();
}

/** Performs a simple test on the attached Dataflash IC(s) to ensure that they are working.
 *
 *  \return Boolean true if all media chips are working, false otherwise
 */
bool DataflashManager_CheckDataflashOperation(void)
{
	uint8_t ReturnByte;

	/* Test first Dataflash IC is present and responding to commands */
	Dataflash_SelectChip(DATAFLASH_CHIP1);
	Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO);
	ReturnByte = Dataflash_ReceiveByte();
	Dataflash_DeselectChip();

	/* If returned data is invalid, fail the command */
	if (ReturnByte != DF_MANUFACTURER_ATMEL) {
		return false;
	}

#if (DATAFLASH_TOTALCHIPS == 2)
	/* Test second Dataflash IC is present and responding to commands */
	Dataflash_SelectChip(DATAFLASH_CHIP2);
	Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO);
	ReturnByte = Dataflash_ReceiveByte();
	Dataflash_DeselectChip();

	/* If returned data is invalid, fail the command */
	if (ReturnByte != DF_MANUFACTURER_ATMEL) {
		return false;
	}
#endif

	return true;
}
