Use Two MPFS2 files

This page lists the changes required to the Microchip TCPIP Stack such that if a requested file is not found in a MPFS2 file system image in external memory (typically PNP-MEMFL32 ) the application will then look in an MPFS2 file system image in the MC32MX64GP mainboard’s PIC32MX340F512H MCU program flash

This exercise was done for Microchip TCPIP Stack V5.25 but is compatible with V5.31

Introduction

By default, the Microchip TCPIP Stack application (excluding the MDD project) allows you to store one MPFS2 file image either in the target processor program flash ROM or on an external SPI EEPROM or SPI Flash memory IC

We can change the MPFS2 File Access API  to look first in the external memory and if a file is not found there then look in internal memory

All changes will be internal to the MPFS2 File Access API, so no additional changes required in any other part of the TCPIP Stack

Approach

The V5.31 start point for this is project is: \Microchip Solutions v2010-10-19\TCPIP Demo App

Interaction between the MPFS2 API and other parts of the TCPIP stack (once a file is found by name) is via either a MPFS2 supplied file handle or a fatID.

To indicate if a file is internal or external we:

  • overlay a flag INTERNALFATIDOFFSET on MPFS_HANDLE to indicate if the file is in external or internal memory when using the file handle
  • add an indicator ‘internal’ to struct MPFS_STUB to indicate if the file is in external or internal memory

We also amend helper function initialise _Validate() & it’s associated variable ‘numFiles’  into 2 separate functions/variables

  • static void _ValidateInternal(void) populates numFilesInternal
  • static void _ValidateExternal(void) populates numFilesExternal

And because we have a significant amount of unused ram we can MPFS_Handle from Byte to DWord which will allow us to increase MAX_MPFS_HANDLES in TCPIPConfig.h if we so desire

Changes

MPFS2.h

change this:

/****************************************************************************
  Section:
	Type Definitions
  ***************************************************************************/
	#define MPFS2_FLAG_ISZIPPED		((WORD)0x0001)	// Indicates a file is compressed with GZIP compression
	#define MPFS2_FLAG_HASINDEX		((WORD)0x0002)	// Indicates a file has an associated index of dynamic variables
	#define MPFS_INVALID			(0xffffffffu)	// Indicates a position pointer is invalid
	#define MPFS_INVALID_FAT		(0xffffu)		// Indicates an invalid FAT cache
	#define MPFS_INVALID_HANDLE 	(0xffu)			// Indicates that a handle is not valid
	typedef DWORD MPFS_PTR;							// MPFS Pointers are currently DWORDs
	typedef BYTE MPFS_HANDLE;						// MPFS Handles are currently stored as BYTEs

	// Stores each file handle's information
	// Handles are free when addr = MPFS_INVALID
	typedef struct
	{
		MPFS_PTR addr;		// Current address in the file system
		DWORD bytesRem;		// How many bytes remain in this file
		WORD fatID;			// ID of which file in the FAT was accessed
	} MPFS_STUB;

To this:

/****************************************************************************
 Section:
 Type Definitions
 ***************************************************************************/
 #define MPFS2_FLAG_ISZIPPED        ((WORD)0x0001)    // Indicates a file is compressed with GZIP compression
 #define MPFS2_FLAG_HASINDEX        ((WORD)0x0002)    // Indicates a file has an associated index of dynamic variables
 #define MPFS_INVALID            (0xffffffffu)    // Indicates a position pointer is invalid
 #define MPFS_INVALID_FAT        (0xffffu)        // Indicates an invalid FAT cache
 #define MPFS_INVALID_HANDLE     (0xffffu)            // Indicates that a handle is not valid // PNP
 typedef DWORD MPFS_PTR;                            // MPFS Pointers are currently DWORDs
 typedef DWORD MPFS_HANDLE;                        // MPFS Handles are currently stored as BYTEs // PNP
 #define INTERNALFATIDOFFSET            (0x80000000)  //PNP

 // Stores each file handle's information
 // Handles are free when addr = MPFS_INVALID

 typedef struct
 {
 MPFS_PTR addr;        // Current address in the file system
 DWORD bytesRem;        // How many bytes remain in this file
 MPFS_HANDLE fatID;            // ID of which file in the FAT was accessed
 BYTE internal;        // 1 = found in internal memory, 0 = in external memory
 } MPFS_STUB;

MPFS2.c

Replace with (search for numFiles, internal & INTERNALFATIDOFFSET to find the differences):

