view gsm-fw/services/ffs/tmffs.c @ 992:a7b0b426f9ca

target-utils: boot ROM UART autodetection revamped The new implementation should work with both the familiar Calypso C035 boot ROM version found in our regular targets as well as the older Calypso F741979B version found on the vintage D-Sample board.
author Mychaela Falconia <falcon@ivan.Harhan.ORG>
date Wed, 30 Dec 2015 21:28:41 +0000
parents 842c9fd828fd
children
line wrap: on
line source

/******************************************************************************
 * Flash File System (ffs)
 * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
 *
 * ffs testmode interface
 *
 * $Id: tmffs.c 1.51 Thu, 18 Dec 2003 10:50:52 +0100 tsj $
 *
 ******************************************************************************/

#include "../../include/config.h"
#include "../../riviera/rv/rv_defined_swe.h"

#ifdef RVM_ETM_SWE
#include "../etm/etm.h"
#include "../etm/etm_api.h"
#endif

#include "ffs.h"
#include "task.h"
#include "ffstrace.h"
#include "tmffs.h"

#include <string.h>

/******************************************************************************
 * Local globals for all protocols
 ******************************************************************************/

#if TMFFS1 || TMFFS2
static int32 bufsize, tmpsize;
static uint8 stringsize;
#endif

effs_t ffs_initialize(void);
effs_t ffs_exit(void);

#define tmffs_put8(x)   *outp++ = x;
#define tmffs_put16(x) *outp++ = (x & 0xff); *outp++ = (x>>8);

// Not in use
//#define tmffs_put32(x) tmffs_put16(x); tmffs_put16(x >> 16);

#ifdef RVM_ETM_SWE

int etm_ffs2(T_ETM_PKT *pkt, unsigned char *inp, int insize);

/******************************************************************************
 * TM FFS registration to ETM database
 *****************************************************************************/
/* Callback function registered in ETM database */
int etm_ffs1_pkt_receive(uint8 *data, int size)
{
    int mid;
    T_ETM_PKT *pkt;  

    ttw(ttr(TTrTmffs, "etm_ffs1_pkt_receive(*, %d)" NL, size)); 
    
    /* Create TestMode return Packet */
    if ((pkt = (T_ETM_PKT *) target_malloc(sizeof(T_ETM_PKT))) == NULL) {
        ttw(ttr(TTrTmffs, "etm_ffs1_pkt_receive(): Limit of memory bank reached" NL)); 
        return ETM_NOMEM;
    }
 
    // Max packet size for TM3 is 128 bytes
    size = tm_ffs(pkt->data, TM3_PACKET_SIZE, data, size);

    pkt->size   = size;
    pkt->status = ETM_OK;
    pkt->mid    = ETM_FFS1;
    
    etm_pkt_send(pkt);
    target_free(pkt);

    return ETM_OK;
}

/* Callback function registered in ETM database */
int etm_ffs2_pkt_receive(uint8 *data, int size)
{
    int status;
    T_ETM_PKT *pkt = NULL;  

    ttw(ttr(TTrTmffs, "etm_ffs2_pkt_receive(*, %d)" NL, size)); 

    /* Create TestMode return Packet */
    if ((pkt = (T_ETM_PKT *) target_malloc(sizeof(T_ETM_PKT))) == NULL) {
        ttw(ttr(TTrTmffs, "etm_ffs2_pkt_receive(): Limit of memory bank reached" NL)); 
        return ETM_NOMEM;
    } 
    
    status = etm_ffs2(pkt, data, size);
    return status;
}
#endif

/* Init of FFS in the ETM database */
int etm_ffs_init(void)
{
    int status;

#ifdef RVM_ETM_SWE
    status = etm_register("FFS1", ETM_FFS1, 0, 0, etm_ffs1_pkt_receive);
    status = etm_register("FFS2", ETM_FFS2, 0, 0, etm_ffs2_pkt_receive);
#else
    status = 0;
#endif
    return status;
} 

/******************************************************************************
 * FFS1 Protocol 
 ******************************************************************************/

