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
Diff: ffuncs.cpp
- 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