/*********************************************************************
 *
 *    Microchip File System (MPFS) File Access API
 *  Module for Microchip TCP/IP Stack
 *     -Provides single API for accessing web pages and other files
 *    from internal program memory or an external serial EEPROM memory
 *     -Reference: AN833
 *
 *********************************************************************
 * FileName:        MPFS.c
 * Dependencies:    SPIEEPROM.c, SPIFlash.c, or MPFSImg2.c/.s
 * Processor:       PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32
 * Compiler:        Microchip C32 v1.05 or higher
 *                    Microchip C30 v3.12 or higher
 *                    Microchip C18 v3.30 or higher
 *                    HI-TECH PICC-18 PRO 9.63PL2 or higher
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * Copyright (C) 2002-2009 Microchip Technology Inc.  All rights
 * reserved.
 *
 * Microchip licenses to you the right to use, modify, copy, and
 * distribute:
 * (i)  the Software when embedded on a Microchip microcontroller or
 *      digital signal controller product ("Device") which is
 *      integrated into Licensee's product; or
 * (ii) ONLY the Software driver source files ENC28J60.c, ENC28J60.h,
 *        ENCX24J600.c and ENCX24J600.h ported to a non-Microchip device
 *        used in conjunction with a Microchip ethernet controller for
 *        the sole purpose of interfacing with the ethernet controller.
 *
 * You should refer to the license agreement accompanying this
 * Software for additional information regarding your rights and
 * obligations.
 *
 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE
 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER
 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
 *
 *
 * Author               Date        Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Elliott Wood            07/2007        Complete rewrite as MPFS2
 * E. Wood                04/2008        Updated as MPFS2.1
 ********************************************************************/

 /********************************************************************
 * PlugAndProgram Modifications
 * 20100801
 *   Amended to look for MPFS2 in ROM if a file is not found in
 *   external Memory
 ********************************************************************/

#define __MPFS2_C

#include "TCPIP Stack/TCPIP.h"

#if defined(STACK_USE_MPFS2)

//Supports long file names to 64 characters
#define MAX_FILE_NAME_LEN   (64u)

/*
 * MPFS Structure:
 *     [M][P][F][S]
 *     [BYTE Ver Hi][BYTE Ver Lo][WORD Number of Files]
 *     [Name Hash 0][Name Hash 1]...[Name Hash N]
 *     [File Record 0][File Record 1]...[File Record N]
 *     [String 0][String 1]...[String N]
 *     [File Data 0][File Data 1]...[File Data N]
 *
 * Name Hash (2 bytes):
 *     hash = 0
 *     for each(byte in name)
 *         hash += byte
 *         hash <<= 1
 *
 *     Technically this means the hash only includes the
 *     final 15 characters of a name.
 *
 * File Record Structure (22 bytes):
 *     [DWORD String Ptr][DWORD Data Ptr]
 *     [DWORD Len][DWORD Timestamp][DWORD Microtime]
 *     [WORD Flags]
 *
 *     Pointers are absolute addresses within the MPFS image.
 *     Timestamp is the UNIX timestamp
 *     Microtime is currently unimplemented
 *
 * String Structure (1 to 64 bytes):
 *     ["path/to/file.ext"][0x00]
 *
 * File Data Structure (arbitrary length):
 *        [File Data]
 *
 * Unlike previous versions, there are no delimiters.
 *
 * Name hash is calculated as follows:
 *      hash = 0
 *      for each(byte in name)
 *          hash += byte, hash <<= 1
 *
 * When a file has an index, that index file has no file name,
 * but is accessible as the file immediately following in the image.
 *
 * Current version is 2.1
 */

/****************************************************************************
 Section:
 Module-Only Globals and Functions
 ***************************************************************************/

// Track the MPFS File Handles
// MPFSStubs[0] is reserved for internal use (FAT access)
static MPFS_STUB MPFSStubs[MAX_MPFS_HANDLES+1];

// Allows the MPFS to be locked, preventing access during updates
static BOOL isMPFSLocked;

// FAT record cache
static MPFS_FAT_RECORD fatCache;

// ID of currently loaded fatCache
static MPFS_HANDLE fatCacheID;

// Number of files in this MPFS image
//static WORD numFiles;  // PNP
static DWORD numFilesInternal;  // PNP
static DWORD numFilesExternal;  // PNP

static void _LoadFATRecord(MPFS_HANDLE fatID);
static void _ValidateInternal(void);
static void _ValidateExternal(void);

/****************************************************************************
 Section:
 EEPROM vs Flash Storage Settings
 ***************************************************************************/

#if defined(MPFS_USE_EEPROM)

 // Beginning address of MPFS Image
 #define MPFS_HEAD        MPFS_RESERVE_BLOCK

 // Track the last read address to prevent unnecessary
 // data overhead to switch locations.
 MPFS_PTR lastRead;

#elif defined(MPFS_USE_SPI_FLASH)

 // Beginning address of MPFS Image
 #define MPFS_HEAD        MPFS_RESERVE_BLOCK

#endif  // PNP

extern ROM BYTE MPFS_Start[];  // PNP
#define MPFS_HEADInternal        ((DWORD)(&MPFS_Start[0]))  // PNP

/****************************************************************************
 Section:
 Stack-Level Functions
 ***************************************************************************/

/*****************************************************************************
 Function:
 void MPFSInit(void)

 Summary:
 Initializes the MPFS module.

 Description:
 Sets all MPFS handles to closed, and initializes access to the EEPROM
 if necessary.

 Precondition:
 None

 Parameters:
 None

 Returns:
 None

 Remarks:
 This function is called only one during lifetime of the application.
 ***************************************************************************/
