summary refs log tree commit diff
path: root/Projects/TempDataLogger/Lib/DataflashManager.c
diff options
context:
space:
mode:
Diffstat (limited to 'Projects/TempDataLogger/Lib/DataflashManager.c')
-rw-r--r--Projects/TempDataLogger/Lib/DataflashManager.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/Projects/TempDataLogger/Lib/DataflashManager.c b/Projects/TempDataLogger/Lib/DataflashManager.c
new file mode 100644
index 0000000000..b1111ce39b
--- /dev/null
+++ b/Projects/TempDataLogger/Lib/DataflashManager.c
@@ -0,0 +1,534 @@
+/*
+             LUFA Library
+     Copyright (C) Dean Camera, 2017.
+
+  dean [at] fourwalledcubicle [dot] com
+           www.lufa-lib.org
+*/
+
+/*
+  Copyright 2017  Dean Camera (dean [at] fourwalledcubicle [dot] com)
+
+  Permission to use, copy, modify, distribute, and sell this
+  software and its documentation for any purpose is hereby granted
+  without fee, provided that the above copyright notice appear in
+  all copies and that both that the copyright notice and this
+  permission notice and warranty disclaimer appear in supporting
+  documentation, and that the name of the author not be used in
+  advertising or publicity pertaining to distribution of the
+  software without specific, written prior permission.
+
+  The author disclaims all warranties with regard to this
+  software, including all implied warranties of merchantability
+  and fitness.  In no event shall the author be liable for any
+  special, indirect or consequential damages or any damages
+  whatsoever resulting from loss of use, data or profits, whether
+  in an action of contract, negligence or other tortious action,
+  arising out of or in connection with the use or performance of
+  this software.
+*/
+
+/** \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"
+
+/** 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[in] MSInterfaceInfo  Pointer to a structure containing a Mass Storage Class configuration and state
+ *  \param[in] BlockAddress  Data block starting address for the write sequence
+ *  \param[in] 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());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+			Dataflash_SendByte(Endpoint_Read_8());
+
+			/* 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;
+
+		/* Read an endpoint packet sized data block from 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(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(Dataflash_ReceiveByte());
+			Endpoint_Write_8(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();
+
+	/* 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,
+                                      const 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;
+
+		/* Read an endpoint packet sized data block from 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 \c true if all media chips are working, \c 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;
+}
+