#ifndef TMFFS1 

int tm_ffs(unsigned char *outp, int outsize, unsigned char *inp, int insize)
{
	return -1;   // FIXME handle error better 
}

// Note these functions must be presented because ffs_query() use them but
// they are only valid if FFS1_PROTOCOL is used.
int tmffs_bufsize(void)
{
    return EFFS_NOSYS;
}

unsigned char *tmffs_bufaddr(void)
{
    return 0;
}

#else

#if (GSMLITE == 1)
#define TMFFS1_BUFFER_SIZE 4000 //previously 8192
#else
#define TMFFS1_BUFFER_SIZE 8192
#endif

#define TMFFS1_STRING_SIZE 127

/******************************************************************************
 * Macros
 ******************************************************************************/

#define tmffs1_putdata(outp, src, size) \
    tmffs_put8(FPI_DATA); \
    tmffs_put16(size); \
    memcpy(outp, src, size); \
    outp += size;

/******************************************************************************
 * Local globals
 ******************************************************************************/

static unsigned char buffer[TMFFS1_BUFFER_SIZE];
static bufindex;

static char string[TMFFS1_STRING_SIZE];

static effs_t tm_ffs_overflowck(void)
{
    if (bufsize    > TMFFS1_BUFFER_SIZE ||
        stringsize > TMFFS1_STRING_SIZE)
        return EFFS_TOOBIG;

    return EFFS_OK;
}


/******************************************************************************
 * tm_ffs
 ******************************************************************************/

/**
 * NOTEME: This has been introduced when the ffs 1MB device limit was
 * broken. This made location_t go from uint16 to uint32, messing up
 * with PCTM.  
 *
 * This makes the xstat_s look the same to PCTM PC side, though
 * location will be forced to 0.
 */
void hack_xstat_2_look_like_old_xstat(struct xstat_s *xstat)
{
  int i;
  char *location;

  xstat->location = 0;
  
  for (location = (char *) &(xstat->location) + 2; location <= (char *) &(xstat->sequence); location++)
    *location = location[2];
}