void MPFSInit(void)
{
 BYTE i;

 for(i = 1; i <= MAX_MPFS_HANDLES; i++)
 {
 MPFSStubs[i].addr = MPFS_INVALID;
 }

 #if defined(MPFS_USE_EEPROM)
 // Initialize the EEPROM access routines.
 XEEInit();
 lastRead = MPFS_INVALID;
 #endif

 #if defined(MPFS_USE_SPI_FLASH)
 // Initialize SPI Flash access routines.
 SPIFlashInit();
 #endif

 // Validate the image and load numFiles
 _ValidateExternal();
 _ValidateInternal();

 isMPFSLocked = FALSE;

}

/****************************************************************************
 Section:
 Handle Management Functions
 ***************************************************************************/

/*****************************************************************************
 Function:
 MPFS_HANDLE MPFSOpen(BYTE* cFile)

 Description:
 Opens a file in the MPFS2 file system.

 Precondition:
 None

 Parameters:
 cFile - a null terminated file name to open

 Returns:
 An MPFS_HANDLE to the opened file if found, or MPFS_INVALID_HANDLE
 if the file could not be found or no free handles exist.
 ***************************************************************************/
MPFS_HANDLE MPFSOpen(BYTE* cFile)
{
 MPFS_HANDLE hMPFS;
 WORD nameHash, i;
 WORD hashCache[8];
 BYTE *ptr, c;

 // Initialize c to avoid "may be used uninitialized" compiler warning
 c = 0;

 // Make sure MPFS is unlocked and we got a filename
 if(*cFile == '\0' || isMPFSLocked == TRUE)
 return MPFS_INVALID_HANDLE;

 // Calculate the name hash to speed up searching
 for(nameHash = 0, ptr = cFile; *ptr != '\0'; ptr++)
 {
 nameHash += *ptr;
 nameHash <<= 1;
 }

 // Find a free file handle to use
 for(hMPFS = 1; hMPFS <= MAX_MPFS_HANDLES; hMPFS++)
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 break;
 if(hMPFS == MAX_MPFS_HANDLES)
 return MPFS_INVALID_HANDLE;

 // Look External Memory first
 // Read in hashes, and check remainder on a match.  Store 8 in cache for performance
 for(i = 0; i < numFilesExternal; i++)
 {
 // For new block of 8, read in data
 if((i & 0x07) == 0u)
 {
 MPFSStubs[0].addr = 8 + i*2;
 MPFSStubs[0].bytesRem = 16;
 MPFSStubs[0].internal = 0;
 MPFSGetArray(0, (BYTE*)hashCache, 16);
 }

 // If the hash matches, compare the full filename
 if(hashCache[i&0x07] == nameHash)
 {
 _LoadFATRecord(i);
 MPFSStubs[0].addr = fatCache.string;
 MPFSStubs[0].bytesRem = 255;

 // Loop over filename to perform comparison
 for(ptr = cFile; *ptr != '\0'; ptr++)
 {
 MPFSGet(0, &c);
 if(*ptr != c)
 break;
 }

 MPFSGet(0, &c);

 if(c == '\0' && *ptr == '\0')
 {// Filename matches, so return true
 MPFSStubs[hMPFS].addr = fatCache.data;
 MPFSStubs[hMPFS].bytesRem = fatCache.len;
 MPFSStubs[hMPFS].fatID = i;
 MPFSStubs[hMPFS].internal = 0;
 return hMPFS;
 break;
 }
 }
 }

 // Look Internal Memory last
 // Read in hashes, and check remainder on a match.  Store 8 in cache for performance
 for(i = 0; i < numFilesInternal; i++)
 {
 // For new block of 8, read in data
 if((i & 0x07) == 0u)
 {
 MPFSStubs[0].addr = 8 + i*2;
 MPFSStubs[0].bytesRem = 16;
 MPFSStubs[0].internal = 1;
 MPFSGetArray(0, (BYTE*)hashCache, 16);
 }

 // If the hash matches, compare the full filename
 if(hashCache[i&0x07] == nameHash)
 {
 _LoadFATRecord(i+INTERNALFATIDOFFSET);
 MPFSStubs[0].addr = fatCache.string;
 MPFSStubs[0].bytesRem = 255;

 // Loop over filename to perform comparison
 for(ptr = cFile; *ptr != '\0'; ptr++)
 {
 MPFSGet(0, &c);
 if(*ptr != c)
 break;
 }

 MPFSGet(0, &c);

 if(c == '\0' && *ptr == '\0')
 {// Filename matches, so return true
 MPFSStubs[hMPFS].addr = fatCache.data;
 MPFSStubs[hMPFS].bytesRem = fatCache.len;
 MPFSStubs[hMPFS].fatID = i+INTERNALFATIDOFFSET;
 MPFSStubs[hMPFS].internal = 1;
 return hMPFS;
 }
 }
 }

 // No file name matched, so return nothing
 return MPFS_INVALID_HANDLE;
}

