MMEx with SPI Slave to allow legacy devices to communicate with modern media such as USB, SD cards, the internet and all of the mbed\'s other interfaces

Dependencies:   NetServices MSCUsbHost mbed TMP102 SDFileSystem

ffuncs.cpp

Committer:
DeMein
Date:
2011-02-27
Revision:
0:67a55a82ce06

File content as of revision 0:67a55a82ce06:

/* MMEx for MBED - File I/O Command processing
 * Copyright (c) 2011 MK
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
  \file ffuncs.cpp
  \brief Commands starting with F for File I/O
*/

#include "ffuncs.h"

FILE *fp1; 
SDFileSystem sd(p5, p6, p7, p8, fs_sd);
MSCFileSystem msc(fs_usb);
LocalFileSystem local(fs_local);

// directory handles
FATFS_DIR dj;
DIR *d;  
char odirectory = NULL;   // keeps track of which directory was opened

fhandle handles[10]; 

/** initialize file handles on startup
 *
 */
void init_handles() {
  // initialize the file handles  
  for (int i = 0; i < 10; i++) {
    // handles[i].filename = NULL;
    // handles[i].medium   = NULL;
    // handles[i].fsp      = NULL;
    // handles[i].direct   = NULL;
    // handles[i].filemode = NULL;
    // handles[i].fullpath = NULL;
    handles[i].fp       = NULL;     // we only use this to test
  }
}

/** main entry for parsing F-commands
 *
 */
void parse_F() {
  err_num = noerror;
  DBG_msg("parse_F", inbuf);
 
  wipesp(inbuf);           // first remove space from string

  if (inbuf[1] != NULL) {
    switch (inbuf[1]) {
    case fmount   : do_fmount();        // mount storage medium
                    break;       
    case funmount : do_funmount();      // unmount storage medium
                    break;         
    case ffopen   : do_ffopen();        // open file
                    break;
    case ffread   : do_ffread();        // read from file
                    break;
    case ffwrite  : do_ffwrite();       // write to file
                    break;
    case ffclose  : do_ffclose();       // close file
                    break;
    case ffseek   : do_ffseek();        // seek to file position
                    break;
    case fflush   : do_fflush();        // flush file        
                    break;
    case fdir     : do_fdir();          // get first directory entry
                    break;
    case fndir    : do_fndir();         // get next directory entry    
                    break;
    case fftell   : do_fftell();        // get next directory entry    
                    break;                    
    case ffsize   : do_ffsize();        // get next directory entry    
                    break;                    
    default       : do_fdefault();      // command not recognized
                    break;
    }
  } else {
    do_fdefault();                      // command not recognized
  }
}

/** mount storage device
 *
 *  syntax: FM[medium]<CR>
 * \n [medium]: U, S, L
 */
void do_fmount() {
  int rslt = 0;
  
  DBG_msg("do_fmount", inbuf);
  if (inbuf[2] != NULL) {
    switch (inbuf[2]) {
      case f_sdcard : DBG_msg("do_fmount", "SD, 1st init");                 
                      rslt = sd.disk_initialize();             //initialize file system   
                      DBG_msg("do_fmount", "SD, 2nd init");
                      rslt = sd.disk_initialize();             //initialize file system 
                      break;                      
      case f_local  : // mount local flash, nothing really to do, it is always there                     
                      break;                      
      case f_usb    : rslt = msc.disk_initialize();             // enumerate USB device    
                      break;                                            
      default       : do_fdefault();                     // command not recognized
                      break;    
    }     
  } else {
    do_fdefault();      
  }
  if (rslt != noerror) {              // could not mount SD or USB device
    DBG_int("device not mounted", rslt); 
    send_error(err_not_mounted);
  }
}

/** unmount storage device
 *
 *  syntax: FU[medium]<CR>
 * \n         [medium]: U, S, L
 */
