The purpose of this application is to allow easy manipulation of the QSPI file system from a PC

Dependencies:   EALib USBDevice mbed

The purpose of this application is to allow easy manipulation of the QSPI file system from a PC.

The application makes the LPC4088 QuickStart Board appear as a USB Memory Stick when connected to a PC. The PC will see the current content of the QSPI file system plus an image file of the file system that can be downloaded and (at a later time) be used to restore the file system to it's current state.

To use this application:

  1. Make sure that the QSPI file system has been formatted (using either the app_qspi_format application or one of the erase.* images).
  2. Download the app_qspifs_memstick application using drag-n-drop and then reset the board
  3. Optionally start a terminal program to read the status messages from the application
  4. Connect a USB cable to the micro USB slot on the back of the LPC4088 QuickStart Board, underneath the ethernet connector, and then to the PC
  5. The PC will install drivers if needed and then the USB Memory Stick will be available as a new drive
  6. Modify the file system to suit your needs
  7. With the USB cable still connected, press the button on the LPC4088 QuickStart Board
  8. The application will now:
    1. disconnect the USB Memory Stick
    2. write all changes to the QSPI flash memory
    3. create a new image file of the updated QSPI file system and store it in the .current/ folder
    4. connect the USB Memory Stick again
  9. Continue from step 6. until satisfied

Note 1: The file system that is exposed is a copy (in SDRAM) of the QSPI file system. The reason for this is that the USBMSD class requires a FAT file system.

Note 2: The image files created in step 8.3 above will be a *.fsX file (where the 'X' is the size of the file system in MB so *.fs1 for a 1MByte file system). The *.fsX file extensions are recognized by the HDK and can be used to drag-n-drop to the MBED drive in the same way as the *.bin files are. A *.fsX file will not overwrite the program stored in internal flash.

main.cpp

Committer:
embeddedartists
Date:
2014-08-26
Revision:
2:5a954ee65b33
Parent:
0:bd0d999bb6fb

File content as of revision 2:5a954ee65b33:

/******************************************************************************
 * Includes
 *****************************************************************************/
 
#include "mbed.h"

#include "QSPIFileSystem.h"

#include "USBMSD_RAMFS.h"
#include "RAMFileSystem.h"
#include "sdram.h"
#include "crc.h"

/******************************************************************************
 * Typedefs and defines
 *****************************************************************************/

typedef bool (*syncFunc)(const char* name, bool isDir);

#define RAM_FS_SIZE  (20*1024*1024)  //20MB

/******************************************************************************
 * Local variables
 *****************************************************************************/

QSPIFileSystem qspi("qspi");
//RAMFileSystem ramfs(0xA0000000, 20*1024*1024, "ram");
//USBMSD_RAMFS usbmsd(&ramfs);

DigitalOut myled1(LED1);
DigitalOut myled2(LED2);
DigitalIn  button(p23);

DigitalOut myled(LED1);
static char lsbuff[NAME_MAX+1];

static char image_file_name[128] = { '\0' };

/******************************************************************************
 * Local functions
 *****************************************************************************/

static void ledShowProgress()
{
  static int state = 0;
  state = (state + 1) % 2;
  switch (state)
  {
    case 0:
      myled1 = 1;
      myled2 = 0;
      break;

    case 1:
    default:
      myled1 = 0;
      myled2 = 1;
  }
}

static void handleError(const char* msg)
{
  printf(msg);
  while(true) {
    myled1 = 1;
    myled2 = 1;
    wait(0.3);
    myled1 = 0;
    myled2 = 0;
    wait(0.3);
  }
}

static void waitForButtonPress()
{
  printf("Press button to sync file systems\n");

  myled1 = 1;
  myled2 = 1;
  while(button.read() == 1) {
    wait(0.1);
  }
  myled1 = 0;
  printf("Button pressed, now release it\n");
  while(button.read() == 0) {
    wait(0.1);
  }
}

static void addNotFormattedFile()
{
    FILE *fp = fopen("/ram/qspi_not_formatted.txt", "w");
    if (fp != NULL) {
        fprintf(fp, "The QSPI file system has not be formatted and this program can't do it.\n");
        fprintf(fp, "Format the QSPI file system and then run this program again!\n");
        fclose(fp);
    }
}