/*****************************************************************************
 Function:
 MPFS_HANDLE MPFSOpenID(DWORD hFatID)

 Summary:
 Quickly re-opens a file.

 Description:
 Quickly re-opens a file in the MPFS2 file system.  Use this function
 along with MPFSGetID() to quickly re-open a file without tying up
 a permanent MPFSStub.

 Precondition:
 None

 Parameters:
 hFatID - the ID of a previous opened file in the FAT

 Returns:
 An MPFS_HANDLE to the opened file if found, or MPFS_INVALID_HANDLE
 if the file could not be found or no free handles exist.
 ***************************************************************************/
MPFS_HANDLE MPFSOpenID(MPFS_HANDLE hFatID)
{
 MPFS_HANDLE hMPFS;

 // Make sure MPFS is unlocked and we got a valid id
 if(isMPFSLocked == TRUE || ( (hFatID > 0xffff && (hFatID - INTERNALFATIDOFFSET) >= numFilesInternal) || (hFatID <= 0xffff && hFatID >= numFilesExternal) ) )
 return MPFS_INVALID_HANDLE;

 // Find a free file handle to use
 for(hMPFS = 1; hMPFS <= MAX_MPFS_HANDLES; hMPFS++)
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 break;
 if(hMPFS == MAX_MPFS_HANDLES)
 return MPFS_INVALID_HANDLE;

 // Load the FAT record
 _LoadFATRecord(hFatID);

 // Set up the file handle
 MPFSStubs[hMPFS].fatID = hFatID;
 MPFSStubs[hMPFS].addr = fatCache.data;
 MPFSStubs[hMPFS].bytesRem = fatCache.len;
 if (hFatID > 0xffff)
 MPFSStubs[hMPFS].internal = 1;
 else
 MPFSStubs[hMPFS].internal = 0;

 return hMPFS;
}

/*****************************************************************************
 Function:
 void MPFSClose(MPFS_HANDLE hMPFS)

 Summary:
 Closes a file.

 Description:
 Closes a file and releases its stub back to the pool of available
 handles.

 Precondition:
 None

 Parameters:
 hMPFS - the file handle to be closed

 Returns:
 None
 ***************************************************************************/
void MPFSClose(MPFS_HANDLE hMPFS)
{
 if(hMPFS != 0u && hMPFS <= MAX_MPFS_HANDLES)
 MPFSStubs[hMPFS].addr = MPFS_INVALID;
}

/****************************************************************************
 Section:
 Data Reading Functions
 ***************************************************************************/

/*****************************************************************************
 Function:
 BOOL MPFSGet(MPFS_HANDLE hMPFS, BYTE* c)

 Description:
 Reads a byte from a file.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read
 c - Where to store the byte that was read

 Return Values:
 TRUE - The byte was successfully read
 FALSE - No byte was read because either the handle was invalid or
 the end of the file has been reached.
 ***************************************************************************/
BOOL MPFSGet(MPFS_HANDLE hMPFS, BYTE* c)
{
 // Make sure we're reading a valid address
 if(hMPFS > MAX_MPFS_HANDLES)
 return FALSE;
 if(    MPFSStubs[hMPFS].addr == MPFS_INVALID ||
 MPFSStubs[hMPFS].bytesRem == 0u)
 return FALSE;

 if(c == NULL)
 {
 MPFSStubs[hMPFS].addr++;
 MPFSStubs[hMPFS].bytesRem--;
 return TRUE;
 }

 if (MPFSStubs[hMPFS].internal == 0) // PNP
 {
 // Read function for EEPROM
 #if defined(MPFS_USE_EEPROM)
 // For performance, cache the last read address
 if(MPFSStubs[hMPFS].addr != lastRead+1)
 XEEBeginRead(MPFSStubs[hMPFS].addr + MPFS_HEAD);
 *c = XEERead();
 lastRead = MPFSStubs[hMPFS].addr;
 MPFSStubs[hMPFS].addr++;
 #elif defined(MPFS_USE_SPI_FLASH)
 SPIFlashReadArray(MPFSStubs[hMPFS].addr + MPFS_HEAD, c, 1);
 MPFSStubs[hMPFS].addr++;
 #endif
 }
 else
 {

 DWORD dwHITECHWorkaround = MPFS_HEADInternal; // PNP

 *c = *((ROM BYTE*)(MPFSStubs[hMPFS].addr+dwHITECHWorkaround));
 MPFSStubs[hMPFS].addr++;
 }

 MPFSStubs[hMPFS].bytesRem--;
 return TRUE;
}

/*****************************************************************************
 Function:
 WORD MPFSGetArray(MPFS_HANDLE hMPFS, BYTE* cData, WORD wLen)

 Description:
 Reads a series of bytes from a file.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read
 cData - where to store the bytes that were read
 wLen - how many bytes to read

 Returns:
 The number of bytes successfully read.  If this is less than wLen,
 an EOF occurred while attempting to read.
 ***************************************************************************/