void do_funmount() {
  // unmount storage device
  int rslt = 0;
  DBG_msg("do_funmount", inbuf);
 
  if (inbuf[2] != NULL) {
    switch (inbuf[2]) {
      case f_sdcard : rslt = sd.disk_sync();
                      break;                      
      case f_local  : // sync not needed, class does not have this functions                    
                      break;                      
      case f_usb    : rslt = msc.disk_sync();            // enumerate USB device    
                      break;                                            
      default       : do_fdefault();                     // command not recognized
                      break;    
    }     
  } else {
    DBG_msg("do_funmount", "argument error");      
    do_fdefault(); 
  }
  if (rslt != noerror) {              // could not mount SD or USB device
                                      // will never see this message
    DBG_int("device not unmounted", rslt); 
    send_error(err_not_mounted);
  }
}

/** open file on storage device
 *
 *  syntax: FO [handle] [mode] [medium] [name]
 * \n          [handle]: 0..9
 * \n          [mode]  : R, W, A 
 * \n          [medium]: U, S, L
 * \n          [name]  : filename in 8.3 format
 * \n anything after FO may be replaced by a Parameter when %n is present
 */
void do_ffopen() {
  int han = 0;            // variable for handle
  int i = 0;
  int pos = 0;
  int pnum = 0;
  unsigned char c;
  char * cstr;
    
  wipesp(inbuf);
   
  DBG_msg("do_ffopen", inbuf);  
  
  // check for a % sign in inbuf
  pos = 2;
  while ((inbuf[pos] != NULL) && (inbuf[pos] != c_percent)) pos++;
  
  if ((inbuf[pos] != NULL) && ((pnum = getpnum(inbuf[pos + 1])) >= 0)) {
    // found and valid parameter, now insert
    cstr = new char [param[pnum].size() + 1];
    strcpy (cstr, param[pnum].c_str());
    DBG_msg("param", cstr);
    DBG_int("pos", pos);
    remchar(inbuf, pos, 2);       // remove %n, works better for insert
    DBG_msg("do_ffopen", inbuf); 
    insertstr(inbuf, cstr, pos);  // insert
    DBG_msg("do_ffopen", inbuf); 
    delete[] cstr;
  }
    
  // check the arguments
  c = inbuf[2];             // [handle]
  if ((c != NULL) && (c >= '0') && (c <= '9')) {
    // [handle] in range
    han = c - '0';
  } else {
    do_fdefault();
    return;          // terminate this function
  }
  
  if (handles[han].fp != NULL) {        // file handle in use!
    DBG_msg("do_ffopen", "file handle in use");
    send_error(err_handle_used);
    return;
  }
  
  c = inbuf[4];            // [medium]
  if (c != NULL) {
    switch (c) {
      case f_sdcard : handles[han].medium = f_sdcard;                                       
                      strcpy(handles[han].fsp, fsp_sd);
                      break;                      
      case f_local  : handles[han].medium = f_local;
                      strcpy(handles[han].fsp, fsp_local);
                      break;                      
      case f_usb    : handles[han].medium = f_usb;
                      strcpy(handles[han].fsp, fsp_usb);
                      break;                                            
      default       : do_fdefault();                            // command not recognized
                      break;    
    } 
  } else {
    do_fdefault();
    return;        // terminate this function  
  } 

  c = inbuf[3];       // get filemode
  if (c != NULL) {
    switch (c) {
      case 'W' : handles[han].filemode = f_modew;
                 break;
      case 'R' : handles[han].filemode = f_moder;
                 break; 
      case 'A' : handles[han].filemode = f_modea;
                 break;
      default  : do_fdefault();                    // command not recognized
                 break;
    }
  } else {
    do_fdefault();
    return;          // terminate this function
  }
  
  // now get filename
  i = 0;
  while ((inbuf[i + 5] != NULL) && (i <= 13)) {
    // will not check until end of name, take max 12 chars
    handles[han].filename[i] = inbuf[i + 5];
    i++;
    handles[han].filename[i] = NULL;  // guarantee null terminated string
  }
 
  // create full pathname
  strcpy(handles[han].fullpath, handles[han].fsp);
  strcat(handles[han].fullpath, handles[han].filename);
  char tmp[5]; 
  sprintf(tmp,"%c", handles[han].filemode);
    
  DBG_msg("filename:", handles[han].filename);
  DBG_msg("path    :", handles[han].fullpath);
  DBG_int("handle# :", han);
  DBG_chr("mode    :", handles[han].filemode);
  DBG_chr("medium  :", handles[han].medium);
    
  // now open file  
  handles[han].fp = fopen(handles[han].fullpath,tmp);
  if (handles[han].fp != NULL) {
    DBG_msg("do_ffopen", "file opened");
  } else {
    DBG_msg("do_ffopen", "file NOT opened");
    send_error(err_filenotopen);
  }  
}