// Parse input message and execute function. Then fill output buffer with
// return values from the called function and transmit the message.  Return
// number of bytes inserted into output buffer. If return value is negative,
// it represents an error code.
int tm_ffs(unsigned char *outp, int outsize, unsigned char *inp, int insize)
{
    int error;
    tmffs_cid_t fid;

    unsigned char *outp_start = outp;
    unsigned char *inp_start  = inp;

    static uint8   i8[2]; static uint16 i8i;
    static uint16 i16[2]; static uint16 i16i;
    static uint32 i32[2]; static uint16 i32i;

    tw(tr(TR_BEGIN, TrTmffs, "TMFFS:\n"));

    while((fid = *inp++) != FPI_END)
    {
        switch(fid)
        {
            /**********************************************************
             * Generic Protocol Functions
             **********************************************************/

        case FPI_BEGIN:
            // for (i8i = 0; i8i < TMFFS1_STRING_SIZE; i8i++) // DEBUG
            //     string[i8i] = '#';
            // for (i8i = 0; i8i < TMFFS1_BUFFER_SIZE; i8i++) // DEBUG
            //     buffer[i8i] = '$';
            i8i = i16i = i32i = bufsize = stringsize = 0;
            bufindex = 0;
             i8[0] =  i8[1] = 0;
            i16[0] = i16[1] = 0;
            i32[0] = i32[1] = 0;
            string[0] = buffer[0] = 0;
            tw(tr(TR_FUNC, TrTmffs, "FPI_BEGIN\n"));
            ttw(ttr(TTrTmffs, "tm1" NL));
            break;
        case FPI_TMFFS_VERSION:
            // NULL -> UINT16
            tmffs_put16(TMFFS1_VERSION);
            break;

        case FPI_INT8:
            i8[i8i++] = inp[0]; inp += 1;
            tw(tr(TR_FUNC, TrTmffs, "FPI_INT8(%d/0x%x)\n",
                  i8[i8i-1], i8[i8i-1]));
            ttw(ttr(TTrTmffs, "tm_i8" NL));
            break;
        case FPI_INT16:
            i16[i16i++] = (inp[0]) | (inp[1] << 8); inp += 2;
            tw(tr(TR_FUNC, TrTmffs, "FPI_INT16(%d/0x%x)\n",
                  i16[i16i-1], i16[i16i-1]));
            ttw(ttr(TTrTmffs, "tm_i16" NL));
            break;
        case FPI_INT32:
            i32[i32i++] = inp[0] | (inp[1] << 8)
                | (inp[2] << 16) | (inp[3] << 24);
            inp += 4;
            tw(tr(TR_FUNC, TrTmffs, "FPI_INT32(%d/0x%x)\n",
                  i32[i32i-1], i32[i32i-1]));
            ttw(ttr(TTrTmffs, "tm_i32" NL));
            break;
        case FPI_BUFFER:
            bufsize = inp[0] | (inp[1] << 8); inp += 2;
            tw(tr(TR_FUNC, TrTmffs, "FPI_BUFFER(%d)\n", bufsize));
            ttw(ttr(TTrTmffs, "tm_buf" NL));
            break;
        case FPI_DATA:
            bufsize = inp[0] | (inp[1] << 8); inp += 2;
            memcpy(buffer, inp, bufsize); inp += bufsize;
            tw(tr(TR_FUNC, TrTmffs, "FPI_DATA(%d)\n", bufsize));
            ttw(ttr(TTrTmffs, "tm_data" NL));
            break;
        case FPI_STRBUF:
            // string buffer size MUST include null-terminator!
            stringsize = inp[0]; inp += 1;
            tw(tr(TR_FUNC, TrTmffs, "FPI_STRBUF(%d)\n", stringsize));
            ttw(ttr(TTrTmffs, "tm_sbuf" NL));
            break;
        case FPI_STRING:
            // stringsize MUST include null-terminator!
            // <INT8>, <BYTES> -> NULL (or ERROR)
            stringsize = inp[0]; inp += 1;
            if (stringsize <= TMFFS1_STRING_SIZE)
                memcpy(string, inp, stringsize);
            inp += stringsize;
            tw(tr(TR_FUNC, TrTmffs, "FPI_STRING(%d,'%s')\n",
                  stringsize, string));
            ttw(ttr(TTrTmffs, "tm_s" NL));
            break;

        case FPI_BUFREAD:
            // <INT16> -> DATA
            tmpsize = inp[0] | (inp[1] << 8); inp += 2;
            tw(tr(TR_FUNC, TrTmffs, "FPI_BUF_READ(%d)\n", tmpsize));
            tmffs1_putdata(outp, &buffer[bufindex], tmpsize);
            bufindex += tmpsize;
            ttw(ttr(TTrTmffs, "tm_bufrd" NL));
            break;
        case FPI_BUFWRITE:
            // <INT16>, <BYTES> -> NULL (or ERROR)
            tmpsize = inp[0] | (inp[1] << 8); inp += 2;
            tw(tr(TR_FUNC, TrTmffs, "FPI_BUF_WRITE(%d)\n", tmpsize));
            if (bufsize + tmpsize <= TMFFS1_BUFFER_SIZE)
                memcpy(&buffer[bufsize], inp, tmpsize);
            inp += tmpsize;
            bufsize += tmpsize;
            ttw(ttr(TTrTmffs, "tm_bufwr" NL));
            break;
        case FPI_BUFSET:
            bufindex = inp[0] | (inp[1] << 8); inp += 2;
            tw(tr(TR_FUNC, TrTmffs, "FPI_BUF_SET(%d)\n", bufindex));
            ttw(ttr(TTrTmffs, "tm_bufset" NL));
            break;

            /**********************************************************
             * FFS Functions
             **********************************************************/

        case FPI_PREFORMAT:
            // NULL -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_preformat_nb(i16[0], 0);
            if (error > 0) 
                error = 0;  // ignore request id
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_PREFORMAT(0x%x)\n", i16[0]));
            ttw(ttr(TTrTmffs, "tm_pfmt" NL));
            break;
        case FPI_FORMAT:
            // STRING -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_format_nb(&string[0], i16[0], 0);
            if (error > 0) 
                error = 0;  // ignore request id
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_FORMAT(0x%x)\n", i16[0]));
            ttw(ttr(TTrTmffs, "tm_fmt" NL));
            break;


        case FPI_FCREATE:
            // STRING, DATA -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_fcreate_nb(string, buffer, bufsize, 0);
            if (error > 0) 
                error = 0;  // ignore request id
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_FCREATE('%s', 0x%x, %d/0x%x)\n",
                  string, buffer, bufsize, bufsize));
            ttw(ttr(TTrTmffs, "tm_fcr" NL));
            break;
        case FPI_FUPDATE:
            // STRING, DATA -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_fupdate_nb(string, buffer, bufsize, 0);
            if (error > 0) 
                error = 0;  // ignore request id
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_FUPDATE('%s', 0x%x, %d/0x%x)\n",
                  string, buffer, bufsize, bufsize));
            ttw(ttr(TTrTmffs, "tm_fup" NL));
            break;
        case FPI_FWRITE:
            // STRING, DATA -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_fwrite_nb(string, buffer, bufsize, 0);
            if (error > 0) 
                error = 0;  // ignore request id
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_FWRITE('%s', 0x%x, %d/0x%x)\n",
                  string, buffer, bufsize, bufsize));
            ttw(ttr(TTrTmffs, "tm_fwr" NL));
            break;
        case FPI_FREAD:
            // STRING, BUFFER -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_file_read(string, buffer, TMFFS1_BUFFER_SIZE);
            // Because a 32-bit integer is returned, we have to saturate it
            // into an 8-bit value.
            if (error >= 0)
                error = 0;
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_FREAD('%s', 0x%x, %d/0x%x)\n",
                  string, buffer, bufsize, bufsize));
            ttw(ttr(TTrTmffs, "tm_frd" NL));
            break;
        case FPI_REMOVE:
            // STRING -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_remove_nb(string, 0);
            if (error > 0) 
                error = 0;  // ignore request id
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_REMOVE()\n"));
            ttw(ttr(TTrTmffs, "tm_rm" NL));
            break;


        case FPI_MKDIR:
            // STRING -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_mkdir_nb(string, 0);
            if (error > 0) 
                error = 0;  // ignore request id
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_MKDIR()\n"));
            ttw(ttr(TTrTmffs, "tm_mkd" NL));
            break;
        case FPI_OPENDIR:
            // STRING, BUFFER -> ERROR, DATA
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_opendir(string, (struct dir_s *) buffer);
            // Because a 32-bit integer is returned, we have to saturate it
            // into an 8-bit value.
            if (error >= 0)
                error = 0;
            tmffs_put8(error);
            tmffs1_putdata(outp, buffer, sizeof(struct dir_s));
            tw(tr(TR_FUNC, TrTmffs, "FPI_OPENDIR()\n"));
            ttw(ttr(TTrTmffs, "tm_od" NL));
            break;
        case FPI_READDIR:
            // DATA, STRBUF -> ERROR, DATA, STRING
            string[0] = 0;
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_readdir((struct dir_s *) buffer, string, stringsize);

	    // Saturate error(i) in order to let it fit in type int8.
	    if (error > 127)
		error = 127;
            tmffs_put8(error);
            tmffs1_putdata(outp, buffer, sizeof(struct dir_s));
            stringsize = strlen(string) + 1;
            tmffs_put8(FPI_STRING); // put directory entry's name...
            tmffs_put8(stringsize);
            memcpy(outp, string, stringsize);
            outp += stringsize;
            tw(tr(TR_FUNC, TrTmffs, "FPI_READDIR()\n"));
            ttw(ttr(TTrTmffs, "tm_rdd" NL));
            break;


        case FPI_STAT:
            // STRING, BUFFER -> ERROR, DATA 
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_stat(&string[0], (struct stat_s *) buffer);
            tmffs_put8(error);
            tmffs1_putdata(outp, buffer, sizeof(struct stat_s));
            tw(tr(TR_FUNC, TrTmffs, "FPI_STAT()\n"));
            ttw(ttr(TTrTmffs, "tm_st" NL));
            break;
        case FPI_LINKSTAT:
            // STRING, BUFFER -> ERROR, DATA
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_xlstat(&string[0], (struct xstat_s *) buffer);
            tmffs_put8(error);

            hack_xstat_2_look_like_old_xstat((struct xstat_s *) buffer);

            tmffs1_putdata(outp, buffer, sizeof(struct xstat_s) - 2);
            tw(tr(TR_FUNC, TrTmffs, "FPI_()\n"));
            ttw(ttr(TTrTmffs, "tm_lst" NL));
            break;


        case FPI_SYMLINK:
            // STRING, DATA -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_symlink_nb(string, (char *) buffer, 0);
            if (error > 0) 
                error = 0;  // ignore request id
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_SYMLINK()\n"));
            ttw(ttr(TTrTmffs, "tm_sym" NL));
            break;
        case FPI_READLINK:
            // STRING, BUFFER -> ERROR, DATA
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_readlink(string, (char *) buffer, TMFFS1_BUFFER_SIZE);
            // Because a 32-bit integer is returned, we have to saturate it
            // into an 8-bit value.
            if (error >= 0)
                error = 0;
            tmffs_put8(error);
            tmffs1_putdata(outp, buffer, bufsize); // put link contents
            tw(tr(TR_FUNC, TrTmffs, "FPI_READLINK()\n"));
            ttw(ttr(TTrTmffs, "tm_rdl" NL));
            break;


        case FPI_QUERY:
            // INT8 -> ERROR, DATA
            error = ffs_query(i8[0], buffer);
            tmffs_put8(error);
            tmffs1_putdata(outp, buffer, 16);
            tw(tr(TR_FUNC, TrTmffs, "FPI_QUERY()\n"));
            ttw(ttr(TTrTmffs, "tm_q" NL));
            break;
        case FPI_FCONTROL:
            // STRING INT8 INT32 -> ERROR
            if ((error = tm_ffs_overflowck()) == EFFS_OK)
                error = ffs_fcontrol_nb(string, i8[0], i32[0], 0);
            if (error > 0) 
                error = 0;  // ignore request id
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_FCONTROL()\n"));
            ttw(ttr(TTrTmffs, "tm_fc" NL));
            break;

        case FPI_INIT:
            // NULL -> ERROR
            error =ffs_initialize();
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_INIT()\n"));
            ttw(ttr(TTrTmffs, "tm_init" NL));
            break;
        case FPI_EXIT:
            // NULL -> ERROR
            error = ffs_exit();
            tmffs_put8(error);
            tw(tr(TR_FUNC, TrTmffs, "FPI_EXIT()\n"));
            ttw(ttr(TTrTmffs, "tm_exit" NL));
            break;


        case FPI_TFFS:
        {
            // STRING -> ERROR
#if (WITH_TFFS == 1)
            extern char ffs_test_string[]; // defined in task.c

            memcpy(ffs_test_string, string, stringsize);
            tw(tr(TR_FUNC, TrTmffs, "FPI_TFFS()\n"));
            ttw(ttr(TTrTmffs, "tm_tffs" NL));
#else
            tmffs_put8(EFFS_NOSYS);
#endif            
            break;
        }
        default:
            tw(tr(TR_FUNC, TrTmffs, "ERROR: Unknown tmffs protocol code\n"));
            ttw(ttr(TTrTmffs, "tm?" NL));
            break;
        }
        // check if we read beyond buffer end
        if (inp > inp_start + insize) {
            tw(tr(TR_FUNC, TrTmffs, "ERROR: Read beyond end of input buffer\n"));
            ttw(ttr(TTrTmffs, "tm_fatal" NL));
            // NOTEME: We really should reset output buffer and put a return
            // code that tells us what went wrong!
            return 0;
        }
    }

    tw(tr(TR_END, TrTmffs, ""));

    return outp - outp_start;
}