WORD MPFSGetArray(MPFS_HANDLE hMPFS, BYTE* cData, WORD wLen)
{
 // Make sure we're reading a valid address
 if(hMPFS > MAX_MPFS_HANDLES)
 return 0;

 // Determine how many we can actually read
 if(wLen > MPFSStubs[hMPFS].bytesRem)
 wLen = MPFSStubs[hMPFS].bytesRem;

 // Make sure we're reading a valid address
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID || wLen == 0u)
 return 0;

 if(cData == NULL)
 {
 MPFSStubs[hMPFS].addr += wLen;
 MPFSStubs[hMPFS].bytesRem -= wLen;
 return wLen;
 }

 // Read the data
 if (MPFSStubs[hMPFS].internal == 0) // PNP
 {
 #if defined(MPFS_USE_EEPROM)
 XEEReadArray(MPFSStubs[hMPFS].addr+MPFS_HEAD, cData, wLen);
 MPFSStubs[hMPFS].addr += wLen;
 MPFSStubs[hMPFS].bytesRem -= wLen;
 lastRead = MPFS_INVALID;
 #elif defined(MPFS_USE_SPI_FLASH)
 //        DelayMs(1); // PNPxx
 SPIFlashReadArray(MPFSStubs[hMPFS].addr+MPFS_HEAD, cData, wLen);
 MPFSStubs[hMPFS].addr += wLen;
 MPFSStubs[hMPFS].bytesRem -= wLen;
 #endif
 }
 else
 {
 DWORD dwHITECHWorkaround = MPFS_HEADInternal; // PNP
 memcpypgm2ram(cData, (ROM void*)(MPFSStubs[hMPFS].addr + dwHITECHWorkaround), wLen);
 MPFSStubs[hMPFS].addr += wLen;
 MPFSStubs[hMPFS].bytesRem -= wLen;
 }

 return wLen;
}

/*****************************************************************************
 Function:
 BOOL MPFSGetLong(MPFS_HANDLE hMPFS, DWORD* ul)

 Description:
 Reads a DWORD or Long value from the MPFS.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read
 ul - where to store the DWORD or long value that was read

 Returns:
 TRUE - The byte was successfully read
 FALSE - No byte was read because either the handle was invalid or
 the end of the file has been reached.
 ***************************************************************************/
BOOL MPFSGetLong(MPFS_HANDLE hMPFS, DWORD* ul)
{
 return ( MPFSGetArray(hMPFS, (BYTE*)ul, 4) == 4u );
}

/*****************************************************************************
 Function:
 BOOL MPFSSeek(MPFS_HANDLE hMPFS, DWORD dwOffset, MPFS_SEEK_MODE tMode)

 Description:
 Moves the current read pointer to a new location.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle to seek with
 dwOffset - offset from the specified position in the specified direction
 tMode - one of the MPFS_SEEK_MODE constants

 Returns:
 TRUE - the seek was successful
 FALSE - either the new location or the handle itself was invalid
 ***************************************************************************/
BOOL MPFSSeek(MPFS_HANDLE hMPFS, DWORD dwOffset, MPFS_SEEK_MODE tMode)
{
 DWORD temp;

 // Make sure a valid file is open
 if(hMPFS > MAX_MPFS_HANDLES)
 return FALSE;
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 return FALSE;

 switch(tMode)
 {
 // Seek offset bytes from start
 case MPFS_SEEK_START:
 temp = MPFSGetSize(hMPFS);
 if(dwOffset > temp)
 return FALSE;

 MPFSStubs[hMPFS].addr = MPFSGetStartAddr(hMPFS) + dwOffset;
 MPFSStubs[hMPFS].bytesRem = temp - dwOffset;
 return TRUE;

 // Seek forwards offset bytes
 case MPFS_SEEK_FORWARD:
 if(dwOffset > MPFSStubs[hMPFS].bytesRem)
 return FALSE;

 MPFSStubs[hMPFS].addr += dwOffset;
 MPFSStubs[hMPFS].bytesRem -= dwOffset;
 return TRUE;

 // Seek backwards offset bytes
 case MPFS_SEEK_REWIND:
 temp = MPFSGetStartAddr(hMPFS);
 if(MPFSStubs[hMPFS].addr < temp + dwOffset)
 return FALSE;

 MPFSStubs[hMPFS].addr -= dwOffset;
 MPFSStubs[hMPFS].bytesRem += dwOffset;
 return TRUE;

 // Seek so that offset bytes remain in file
 case MPFS_SEEK_END:
 temp = MPFSGetSize(hMPFS);
 if(dwOffset > temp)
 return FALSE;

 MPFSStubs[hMPFS].addr = MPFSGetEndAddr(hMPFS) - dwOffset;
 MPFSStubs[hMPFS].bytesRem = dwOffset;
 return TRUE;

 default:
 return FALSE;
 }
}

/****************************************************************************
 Section:
 Data Writing Functions
 ***************************************************************************/

/*****************************************************************************
 Function:
 MPFS_HANDLE MPFSFormat(void)

 Summary:
 Prepares the MPFS image for writing.

 Description:
 Prepares the MPFS image for writing and locks the image so that other
 processes may not access it.

 Precondition:
 None

 Parameters:
 None

 Returns:
 An MPFS handle that can be used for MPFSPut commands, or
 MPFS_INVALID_HANDLE when the EEPROM failed to initialize for writing.

 Remarks:
 In order to prevent misreads, the MPFS will be inaccessible until
 MPFSClose is called.  This function is not available when the MPFS
 is stored in internal Flash program memory.
 ***************************************************************************/