/** read file contents
 *
 *  syntax: FR [handle] [value]
 *  \n         [handle]: 0..9
 *  \n         [value] : number of byte to read
 *  \n when [value]=0 or omitted will read until <EOF>, 
 *  reading can be interrupted by sending >F or >I
 */
void do_ffread() {
  long int numbytes = 0;
  long int count = 0;
  int i, m;
  int han;
  bool go = true;
  bool stopread = false;
  
  DBG_msg("do_ffread", inbuf);  
  
  rx_max = 0;
  tx_max = 0;
  
  int c = inbuf[2];            // [handle]
  if ((c != NULL) && (c >= '0') && (c <= '9')) {
    // [handle] in range
    han = c - '0';
  } else {
    do_fdefault();
    return;           // exit with error
  }
  DBG_int("handle :", han);
  if (handles[han].fp == NULL) {     // file handle not in use!
    DBG_msg("do_ffread", "no valid file handle");
    send_error(err_no_handle);
    return;
  }   
  
  count = 0;
  // next chars on the commandline is the number of bytes to read
  if (inbuf[3] == NULL) {
    // no value on the command line
    numbytes = 0;
  } else {
    // now process line
    for (i = 3; ((inbuf[i] >='0') && (inbuf[i] <= '9')); i++ ) {
      numbytes = 10 * numbytes + (inbuf[i] - '0');
    }
  }
  DBG_int("do_ffread bytes:", numbytes);
  
  // now ready to really do the read
  // must monitor input buffer to check if we need to stop!  
  while (go) {       
    m = mldl.rx_use();
    if (m > rx_max) rx_max = m;
    m = mldl.tx_use();
    if (m > tx_max) tx_max = m;    
    
    c = getc(handles[han].fp);     // read one byte
    // remove comment below to list all bytes read
    DBG_chr("do_ffread: ", c);         
    if (c == EOF) {
      // EOF found, send <EOF> sequence
      mldl.tx_add(c_escape);
      mldl.tx_add(c_eof); 
      DBG_int("do_ffread: EOF at byte ", count);
    } else {
      // no problem, just send data
      mldl.tx_addp(c);
      count++;      // increase counter  
    }               
    // now check if we have to stop?    
    if (!mldl.rx_empty()) {
      stopread = (mldl.rxx_read(data_mode) < 0);   // intentional stop of read
    }    
    go = !((c == EOF) || (count == numbytes) || stopread);
  }    
  
  // in case the complete file is read, now wait until buffer is emptied  
  while (!stopread && !mldl.tx_empty()) {
    // keep checking for EOF
    if (!mldl.rx_empty()) {
      stopread = (mldl.rxx_read(data_mode) < 0);
    }
  }
  
  if (stopread) {
    DBG_msg("do_ffread", "forced stop read received");
    mldl.flush_tx();    // empty transmit buffer
  }
  
  DBG_int("do_ffread: end read at byte ", count);
  DBG_int("rx_max", rx_max);
  DBG_int("tx_max", tx_max); 
}

/** write to file
 *
 *  syntax: FW [handle]
 *  \n         [handle]: 0..9
 *  \ wait for prompt, then send data to be written. 
 *  End writing by sending >F
 */