int tmffs_bufsize(void)
{
    return TMFFS1_BUFFER_SIZE;
}

unsigned char *tmffs_bufaddr(void)
{
    return buffer;
}

#endif // TMFFS1 

/******************************************************************************
 * FFS2 protocol  
 ******************************************************************************/

#ifndef TMFFS2

#ifdef RVM_ETM_SWE
int etm_ffs2(T_ETM_PKT *pkt, unsigned char *inp, int insize)
{
    int error;

    tw(tr(TR_BEGIN, TrTmffs, "FFS2 protocol not represented in target\n"));
    error = -1;  // FIXME other error?

    // We return a packet instead of waiting for timeout.
    pkt->size =  0;
    pkt->status = -error;       
    pkt->mid = ETM_FFS2;
    etm_pkt_send(pkt);

    target_free(pkt);
    tw(tr(TR_END, TrTmffs, ""));

    return error;
}
#endif

#else

#define TMFFS_BUFFER_SIZE 256   // FIXME change to packet size
#define TMFFS_STRING_SIZE 127

/******************************************************************************
 * Macros
 ******************************************************************************/

#define tmffs_get8() inp[0]; inp += 1; 
#define tmffs_get16() (inp[0]) | (inp[1] << 8); inp += 2; 
#define tmffs_get32()  inp[0] | (inp[1] << 8) | (inp[2] << 16)\
                       | (inp[3] << 24); inp += 4; 