#if defined(MPFS_USE_EEPROM) || defined(MPFS_USE_SPI_FLASH)
MPFS_HANDLE MPFSFormat(void)
{

 BYTE i;

 // Close all files
 for(i = 0; i < MAX_MPFS_HANDLES; i++)
 MPFSStubs[i].addr = MPFS_INVALID;

 // Lock the image
 isMPFSLocked = TRUE;

 #if defined(MPFS_USE_EEPROM)
 // Set FAT ptr for writing
 MPFSStubs[0].addr = 0;
 MPFSStubs[0].fatID = 0xffffffff;
 MPFSStubs[0].bytesRem = MPFS_WRITE_PAGE_SIZE - ( ((BYTE)MPFSStubs[0].addr+MPFS_HEAD) & (MPFS_WRITE_PAGE_SIZE-1) );

 // Set up EEPROM for writing
 if( XEEBeginWrite(MPFSStubs[0].addr+MPFS_HEAD) == XEE_SUCCESS )
 return 0x00;

 return MPFS_INVALID_HANDLE;
 #else
 // Set up SPI Flash for writing
 SPIFlashBeginWrite(MPFS_HEAD);
 return 0x00;
 #endif
}
#endif

/*****************************************************************************
 Function:
 WORD MPFSPutArray(MPFS_HANDLE hMPFS, BYTE *cData, WORD wLen)

 Description:
 Writes an array of data to the MPFS image.

 Precondition:
 MPFSFormat was sucessfully called.

 Parameters:
 hMPFS - the file handle for writing
 cData - the array of bytes to write
 wLen - how many bytes to write

 Returns:
 The number of bytes successfully written.

 Remarks:
 For EEPROM, the actual write may not initialize until the internal write
 page is full.  To ensure that previously written data gets stored,
 MPFSPutEnd must be called after the last call to MPFSPutArray.
 ***************************************************************************/
#if defined(MPFS_USE_EEPROM) || defined(MPFS_USE_SPI_FLASH)
WORD MPFSPutArray(MPFS_HANDLE hMPFS, BYTE* cData, WORD wLen)
{
 #if defined(MPFS_USE_EEPROM)
 // Write to the EEPROM
 WORD count;

 for(count = 0; count < wLen; count++)
 {
 XEEWrite(cData[count]);

 MPFSStubs[hMPFS].addr++;
 MPFSStubs[hMPFS].bytesRem--;

 if(MPFSStubs[hMPFS].bytesRem == 0u)
 {
 MPFSPutEnd(FALSE);
 isMPFSLocked = TRUE;
 XEEBeginWrite(MPFSStubs[hMPFS].addr+MPFS_HEAD);
 MPFSStubs[hMPFS].bytesRem = MPFS_WRITE_PAGE_SIZE;
 }
 }

 return count;

 #else
 // Write to the SPI Flash
 SPIFlashWriteArray(cData, wLen);
 return wLen;
 #endif
}
#endif

/*****************************************************************************
 Function:
 void MPFSPutEnd(void)

 Description:
 Finalizes an MPFS writing operation.

 Precondition:
 MPFSFormat and MPFSPutArray were sucessfully called.

 Parameters:
 final - TRUE if the application is done writing, FALSE if MPFS2 called
 this function locally.

 Returns:
 None
 ***************************************************************************/
#if defined(MPFS_USE_EEPROM) || defined(MPFS_USE_SPI_FLASH)
void MPFSPutEnd(BOOL final)
{
 isMPFSLocked = FALSE;

 #if defined(MPFS_USE_EEPROM)
 XEEEndWrite();
 while(XEEIsBusy());
 #endif

 if(final)
 _ValidateExternal();
}
#endif

/****************************************************************************
 Section:
 Meta Data Accessors
 ***************************************************************************/

/*****************************************************************************
 Function:
 static void _LoadFATRecord(WORD fatID)

 Description:
 Loads the FAT record for a specified handle.

 Precondition:
 None

 Parameters:
 fatID - the ID of the file whose FAT is to be loaded

 Returns:
 None

 Remarks:
 The FAT record will be stored in fatCache.
 ***************************************************************************/
static void _LoadFATRecord(MPFS_HANDLE fatID)
{
 if(fatID == fatCacheID || (fatID > 0xffff && (fatID - INTERNALFATIDOFFSET) >= numFilesInternal) || (fatID <= 0xffff && fatID >= numFilesExternal) )
 return;

 // Read the FAT record to the cache
 MPFSStubs[0].bytesRem = 22;
 if (fatID > 0xffff)
 {
 MPFSStubs[0].addr = 8 + numFilesInternal*2 + (fatID - INTERNALFATIDOFFSET)*22;
 MPFSStubs[0].internal = 1;
 }
 else
 {
 MPFSStubs[0].addr = 8 + numFilesExternal*2 + fatID*22;
 MPFSStubs[0].internal = 0;
 }

 fatCacheID = fatID;

 MPFSGetArray(0, (BYTE*)&fatCache, 22); 

}