void do_ffwrite() {
  int han, m;
  bool go = true;
  int count = 0;
  
  DBG_msg("do_ffwrite", inbuf);  
  int c = inbuf[2];            // [handle]
  if ((c != NULL) && (c >= '0') && (c <= '9')) {
    // [handle] in range
    han = c - '0';
  } else {
    do_fdefault();
    return;           // check how to exit with error
  }
  DBG_int("handle :", han);
  if (handles[han].fp == NULL) {     // file handle not in use!
    DBG_msg("do_ffwrite", "no valid file handle");
    send_error(err_no_handle);
    return;
  }   
  send_prompt();              // now send a prompt and we are ready to go
  while (go) {                  // as long as we can continue   
    m = mldl.rx_use();
    if (m > rx_max) rx_max = m;
    m = mldl.tx_use();
    if (m > tx_max) tx_max = m;                     
    c = mldl.rxx_read(data_mode);             // get data     
    if ((c == -1) || (c == -2)) go = false;   // <EOF> or interrupt 
    if (go) {
      // remove comment below to list all bytes written
      DBG_chr("fwrite", c);
      count++;

      fputc(c, handles[han].fp);      
    } else {
      DBG_int("fwrite: ", c);
    }
  }
  DBG_int("fwrite written", count);
  DBG_int("rx_max", rx_max);
  DBG_int("tx_max", tx_max); 
}

/** close file
 *
 *  syntax: FC [handle]
 *  \n         [handle]: 0..9
 */
void do_ffclose() {
  int han = 0;
  int rslt = 0;
  DBG_msg("do_ffclose", inbuf);
 
  char c = inbuf[2];      // file handle should be here
  if ((c != NULL) && (c >= '0') && (c <= '9')) {
    // [handle] in range
    han = c - '0';
  } else {
    do_fdefault();
    return;           // check how to exit with error
  }    
  if (handles[han].fp == NULL) {     // file handle not in use!
    DBG_msg("do_ffclose", "no valid file handle");
    send_error(err_no_handle);
    return;
  }        
  rslt = fclose(handles[han].fp);
  handles[han].fp = NULL;
  DBG_int("do_ffclose result", rslt);  
}

/** seek to file position
 *
 *  syntax: FS [handle] [value]
 *  \n         [handle]: 0..9
 *  \n         [value] : new position
 *  \n if [value] is ommitted, will assume 0, 
 *  will always seek relative to the start of the file
 */
void do_ffseek() {
  DBG_msg("do_ffseek", inbuf);
 
  long int position = 0;  // max value is 2147483647
  int i;
  int han;
  
  int c = inbuf[2];            // [handle]
  if ((c != NULL) && (c >= '0') && (c <= '9')) {
    // [handle] in range
    han = c - '0';
  } else {
    do_fdefault();
    return;           // check how to exit with error
  }
  DBG_int("handle :", han);
  if (handles[han].fp == NULL) {     // file handle not in use!
    DBG_msg("do_ffseek", "no valid file handle");
    send_error(err_no_handle);
    return;
  }  
  
   // next chars on the commandline is the number of bytes to read
  if (inbuf[3] == NULL) {
    // no value on the command line
    position = 0;
  } else {
    // now process line
    for (i = 3; ((inbuf[i] >='0') && (inbuf[i] <= '9')); i++ ) {
      position = 10 * position + (inbuf[i] - '0');
    }
  }
  
  if (fseek(handles[han].fp, position, SEEK_SET) < 0) {
    // error in fseek
    send_error(err_seek);
    return;
  }
}

/** flush file
 *
 *  syntax: FF [handle]
 *  \n         [handle]: 0..9
 *  \n fflush is not implemented in the file systems
 */
void do_fflush() {
  DBG_msg("do_fflush", inbuf);
}  


/** open directory and list first entry
 *
 *  syntax: FD [medium]
 *  \n         [medium]: U, S, L
 *  \n returns the first directory item, 
 *  subdirectories are not yet supported,
 *  if the listing is empty >F is returned
 */
