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

Revision:
0:67a55a82ce06
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffuncs.cpp	Sun Feb 27 18:54:40 2011 +0000
@@ -0,0 +1,791 @@
+/* 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);
+}
\ No newline at end of file