static bool recursiveProcessFS(char* buff, const char* name, syncFunc func, bool adding)
{
  uint32_t len = strlen(buff);
  if (len > 0) {
    if (buff[len - 1] != '/') {
      buff[len++] = '/';
      buff[len] = '\0';
    }
  }
  strcat(buff, name);
  len += strlen(name);

  DIR *d = opendir(buff);
  bool result = true; // success
  if (d != NULL) {
    if (adding) {
      // when processing in adding mode folders must be created before it's content
      result = func(buff, true);
    }
    struct dirent *p;
    while (result && ((p = readdir(d)) != NULL)) {
      result = recursiveProcessFS(buff, p->d_name, func, adding);
      buff[len] = '\0';
    }
    closedir(d);
    if (result && !adding) {
      // when processing in removing mode folders must be deleted after it's content
      result = func(buff, true);
    }
  } else {
    // a file
    result = func(buff, false);
  }
  return result;
}

static uint32_t fileLen(FILE* f)
{
  uint32_t pos = ftell(f);
  fseek(f, 0, SEEK_END);
  uint32_t size = ftell(f);
  fseek(f, pos, SEEK_SET);
  return size;
}

static bool copy(FILE* fSrc, FILE* fDst)
{
  char buff[512];
  uint32_t left = fileLen(fSrc);
  uint32_t num;
  uint32_t chunk;

  fseek(fSrc, 0, SEEK_SET);
  do {
    chunk = (left < 512) ? left : 512;
    num = fread(buff, 1, chunk, fSrc);
    if (num > 0) {
      uint32_t tmp = fwrite(buff, 1, num, fDst);
      if (tmp != num) {
        // failed to write
        return false;
      }
      left -= num;
      ledShowProgress();
    }
  } while(num > 0 && left > 0);

  // copied entire file
  return true;
}

static bool identicalFiles(const char* srcName, const char* dstName)
{
  FILE* fSrc = NULL;
  FILE* fDst = NULL;
  bool identical = false;
  do
  {
    fSrc = fopen(srcName, "r");
    if (fSrc == NULL) {
      break;
    }
    fDst = fopen(dstName, "r");
    if (fDst == NULL) {
      break;
    }
    if (fileLen(fSrc) != fileLen(fDst)) {
      break;
    }
    if (crc_Read(fSrc) != crc_Read(fDst)) {
      break;
    }

    // All tests passed so the files are identical
    identical = true;

  } while(false);

  if (fSrc != NULL) {
    fclose(fSrc);
  }
  if (fDst != NULL) {
    fclose(fDst);
  }

  return identical;
}

static bool addExisting(const char* srcName, bool isDir, const char* dstName)
{
  bool result = true; // success
  if (isDir) {
    DIR *d = opendir(dstName);
    if (d == NULL) {
      if (dstName[1] != 'r') { printf("%s, new dir, adding\n", dstName); }
      if (mkdir(dstName, 0) != 0) {
        printf("Failed to create folder %s\n", dstName);
        result = false; // dir did not exist and could not be created
      }
    } else {
      closedir(d);
    }
  } else if (!identicalFiles(srcName, dstName)) {
    // Compare the files to avoid replacing with same
    FILE* fSrc = fopen(srcName, "r");
    if (fSrc != NULL) {
      FILE* fDst = fopen(dstName, "w"); // open and truncate
      if (fDst != NULL) {
        if (dstName[1] != 'r') { printf("%s, changed, updating\n", dstName); }
        result = copy(fSrc, fDst);
        if (!result) {
          printf("Failed to copy %s to %s\n", srcName, dstName);
        }
        fclose(fDst);
      } else {
        printf("Failed to create file %s\n", dstName);
        result = false; // unable to create file
      }
      fclose(fSrc);
    } else {
      printf("Failed to copen source file file %s\n", srcName);
      result = false; // unable to open source
    }
  } else {
    if (dstName[1] != 'r') { printf("%s identical, skipping\n", dstName); }
  }
  return result;
}