void do_fdir() {
  FILINFO finfo;
  FRESULT res ;
  struct dirent *p;
  char tmp[25]; 
  
  DBG_msg("do_ffdir", inbuf);
        
  if (inbuf[2] != NULL) {
    switch (inbuf[2]) {
      case f_sdcard : res = f_opendir(&dj, "0:/");
                      odirectory = f_sdcard; 
                      break;                      
      case f_local  : d = opendir(rt_local);
                      odirectory = f_local;                      
                      break;                      
      case f_usb    : res = f_opendir(&dj, "1:/");
                      odirectory = f_usb;
                      break;                                            
      default       : do_fdefault();    // command not recognized
                      break;    
    }     
  } else {
    do_fdefault();      
    return;
  }
   
  if (odirectory == f_local) {
    // get listing from local flash
    if (d != NULL) { 
      if ((p = readdir(d)) != NULL) {
        sprintf(tmp,"%s", p->d_name);      
        DBG_msg("do_ffdir", tmp);
        upstring(tmp);
        mldl.tx_string(tmp);
      } else {
        DBG_msg("do_ffdir", "Last directory entry");             
        mldl.tx_add(c_escape);    // send <EOF> sequence
        mldl.tx_add(c_eof);
      }
    } else {
      // could not open directory  
      DBG_msg("do_ffdir", "Could not open directory");
      send_error(err_directory);
    }
  } else {
    // get listing from USB or SD card
    
    /* File attribute bits for directory entry  */
    // #define AM_RDO    0x01    /* Read only   */
    // #define AM_HID    0x02    /* Hidden      */
    // #define AM_SYS    0x04    /* System      */
    // #define AM_VOL    0x08    /* Volume label */
    // #define AM_LFN    0x0F    /* LFN entry   */
    // #define AM_DIR    0x10    /* Directory   */
    // #define AM_ARC    0x20    /* Archive     */
    
    if (res == FR_OK) {
      res = f_readdir(&dj, &finfo);
      if ((res == FR_OK) && finfo.fname[0] != 0) {
        if ((finfo.fattrib & AM_DIR) == AM_DIR) {
          // file is a directory entry
          sprintf(tmp,"./%s %d", finfo.fname, finfo.fsize);
        } else {
          sprintf(tmp,"%s %d", finfo.fname, finfo.fsize);
        } 
        DBG_msg("ffdir", tmp);
        upstring(tmp);
        mldl.tx_string(tmp);
      } else {
        DBG_msg("do_ffdir", "Last directory entry");
        mldl.tx_add(c_escape);    // send <EOF> sequence
        mldl.tx_add(c_eof);                
      }
    } else {
      // could not open directory  
      DBG_msg("do_ffdir", "Could not open directory");
      send_error(err_directory);      
    }
  }  
}

/** lists the next directory item
 *
 *  syntax: FN [medium]
 *  \n         [medium]: U, S, L
 *  returns the next directory item, must be preceded by a FD call. 
 *  Subdirectories are not yet supported, 
 *  after the last item >F is returned
 */
void do_fndir() {
  FILINFO finfo;
  FRESULT res ;
  struct dirent *p;
  char tmp[25];   
    
  DBG_msg("do_fndir", inbuf);

  if (odirectory == f_local) {
    // get listing from local flash
    if (d != NULL) { 
      if ((p = readdir(d)) != NULL) {
        sprintf(tmp,"%s", p->d_name);      
        DBG_msg("ffdir", tmp);
        upstring(tmp);
        mldl.tx_string(tmp);
      } else {
        DBG_msg("ffdir", "Last directory entry");
        mldl.tx_add(c_escape);    // send <EOF> sequence
        mldl.tx_add(c_eof);         
      }
    }
  } else {
    // get listing from USB or SD card
    res = f_readdir(&dj, &finfo);
    if ((res == FR_OK) && finfo.fname[0] != 0) {
      if ((finfo.fattrib & AM_DIR) == AM_DIR) {
        // file is a directory entry
        sprintf(tmp,"./%s %d", finfo.fname, finfo.fsize);
      } else {
        sprintf(tmp,"%s %d", finfo.fname, finfo.fsize);
      }  
      DBG_msg("do_fndir", tmp);
      upstring(tmp);
      mldl.tx_string(tmp);
    } else {
      DBG_msg("do_fndir", "Last directory entry");
        mldl.tx_add(c_escape);    // send <EOF> sequence
        mldl.tx_add(c_eof);       
    }       
  }  
}  