#define tmffs_getdata()    bufsize = inp[0]; inp += 1; \
                           memcpy(buffer, inp, bufsize); inp += bufsize;


/******************************************************************************
 * Helper function
 ******************************************************************************/

// If size is less than zero it is because of a error and we dont have to put any
// data if size is returned in status.
int tmffs_putdata(unsigned char **buf, unsigned char *src, int size) 
{
 	unsigned char *p = *buf;

	if (size > 0) {
		*p++ = size;
		memcpy(p, src, size); 
		*buf += 1 + size;
	}
	return size;
}

int tmffs_putstring(unsigned char **buf, char *src, int size) 
{
	unsigned char *p = *buf;

	if (size > 0) {
		*p++ = size;
		memcpy(p, src, size); 
		*buf += 1 + size;
	}
	return size;
}

int tmffs_getstring(unsigned char ** buf, char *string)
{
	unsigned char *p = *buf;
	
	stringsize = *p++;

	if (stringsize > TMFFS_STRING_SIZE) 
		return EFFS_TOOBIG;

	memcpy(string, p, stringsize);
	*buf += 1 + stringsize;

	return stringsize;
}

/******************************************************************************
 * tm_ffs
 ******************************************************************************/

// Parse input message and execute function. Then fill output buffer with
// return values from the called function and transmit the message.  Return
// number of bytes inserted into output buffer. If return value is negative,
// it represents an error code.
int etm_ffs2(T_ETM_PKT *pkt, unsigned char *inp, int insize)
{
	tmffs2_cid_t fid;
	unsigned char buffer[TMFFS_BUFFER_SIZE];
	char string[TMFFS_STRING_SIZE];

	unsigned char *outp_start;
	unsigned char *inp_start  = inp;
	unsigned char *outp;

	int error = 0, i, fdi, size, param, flags;
	uint8 type;

	bufsize = stringsize = tmpsize = 0;

	tw(tr(TR_BEGIN, TrTmffs, "TmFFS2\n"));
    
	outp_start = outp = pkt->data;
	
	fid = *inp++;
	ttw(ttr(TTrTmffs, "etm_ffs2 0x%x" NL, fid));
	switch(fid)
	{
		/**********************************************************
		 * Generic Protocol Functions
		 **********************************************************/

	case TMFFS_VERSION:
		tmffs_put16(TMFFS2_VERSION);
		break;

		/**********************************************************
		 * FFS Functions
		 **********************************************************/

	case TMFFS_PREFORMAT:
		param = tmffs_get16();
		error = ffs_preformat(param);	
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_PREFORMAT(0x%x)\n", param));
		ttw(ttr(TTrTmffs, "tm_pfmt" NL));
		break;
	case TMFFS_FORMAT: 
		error = tmffs_getstring(&inp, string);
		param = tmffs_get16();
		if (error >= 0)
			error = ffs_format(&string[0], param);	
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_FORMAT(0x%x)\n", param));
		ttw(ttr(TTrTmffs, "tm_fmt" NL));	
		break;

		
	case TMFFS_FILE_WRITE:
		error = tmffs_getstring(&inp, string);
		tmffs_getdata();
		flags = tmffs_get8();
		if (error >= 0)
			error = ffs_file_write(string, buffer, bufsize, flags);
		ttw(ttr(TTrTmffs, "tm_fwr" NL));
		break;


	case TMFFS_FILE_READ:
		error = tmffs_getstring(&inp, string);
		bufsize = tmffs_get8();
		if (error >= 0)
			size = ffs_file_read(string, buffer, bufsize);
		error = tmffs_putdata(&outp, &buffer[0], size);
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_FREAD('%s', 0x%x, %d/0x%x)\n",
			  string, buffer, bufsize, bufsize));
		ttw(ttr(TTrTmffs, "tm_frd" NL));
		break;
	case TMFFS_REMOVE:
		error = tmffs_getstring(&inp, string);
		if (error >= 0)
			error = ffs_remove(string);
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_REMOVE()\n"));
		ttw(ttr(TTrTmffs, "tm_rm" NL));
		break;


	case TMFFS_OPEN:
		error = tmffs_getstring(&inp, string);
		flags = tmffs_get8();
		if (error >= 0)
			error = ffs_open(string, flags);
		tmffs_put8(error); // fdi
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_OPEN('%s', %d)\n", string, flags));
		ttw(ttr(TTrTmffs, "tm_open" NL));
		break;
	case TMFFS_CLOSE:
		fdi = tmffs_get8(); 
		error = ffs_close(fdi);
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_CLOSE(%d)\n", fdi));
		ttw(ttr(TTrTmffs, "tm_close" NL));
		break;
	case TMFFS_WRITE:
		fdi = tmffs_get8(); 
		tmffs_getdata(); 
		error = ffs_write(fdi, buffer, bufsize);
		tmffs_put8(error); // put size
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_WRITE(%d, %d)\n", fdi, bufsize));
		ttw(ttr(TTrTmffs, "tm_write" NL));
		break;
	case TMFFS_READ:
		fdi = tmffs_get8(); 
		size = tmffs_get8();
		size = ffs_read(fdi, &buffer[0], size);
		error =	tmffs_putdata(&outp, &buffer[0], size);
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_READ(%d, %d)\n", fdi, size));
		ttw(ttr(TTrTmffs, "tm_read" NL));
		break;


	case TMFFS_MKDIR:
		error = tmffs_getstring(&inp, string);
		if (error >= 0)
			error = ffs_mkdir(string);
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_MKDIR()\n"));
		ttw(ttr(TTrTmffs, "tm_mkd" NL));
		break;
	case TMFFS_OPENDIR:
		error = tmffs_getstring(&inp, string);
		if (error >= 0) { 
			error = ffs_opendir(string, (struct dir_s *) buffer);
			tmffs_put8(error); // Note: we must put error/number of objects.
		}
		if (error >= 0)
			tmffs_putdata(&outp, buffer, sizeof(struct dir_s));
		
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_OPENDIR()\n"));
		ttw(ttr(TTrTmffs, "tm_od" NL));
		break;
	case TMFFS_READDIR:
		tmffs_getdata();
		stringsize = tmffs_get8();		
		error = ffs_readdir((struct dir_s *) buffer, string, stringsize);
		tmffs_put8(error); // Note: we have to return bytes read.
		if (error >= 0) {
			tmffs_putdata(&outp, buffer, sizeof(struct dir_s));
			stringsize = strlen(string) + 1;			
			tmffs_putstring(&outp, string, stringsize);
		}
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_READDIR()\n"));
		ttw(ttr(TTrTmffs, "tm_rdd" NL));
		break;

	case TMFFS_STAT:
		error = tmffs_getstring(&inp, string);
		if (error >= 0) 
			error = ffs_stat(string, (struct stat_s *) buffer);
		if (error >= 0)
			tmffs_putdata(&outp, buffer, sizeof(struct stat_s));
		
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_STAT()\n"));
		ttw(ttr(TTrTmffs, "tm_st" NL));
		break;
	case TMFFS_XLSTAT:
		error = tmffs_getstring(&inp, string);
		if (error >= 0)
			error = ffs_xlstat(&string[0], (struct xstat_s *) buffer);
		if (error >= 0)
			tmffs_putdata(&outp, buffer, sizeof(struct xstat_s));
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_()\n"));
		ttw(ttr(TTrTmffs, "tm_xlst" NL));
		break;


	case TMFFS_SYMLINK:
		error = tmffs_getstring(&inp, string);
		tmffs_getdata();
		if (error >= 0)
			error = ffs_symlink(string, (char *) buffer);
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_SYMLINK()\n"));
		ttw(ttr(TTrTmffs, "tm_sym" NL));
		break;
	case TMFFS_READLINK:
		error = tmffs_getstring(&inp, string);
		tmffs_getdata();
		if (error >= 0) {
			size = ffs_readlink(string, (char *) buffer, TMFFS_BUFFER_SIZE);
			error = tmffs_putdata(&outp, buffer, size); // put link contents
		}
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_READLINK()\n"));
		ttw(ttr(TTrTmffs, "tm_rdl" NL));
		break;


	case TMFFS_QUERY:
		param = tmffs_get8();
		error = ffs_query(param, buffer);
		if (error >= 0)
			tmffs_putdata(&outp, buffer, 16);
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_QUERY(%d)\n", param));
		ttw(ttr(TTrTmffs, "tm_q" NL));
		break;
	case TMFFS_FCONTROL:
		error = tmffs_getstring(&inp, string);
		type = tmffs_get8();
		param = tmffs_get32();
		if (error >= 0)
			error = ffs_fcontrol(string, type, param);
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_FCONTROL()\n"));
		ttw(ttr(TTrTmffs, "tm_fc" NL));
		break;

	case TMFFS_TFFS:
	{
#if (WITH_TFFS == 1)
		extern char ffs_test_string[]; // defined in task.c
		error = tmffs_getstring(&inp, string);
		memcpy(ffs_test_string, string, stringsize);
		tw(tr(TR_FUNC, TrTmffs, "TMFFS_TFFS()\n"));
		ttw(ttr(TTrTmffs, "tm_tffs" NL));
		tmffs_put8(EFFS_OK);
#else
		tmffs_put8(EFFS_NOSYS);
#endif            
		break;
	}
	default:
		error = EFFS_NOSYS;
		tmffs_put8(EFFS_NOSYS);
		tw(tr(TR_FUNC, TrTmffs, "ERROR: Unknown tmffs protocol code\n"));
		ttw(ttr(TTrTmffs, "tm?" NL));
		break;
	}
	
	// check if we read beyond buffer end
	if (inp > inp_start + insize) {
		tw(tr(TR_FUNC, TrTmffs, "ERROR: Read beyond end of input buffer\n"));
		ttw(ttr(TTrTmffs, "tm_fatal" NL));
		ttw(ttr(TTrTmffs, "insize: %d, diff: %d" NL, insize, 
				inp - (inp_start + insize)));
		// NOTEME: We really should reset output buffer and put a return
		// code that tells us what went wrong!
		error = ETM_PACKET;  // FIXME find another error 
	}

	ttw(ttr(TTrTmffs, "error %d" NL, error));
	if (error > 0)
		error = 0;

	pkt->mid    = ETM_FFS2;
	pkt->size   = outp - outp_start;
	pkt->status = -error;

	etm_pkt_send(pkt);
	etm_free(pkt);

	tw(tr(TR_END, TrTmffs, ""));

	return ETM_OK;
}

#endif // TMFFS2