static bool addExistingToQspi(const char* name, bool isDir)
{
  // create the target file name by replacing /ram/ with /qspi/
  char buff[256];
  buff[0] = '\0';
  strcat(buff, "/qspi/");
  strcat(buff, name+5);

  // Don't add the file created by createImageFile()
  if (strcmp(name, image_file_name) == 0) {
    return true;
  }

  return addExisting(name, isDir, buff);
}

static bool addExistingToRAM(const char* name, bool isDir)
{
  // create the target file name by replacing /qspi/ with /ram/
  char buff[256];
  buff[0] = '\0';
  strcat(buff, "/ram/");
  strcat(buff, name+6);
  return addExisting(name, isDir, buff);
}

static bool removeIfMissing(const char* toLookFor, const char* toRemove, bool isDir)
{
  int result = 0; //success
  if (isDir) {
    DIR *d = opendir(toLookFor);
    if (d == NULL) {
      // dir doesn't exist => delete it
      if (toRemove[1] != 'r') { printf("%s, missing, deleting dir\n", toRemove); }
      result = remove(toRemove);
      ledShowProgress();
    } else {
      // dir exist => don't delete
      closedir(d);
    }
  } else {
    FILE* f = fopen(toLookFor, "r");
    if (f == NULL) {
      // file doesn't exist => delete it
      if (toRemove[1] != 'r') { printf("%s, missing, deleting file\n", toRemove); }
      result = remove(toRemove);
      ledShowProgress();
    } else {
      // file exist => don't delete
      fclose(f);
    }
  }
  return (result == 0);
}

static bool removeMissingFromQspi(const char* name, bool isDir)
{
  // create the target file name by replacing /qspi/ with /ram/
  char buff[256];
  buff[0] = '\0';
  strcat(buff, "/ram/");
  strcat(buff, name+6);
  removeIfMissing(buff, name, isDir);
  return true;
}

static bool removeMissingFromRAM(const char* name, bool isDir)
{
  // create the target file name by replacing /ram/ with /qspi/
  char buff[256];
  buff[0] = '\0';
  strcat(buff, "/qspi/");
  strcat(buff, name+5);

  // Don't remove the file created by createImageFile()
  if (strcmp(name, image_file_name) == 0) {
    return true;
  }

  removeIfMissing(buff, name, isDir);
  return true;
}

static void syncDir(const char* to, const char* from)
{
  printf("Starting to sync %s on top of %s (This may take time. LED1 & 2 blink for each file)\n", from, to);

  char* buff = (char*)malloc(512);
  if (buff != NULL)
  {
    buff[0] = '\0';
    if (strcmp(to, "/qspi/") == 0) {
      if (!recursiveProcessFS(buff, to, removeMissingFromQspi, false)) {
        printf("Failed to remove files from %s that were missing on %s\n", to, from);
      } else {
        buff[0] = '\0';
        if (!recursiveProcessFS(buff, from, addExistingToQspi, true)) {
          printf("Failed to add files to %s that existed on %s\n", to, from);
        }
      }
    } else {
      if (!recursiveProcessFS(buff, to, removeMissingFromRAM, false)) {
        printf("Failed to remove files from %s that were missing on %s\n", to, from);
      } else {
        buff[0] = '\0';
        if (!recursiveProcessFS(buff, from, addExistingToRAM, true)) {
          printf("Failed to add files to %s that existed on %s\n", to, from);
        }
      }
    }
    free(buff);
  }
  printf("Sync completed\n");
}