/** returns the value of the current file pointer
 *
 *  syntax: FT [handle]
 *  \n         [handle]: 0..9
 *  returns the value of the file pointer for the open file with the
 *  given handle.
 */
void do_fftell() {
  int han = 0;
  int rslt = 0;
  char tmp[25];
  
  DBG_msg("do_ftell", inbuf);
 
  char c = inbuf[2];      // file handle should be here
  if ((c != NULL) && (c >= '0') && (c <= '9')) {
    // [handle] in range
    han = c - '0';
  } else {
    do_fdefault();
    return;           // check how to exit with error
  }    
  if (handles[han].fp == NULL) {     // file handle not in use!
    DBG_msg("do_ffclose", "no valid file handle");
    send_error(err_no_handle);
    return;
  }        
  rslt = ftell(handles[han].fp);
  sprintf(tmp,"%d", rslt); 
  mldl.tx_string(tmp);

  DBG_int("do_ftell result", rslt);  
}  
  
/** returns the size of a file
 *
 *  syntax: FZ [medium] [name]
 *  \n         [medium]: S, L or U
 *  \n         [name]: valid 8.3 filename
 *  \n         argument may contain %n for replacement by the Parameter n
 *  returns the size of a file. This function will open and close the file!
 */
void do_ffsize() {
  int i = 0;
  int pos = 0;
  int pnum = 0;
  int size = 0;
  char filename[20];
  char fullpath[50];
  char tmp[25];
  unsigned char c;
  char * cstr;
    
  wipesp(inbuf);
   
  DBG_msg("do_fsize", inbuf);  
  
  // check for a % sign in inbuf
  pos = 2;
  while ((inbuf[pos] != NULL) && (inbuf[pos] != c_percent)) pos++;
  
  if ((inbuf[pos] != NULL) && ((pnum = getpnum(inbuf[pos + 1])) >= 0)) {
    //  found and valid parameter, now insert
    cstr = new char [param[pnum].size() + 1];
    strcpy (cstr, param[pnum].c_str());
    DBG_msg("param", cstr);
    DBG_int("pos", pos);
    remchar(inbuf, pos, 2);       // remove %n, works better for insert
    DBG_msg("do_fsize", inbuf); 
    insertstr(inbuf, cstr, pos);  // insert
    DBG_msg("do_fsize", inbuf); 
    delete[] cstr;
  }
  
  c = inbuf[2];            // [medium]
  if (c != NULL) {
    switch (c) {
      case f_sdcard : strcpy(fullpath, fsp_sd);
                      break;                      
      case f_local  : strcpy(fullpath, fsp_local);
                      break;                      
      case f_usb    : strcpy(fullpath, fsp_usb);
                      break;                                            
      default       : do_fdefault();     // command not recognized
                      break;    
    } 
  } else {
    do_fdefault();
    return;        // terminate this function  
  } 
  
  // now get filename
  i = 0;
  while ((inbuf[i + 3] != NULL) && (i <= 13)) {
    // will not check until end of name, take max 13 chars
    filename[i] = inbuf[i + 3];
    i++;  
    filename[i] = NULL;  // ensure null terminated string
  }
 
  // create full pathname
  strcat(fullpath, filename);
    
  DBG_msg("filename", fullpath);
    
  // now open file  
  FILE *fp = fopen(fullpath, "r");
  if (fp != NULL) {
    DBG_msg("do_fsize", "file opened");
  } else {
    DBG_msg("do_fsize", "file NOT opened");
    send_error(err_filenotopen);
    return;                            // and get out
  }  
  
  fseek(fp, 0L, SEEK_END);   // seek to end
  size = ftell(fp);          // get size
  fclose(fp);                // close file
  sprintf(tmp,"%d", size); 
  mldl.tx_string(tmp);
   
  DBG_int("do_fsize result", size);
  
}
  
/** send error message, command not recognized,
 */   
void do_fdefault() {
  // get version string
  DBG_msg("do_fdefault", "unsupported command");
  send_error(err_novalidcommand);
}