/*****************************************************************************
 Function:
 DWORD MPFSGetTimestamp(MPFS_HANDLE hMPFS)

 Description:
 Reads the timestamp for the specified file.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read the metadata

 Returns:
 The timestamp that was read as a DWORD
 ***************************************************************************/
DWORD MPFSGetTimestamp(MPFS_HANDLE hMPFS)
{
 // Make sure a valid file is open
 if(hMPFS > MAX_MPFS_HANDLES)
 return 0x00000000;
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 return 0x00000000;

 // Move to the point for reading
 _LoadFATRecord(MPFSStubs[hMPFS].fatID);
 return fatCache.timestamp;
}

/*****************************************************************************
 Function:
 DWORD MPFSGetMicrotime(MPFS_HANDLE hMPFS)

 Description:
 Reads the microtime portion of a file's timestamp.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read the metadata

 Returns:
 The microtime that was read as a DWORD
 ***************************************************************************/
DWORD MPFSGetMicrotime(MPFS_HANDLE hMPFS)
{
 // Make sure a valid file is open
 if(hMPFS > MAX_MPFS_HANDLES)
 return 0x00000000;
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 return 0x00000000;

 // Move to the point for reading
 _LoadFATRecord(MPFSStubs[hMPFS].fatID);
 return fatCache.microtime;
}

/*****************************************************************************
 Function:
 WORD MPFSGetFlags(MPFS_HANDLE hMPFS)

 Description:
 Reads a file's flags.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read the metadata

 Returns:
 The flags that were associated with the file
 ***************************************************************************/
WORD MPFSGetFlags(MPFS_HANDLE hMPFS)
{
 // Make sure a valid file is open
 if(hMPFS > MAX_MPFS_HANDLES)
 return 0x0000;
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 return 0x0000;

 //move to the point for reading
 _LoadFATRecord(MPFSStubs[hMPFS].fatID);
 return fatCache.flags;
}

/*****************************************************************************
 Function:
 DWORD MPFSGetSize(MPFS_HANDLE hMPFS)

 Description:
 Reads the size of a file.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read the metadata

 Returns:
 The size that was read as a DWORD
 ***************************************************************************/
DWORD MPFSGetSize(MPFS_HANDLE hMPFS)
{
 // Make sure a valid file is open
 if(hMPFS > MAX_MPFS_HANDLES)
 return 0x00000000;
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 return 0x00000000;

 // Move to the point for reading
 _LoadFATRecord(MPFSStubs[hMPFS].fatID);
 return fatCache.len;
}

/*****************************************************************************
 Function:
 DWORD MPFSGetBytesRem(MPFS_HANDLE hMPFS)

 Description:
 Determines how many bytes remain to be read.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read the metadata

 Returns:
 The number of bytes remaining in the file as a DWORD
 ***************************************************************************/
DWORD MPFSGetBytesRem(MPFS_HANDLE hMPFS)
{
 // Make sure a valid file is open
 if(hMPFS > MAX_MPFS_HANDLES)
 return 0x00000000;
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 return 0x00000000;

 return MPFSStubs[hMPFS].bytesRem;
}

/*****************************************************************************
 Function:
 MPFS_PTR MPFSGetStartAddr(MPFS_HANDLE hMPFS)

 Description:
 Reads the starting address of a file.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read the metadata

 Returns:
 The starting address of the file in the MPFS image
 ***************************************************************************/
MPFS_PTR MPFSGetStartAddr(MPFS_HANDLE hMPFS)
{
 // Make sure a valid file is open
 if(hMPFS > MAX_MPFS_HANDLES)
 return 0;
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 return MPFS_INVALID;

 // Move to the point for reading
 _LoadFATRecord(MPFSStubs[hMPFS].fatID);
 return fatCache.data;
}

/*****************************************************************************
 Function:
 MPFS_PTR MPFSGetEndAddr(MPFS_HANDLE hMPFS)

 Description:
 Determines the ending address of a file.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read the metadata

 Returns:
 The address just after the file ends (start address of next file)
 ***************************************************************************/
MPFS_PTR MPFSGetEndAddr(MPFS_HANDLE hMPFS)
{
 // Make sure a valid file is open
 if(hMPFS > MAX_MPFS_HANDLES)
 return MPFS_INVALID;
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 return MPFS_INVALID;

 // Move to the point for reading
 _LoadFATRecord(MPFSStubs[hMPFS].fatID);
 return fatCache.data + fatCache.len;
}

/*****************************************************************************
 Function:
 BOOL MPFSGetFilename(MPFS_HANDLE hMPFS, BYTE* cName, WORD wLen)

 Description:
 Reads the file name of a file that is already open.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to determine the file name
 cName - where to store the name of the file
 wLen - the maximum length of data to store in cName

 Return Values:
 TRUE - the file name was successfully located
 FALSE - the file handle provided is not currently open
 ***************************************************************************/