static void createImageFile()
{
  uint32_t startAddr;
  uint32_t endAddr;
  uint32_t size;

  printf("Creating image of existing (if any) QSPI file system\n");

  if (!qspi.getMemoryBoundaries(&startAddr, &endAddr))
  {
    handleError("QSPI FS not formatted or impossible to determine it's size\n");
  }

  // Align the start address to an even multiple of 1MB
  startAddr = startAddr & 0xfff00000;

  // Update the file to match the size of the file system
  size = endAddr - startAddr;
  if ((size < 0x00100000) || (size > 0x00800000) || ((size & 0xfffff) > 0))
  {
    sprintf(lsbuff, "QSPI FS size is not supported (%u bytes)\n", size);
    handleError(lsbuff);
  }
  sprintf(image_file_name, "/ram/.current/fs_image.fs%d", (size >> 20));
  
  // NOTE: The line below is very very !!!! important. For some weird reason the
  //       RAM file system must have at least one folder on it before USB is connected.
  //       If the RAM file system doesn't have any folders on it the result is that
  //       the content of the RAM file system will be the same after USB is disconnected
  //       as it was before connecting - regardless of what is added. Very strange indeed.
  mkdir("/ram/.current", 0);

  printf("QSPI FS max size is %d MB\n", (size >> 20));

  FILE *fp = fopen(image_file_name, "w");
  if (fp != NULL)
  {
    while (size > 0)
    {
      uint32_t written = fwrite((char*)(endAddr - size), 1, size, fp);
      size -= written;
      if (written == 0)
      {
        handleError("Failed to create QSPI image file\n");
      }
    }
    fclose(fp);
  }
}

static bool list(const char* name, bool isDir)
{
  if (isDir) {
    printf("d:         %s\n", name);
  } else {
    FILE* f = fopen(name, "r");
    if (f != NULL) {
      uint32_t len = fileLen(f);
      printf("f: %7u %s\n", len, name);
      fclose(f);
    } else {
      printf("f:     ??? %s\n", name);
    }
  }
  return true;
}

static void recursiveList(const char* dirname)
{
  printf("\nRecursive list of file and folders in %s\n", dirname);
  char* buff = (char*)malloc(512);
  if (buff != NULL)
  {
    buff[0] = '\0';
    recursiveProcessFS(buff, dirname, list, true);
    free(buff);
  }
}

/******************************************************************************
 * Main function
 *****************************************************************************/
int main()
{
  printf("\n-----------------\n\nWelcome to the QSPI file system tool...\n");

  // 1) Make sure that the button works
  // 2) Init SDRAM and allocate space for the RAM file system
  // 3) Setup RAM FS
  // 4a) If QSPI FS is not formatted:
  //    i) Create a "qspi_not_formatted.txt" file in the root of the file system
  // 4b) QSPI FS formatted
  //    i) Create an image file of the QSPI FS
  //    ii) Sync QSPI FS on top of RAM FS
  // 5) Connect USB
  // 6a) If QSPI FS is not formatted: Loop forever doing nothing
  // 6b) QSPI FS formatted: Wait for button press
  // 7) Button pressed, Disconnect USB
  // 8) QSPI formatted
  //    i) Sync RAM FS on top of QSPI FS
  //    ii) Goto 3)
  //
  
  // 1)
  button.mode(PullUp);

  // 2)
  if (sdram_init()) {
    handleError("Failed to initialize SDRAM\n");
  }
  
  void* fsmem = malloc(RAM_FS_SIZE);
  if (fsmem == NULL) {
    handleError("Failed to allocate memory for RAM file system\n");
  }
  RAMFileSystem ramfs((uint32_t)fsmem, RAM_FS_SIZE, "ram");
  USBMSD_RAMFS usbmsd(&ramfs);
  
  while(true)
  {
    // 3)
    ramfs.format();    

    // 4a)
    bool qspiFormatted = qspi.isformatted();
    if (!qspiFormatted)
    {
      addNotFormattedFile();
    }

    // 4b)
    else
    {
      //addTestFile();
      createImageFile();

      // Copy QSPI FS to RAM FS
      syncDir("/ram/", "/qspi/");
    }
        
    // 5)
    printf("Insert the USB cable!\n");
    printf("Starting USB...\n");
    for (int i = 0; i < 10; i++)
    {
      if (usbmsd.connect())
      {
        printf("Connected!\n");
        break;
      }
      printf("Failed to connect USB, testing again...\n");
      printf("Insert (or remove and then insert) the USB cable!\n");
      wait(1);
    }

    // 6b)
    if (qspiFormatted) 
    {
      waitForButtonPress();
    }
    else
    {
      // 6a) no point in waiting for buttons if no file system
      while (1) {};
    }

    // 7)
    usbmsd.disconnect();
    printf("Disconnected!\n");

    // 8) Copy RAM FS to QSPI FS
    recursiveList("/ram/");
    syncDir("/qspi/", "/ram/");
  }
}