BOOL MPFSGetFilename(MPFS_HANDLE hMPFS, BYTE* cName, WORD wLen)
{
 DWORD addr;

 // Make sure a valid file is open
 if(hMPFS > MAX_MPFS_HANDLES)
 return FALSE;
 if(MPFSStubs[hMPFS].addr == MPFS_INVALID)
 return FALSE;

 // Move to the point for reading
 _LoadFATRecord(MPFSStubs[hMPFS].fatID);
 addr = fatCache.string;
 MPFSStubs[0].addr = addr;
 MPFSStubs[0].bytesRem = 255;

 // Read the value and return
 MPFSGetArray(0, cName, wLen);
 return TRUE;
}

/*****************************************************************************
 Function:
 DWORD MPFSGetPosition(MPFS_HANDLE hMPFS)

 Description:
 Determines the current position in the file

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle for which to determine position

 Returns:
 The position in the file as a DWORD (or MPFS_PTR)

 Remarks:
 Calling MPFSSeek(hMPFS, pos, MPFS_SEEK_START) will return the pointer
 to this position at a later time.  (Where pos is the value returned by
 this function.)
 ***************************************************************************/
DWORD MPFSGetPosition(MPFS_HANDLE hMPFS)
{
 return MPFSStubs[hMPFS].addr - MPFSGetStartAddr(hMPFS);
}

/*****************************************************************************
 Function:
 WORD MPFSGetID(MPFS_HANDLE hMPFS)

 Description:
 Determines the ID in the FAT for a file.

 Precondition:
 The file handle referenced by hMPFS is already open.

 Parameters:
 hMPFS - the file handle from which to read the metadata

 Returns:
 The ID in the FAT for this file

 Remarks:
 Use this function in association with MPFSOpenID to quickly access file
 without permanently reserving a file handle.
 ***************************************************************************/
MPFS_HANDLE MPFSGetID(MPFS_HANDLE hMPFS)
{
 return MPFSStubs[hMPFS].fatID;
}

/****************************************************************************
 Section:
 Utility Functions
 ***************************************************************************/

/*****************************************************************************
 Function:
 void _Validate(void)

 Summary:
 Validates the MPFS Image

 Description:
 Verifies that the MPFS image is valid, and reads the number of
 available files from the image header.  This function is called on
 boot, and again after any image is written.

 Precondition:
 None

 Parameters:
 None

 Returns:
 None
 ***************************************************************************/
static void _ValidateExternal(void)
{
 // If this function causes an Address Error Exception on 16-bit
 // platforms with code stored in internal Flash, make sure your
 // compiler memory model settings are correct.
 //
 // In MPLAB, choose Project Menu > Build Options > Project.
 // Select the MPLAB C30 tab and change Cagetory to Memory Model.
 // Ensure that Large Code Model is selected, and that the remaining
 //   options are set to Default.

 // Validate the image and update numFiles
 MPFSStubs[0].addr = 0;
 MPFSStubs[0].bytesRem = 8;
 MPFSStubs[0].internal = 0;
 MPFSGetArray(0, (BYTE*)&fatCache, 6);
 if(!memcmppgm2ram((void*)&fatCache, (ROM void*)"MPFS\x02\x01", 6))
 MPFSGetArray(0, (BYTE*)&numFilesExternal, 2);
 else
 numFilesExternal = 0;
 fatCacheID = MPFS_INVALID_FAT;
}    

static void _ValidateInternal(void)
{
 // If this function causes an Address Error Exception on 16-bit
 // platforms with code stored in internal Flash, make sure your
 // compiler memory model settings are correct.
 //
 // In MPLAB, choose Project Menu > Build Options > Project.
 // Select the MPLAB C30 tab and change Cagetory to Memory Model.
 // Ensure that Large Code Model is selected, and that the remaining
 //   options are set to Default.

 // Validate the image and update numFiles
 MPFSStubs[0].addr = 0;
 MPFSStubs[0].bytesRem = 8;
 MPFSStubs[0].internal = 1;
 MPFSGetArray(0, (BYTE*)&fatCache, 6);
 if(!memcmppgm2ram((void*)&fatCache, (ROM void*)"MPFS\x02\x01", 6))
 MPFSGetArray(0, (BYTE*)&numFilesInternal, 2);
 else
 numFilesInternal = 0;
 fatCacheID = MPFS_INVALID_FAT;
}
#endif //#if defined(STACK_USE_MPFS2)

Include an inline MPFS2.c image

The above changes will fail to compile unless we include an inline MPFS2.c image

Use the MPFS2.exe utility to create the MPFS2 file system image that will be contained in your target processors program ROM.  ie MPFSImg2.c

Rename it as appropriate.  In this example our goal is to embed JQuery Javascript library & associated JQueryUI user interface library on the target device’s ROM, so we have renamed our newly created MPFS2 file system image MPFS2JQuery.c

Include this MPFS2 file system image .c file in your project and edit it changing (or just remove it):

#if !defined(MPFS_USE_EEPROM) && !defined(MPFS_USE_SPI_FLASH)

to

#if defined(MPFS_USE_EEPROM) || defined(MPFS_USE_SPI_FLASH)

Updated 20101120