www.pudn.com > drivers.rar > tffs.c


  /****************************************************************************** 
 * Flash File System (ffs) 
 * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com 
 * 
 * ffs test scaffold/framework 
 * 
 * $Id: tffs.c 1.12.1.1.1.20 Fri, 19 Dec 2003 12:00:13 +0100 tsj $ 
 * 
 ******************************************************************************/ 
 
#ifndef TARGET 
#include "ffs.cfg" 
#endif 
 
#include "ffs/ffs_api.h" 
#include "ffs/board/core.h" 
#include "ffs/board/drv.h" 
#include "ffs/board/tffs.h" 
#include "ffs/board/tdata.h" 
#include "ffs/board/tmffs.h" 
#include "ffs/board/ffstrace.h" 
 
#if (TARGET == 1) 
#include "ffs/board/task.h" 
#endif 
 
#include  
#include  
#include  
#include  
 
 
/****************************************************************************** 
 * Prototypes and Globals 
 ******************************************************************************/ 
 
struct dir_s dir; 
struct stat_s tstat; 
struct xstat_s txstat; 
 
int error; 
 
#if (TARGET == 1) 
int smallbuf_size = 512; 
int bigbuf_size = 65536; 
#else 
int smallbuf_size = 1024; 
int bigbuf_size = 1024*1024*8; 
#endif 
 
char *smallbuf = 0; 
char *bigbuf = 0; 
 
 
/****************************************************************************** 
 * Globals and Main 
 ******************************************************************************/ 
 
extern struct testcase_s testcase[]; 
 
struct test_s { 
    char *name;     // name of currently executing test case 
    int  numcases;  // number of test cases run so far 
    int  numcalls;  // total number of ffs function calls so far 
    int  numfails;  // total number of failed test cases 
    int  keepgoing; // keep going when a test case fails 
} test; 
 
struct ffs_params_s param; 
 
 
/****************************************************************************** 
 * Main Functions 
 ******************************************************************************/ 
 
effs_t ffs_initialize(void); 
effs_t ffs_exit(void); 
 
// Each test case returns zero on success, non-zero on failure. 
// test_execute() decides whether to continue with remaining test cases or 
// not. 
 
#if (TARGET == 0) 
void test_listall(void) 
{ 
    struct testcase_s *p; 
 
    printf("Test Cases:\n"); 
    for (p = testcase; p->name != 0; p++) { 
        printf("%8s: %s\n", p->name, p->comment); 
    } 
} 
#endif 
 
//  is a comma-separated string of names of the test cases to 
// run. Return number of test cases that failed. This is typically only 1, 
// unless arg_keepgoing is set in which case it can be several. 
int test_run(char *tests) 
{ 
    struct testcase_s *ptc; 
    int i, failed, tparams[FFS_TESTCASE_PARAMS_MAX]; 
    struct this_test_s this; 
    char *pname, *testsnew; 
 
    failed = 0; 
 
    while (*tests != 0 && (failed == 0 || test.keepgoing)) 
    { 
        // Make local copy of test case name. We have to make local copies 
        // because we can be recursively called 
        pname = this.name; 
        while (isalpha(*tests) && *tests != 0 && *tests != ';') 
            *pname++ = *tests++; 
        *pname = 0; 
 
        // Reset test case parameter(s) 
        for (i = 0; i < FFS_TESTCASE_PARAMS_MAX; i++) 
            tparams[i] = 0; 
 
        // Collect parameter(s) for test case 
        i = 0; 
        while (isdigit(*tests)) { 
            tparams[i] = strtol(tests, &testsnew, 0); 
            if (tests == testsnew) 
                break; 
            tests = testsnew; 
            if (*tests == ',') 
                tests++; 
            i++; 
            if (i > FFS_TESTCASE_PARAMS_MAX) { 
                ttw(ttr(TTrTest, "TEST %s has TOO MANY PARAMS" NL, this.name)); 
                tw(tr(TR_FUNC, TrTest, "TEST %s TOO MANY PARAMS\n", this.name)); 
                return 1; 
            } 
        } 
                 
        if (*tests == ';') 
            tests++; 
 
        // Lookup the test name in the array of test cases 
        for (ptc = testcase; ptc->name != 0; ptc++) { 
            if (strcmp(this.name, ptc->name) == 0) 
                break; 
        } 
        if (ptc->name == 0) { 
            ttw(ttr(TTrTest, "TEST %s UNKNOWN" NL, this.name)); 
            tw(tr(TR_FUNC, TrTest, "TEST %s UNKNOWN\n", this.name)); 
            return 1; 
        } 
 
        this.numcalls = test.numcalls; 
        test_begin(this.name, tparams); 
        i = ptc->function(tparams[0], tparams[1]); 
        if (i != 0) { 
            failed++; 
            test_error(&this, test.numcalls); 
        } 
        test_end(&this, test.numcalls); 
    } 
 
    return failed; 
} 
 
// Overall test initialization. Read static ffs params with ffs_query() 
void test_init(int keepgoing) 
{ 
    test.numcases = 0; 
    test.numcalls = 0; 
    test.numfails = 0; 
    test.keepgoing = keepgoing; 
 
    memset(¶m, 0, sizeof(struct ffs_params_s)); 
 
    if (smallbuf == 0) { 
#if (TARGET == 1) 
        smallbuf = (char*)tffs_malloc(smallbuf_size); 
#else 
        smallbuf = malloc(smallbuf_size); 
#endif 
        tw(tr(TR_FUNC, TrTest, "smallbuf = 0x%X, %d\n", 
              smallbuf, smallbuf_size)); 
    } 
    if (bigbuf == 0) { 
#if (TARGET == 1) 
        // We continuously halve the buffer size until we succeed to allocate 
        // it. 
        while(1) { 
            if ((bigbuf = (char*)tffs_malloc(bigbuf_size)) != 0) 
                break; 
            bigbuf_size /= 2; 
        }  
#else 
        bigbuf = malloc(bigbuf_size); 
#endif 
        tw(tr(TR_FUNC, TrTest, "bigbuf   = 0x%X, %d\n", bigbuf, bigbuf_size)); 
    } 
 
    test_tdata_init(); 
 
    tffs_initialize(); 
} 
 
void test_exit(void) 
{ 
    test_state_print(0); 
} 
 
 
// Begin new test case 
void test_begin(char *name, int *params) 
{ 
    test.numcases++; 
 
    tw(tr(TR_BEGIN, TrTest, "TEST %s(%d,%d)\n", name, params[0], params[1])); 
    ttw(ttr(TTrTest, "TEST %s(%d,%d)" NL, name, params[0], params[1])); 
} 
 
void test_end(struct this_test_s *test, int n) 
{ 
    int objects = 0; 
 
    ffs_query(Q_TOTAL_OBJECTS, (uint16 *) &objects); 
    tw(tr(TR_FUNC, TrTestHigh, "(total objects = %d)\n", objects)); 
    tw(tr(TR_END, TrTest, "")); 
    //tw(tr(TR_END, TrTest, "TEST END   %s (calls = %d)\n", 
    //      test->name, n - test->numcalls)); 
} 
 
void test_error(struct this_test_s *test, int n) 
{ 
    ttw(ttr(TTrTest, "TEST FAIL %s, call %d" NL, 
            test->name, n - test->numcalls)); 
    tw(tr(TR_FUNC, TrTest, "TEST FAIL %s, call %d\n", 
          test->name, n - test->numcalls)); 
} 
 
 
/****************************************************************************** 
 * Miscellaneous 
 ******************************************************************************/ 
 
int test_ffs_state_get(struct ffs_state_s *s) 
{ 
    memset(s, 0, sizeof(struct ffs_state_s)); 
 
    error  = ffs_query(Q_INODES_USED,  (uint16 *) &s->inodes_used); 
    error += ffs_query(Q_INODES_LOST,  (uint16 *) &s->inodes_lost); 
    error += ffs_query(Q_OBJECTS_FREE,  (uint16 *) &s->objects_free); 
    error += ffs_query(Q_TOTAL_OBJECTS, (uint16 *) &s->objects_total); 
 
    error += ffs_query(Q_BYTES_USED, (uint16 *) &s->bytes_used); 
    error += ffs_query(Q_BYTES_LOST, (uint16 *) &s->bytes_lost); 
    error += ffs_query(Q_BYTES_FREE, (uint16 *) &s->bytes_free); 
 
    error += ffs_query(Q_BLOCKS_FREE, (uint16 *) &s->blocks_free); 
 
    return error; 
} 
 
void test_ffs_state_copy(struct ffs_state_s *dst, struct ffs_state_s *src) 
{ 
    memcpy(dst, src, sizeof(struct ffs_state_s)); 
} 
 
void test_state_print(struct ffs_state_s *state) 
{ 
    struct ffs_state_s mystate; 
 
    if (state == 0) { 
        state = &mystate; 
        test_ffs_state_get(state); 
    } 
 
    tw(tr(TR_FUNC, TrTest, "\nFFS State Summary:\n\n")); 
    ttw(str(TTrTest, NL "FFS State Summary:" NL NL)); 
 
    tw(tr(TR_FUNC, TrTest, "  block_size = %d\n", param.block_size)); 
    tw(tr(TR_FUNC, TrTest, "  bytes_avail = %d\n\n", param.bytes_avail)); 
 
    ttw(ttr(TTrTest, "  block_size = %d" NL, param.block_size)); 
    ttw(ttr(TTrTest, "  bytes_avail = %d" NL NL, param.bytes_avail)); 
 
    test_state_bytes_print(state, 0); 
    test_state_objects_print(state, 0); 
 
} 
 
void test_state_objects_print(struct ffs_state_s *old, struct ffs_state_s *new) 
{ 
    ttw(str(TTrTest, "              inodes            objects" NL)); 
    ttw(str(TTrTest, "  -------------------------------------------" NL)); 
    ttw(str(TTrTest, "  objects:    used     lost     free    total" NL)); 
    ttw(ttr(TTrTest, "  old:      %6d   %6d   %6d   %6d" NL, 
          old->inodes_used, old->inodes_lost, 
          old->objects_free, old->objects_total)); 
 
    tw(tr(TR_FUNC, TrTest, "              inodes            objects\n")); 
    tw(tr(TR_FUNC, TrTest, "  -------------------------------------------\n")); 
    tw(tr(TR_FUNC, TrTest, "  objects:    used     lost     free    total\n")); 
    tw(tr(TR_FUNC, TrTest, "  old:      %6d   %6d   %6d   %6d\n", 
          old->inodes_used, old->inodes_lost, 
          old->objects_free, old->objects_total)); 
 
    if (new != NULL) 
    { 
        ttw(ttr(TTrTest, 
              "  new:      %6d   %6d   %6d   %6d" NL, 
              new->inodes_used, new->inodes_lost, 
              new->objects_free, new->objects_total)); 
        ttw(ttr(TTrTest, 
              "  diff:     %6d   %6d   %6d   %6d" NL, 
              new->inodes_used - old->inodes_used, 
              new->inodes_lost - old->inodes_lost, 
              new->objects_free - old->objects_free, 
              new->objects_total - old->objects_total)); 
 
        tw(tr(TR_FUNC, TrTest, 
              "  new:      %6d   %6d   %6d   %6d\n", 
              new->inodes_used, new->inodes_lost, 
              new->objects_free, new->objects_total)); 
        tw(tr(TR_FUNC, TrTest, 
              "  diff:     %6d   %6d   %6d   %6d\n", 
              new->inodes_used - old->inodes_used, 
              new->inodes_lost - old->inodes_lost, 
              new->objects_free - old->objects_free, 
              new->objects_total - old->objects_total)); 
    } 
    ttw(str(TTrTest, "" NL)); 
    tw(tr(TR_FUNC, TrTest, "\n")); 
} 
 
void test_state_bytes_print(struct ffs_state_s *old, struct ffs_state_s *new) 
{ 
    tw(tr(TR_FUNC, TrTest, "  bytes:      used     lost     free    total\n")); 
    tw(tr(TR_FUNC, TrTest, "  old:    %8d %8d %8d %8d\n", 
          old->bytes_used, old->bytes_lost, 
          old->bytes_free, param.bytes_max)); 
    tw(tr(TR_FUNC, TrTest, "  +/-:    %8d          %8d\n", 
          old->bytes_used - old->bytes_lost, 
          old->bytes_free + old->bytes_lost)); 
 
    ttw(str(TTrTest, "  bytes:      used     lost     free    total" NL)); 
    ttw(ttr(TTrTest, "  old:    %8d %8d %8d %8d" NL, 
          old->bytes_used, old->bytes_lost, 
          old->bytes_free, param.bytes_max)); 
    ttw(ttr(TTrTest, "  +/-:    %8d          %8d" NL, 
          old->bytes_used - old->bytes_lost, 
          old->bytes_free + old->bytes_lost)); 
 
    if (new != NULL) { 
        tw(tr(TR_FUNC, TrTest, "  new:    %8d %8d %8d\n", 
              new->bytes_used, new->bytes_lost, 
              new->bytes_free)); 
        tw(tr(TR_FUNC, TrTest, "  diff:   %8d %8d %8d\n", 
              new->bytes_used - old->bytes_used, 
              new->bytes_lost - old->bytes_lost, 
              new->bytes_free - old->bytes_free)); 
 
        ttw(ttr(TTrTest, "  new:    %8d %8d %8d" NL, 
              new->bytes_used, new->bytes_lost, 
              new->bytes_free)); 
        ttw(ttr(TTrTest, "  diff:   %8d %8d %8d" NL, 
              new->bytes_used - old->bytes_used, 
              new->bytes_lost - old->bytes_lost, 
              new->bytes_free - old->bytes_free)); 
    } 
    tw(tr(TR_FUNC, TrTest, "\n")); 
    ttw(str(TTrTest, "" NL)); 
} 
 
 
// Retrieve all static ffs parameters with ffs_query() 
int test_ffs_params_get(void) 
{ 
    error  = ffs_query(Q_FILENAME_MAX,    ¶m.filename_max); 
    error += ffs_query(Q_PATH_DEPTH_MAX,  ¶m.pathdepth_max); 
    error += ffs_query(Q_INODES_MAX,      ¶m.inodes_max); 
    error += ffs_query(Q_BYTES_MAX,       ¶m.bytes_max); 
    error += ffs_query(Q_DEV_BLOCKS,      ¶m.numblocks); 
    error += ffs_query(Q_DEV_ATOMSIZE,    ¶m.atomsize); 
    error += ffs_query(Q_BLOCKS_FREE_MIN, ¶m.blocks_free_min); 
 
    // Compute block size 
    param.block_size = param.bytes_max / param.numblocks; 
 
    // Compute total number of available storage space, subtracting 
    // fs.blocks_free_min plus one block for inodes 
    param.bytes_avail = 
        param.bytes_max - (param.block_size * (1 + param.blocks_free_min)); 
 
    // Compute number of blocks available for data storage 
    param.data_blocks = param.numblocks - (1 + param.blocks_free_min); 
 
    return error; 
} 
 
void test_statistics_print(void) 
{ 
    tw(tr(TR_FUNC, TrTest, "Data allocated(%dMB)\n", stats.data_allocated>>20));  
    tw(tr(TR_FUNC, TrTest, "Reclaim candidates: most-lost(%d), youngest(%d)\n",  
          stats.drec.most_lost, stats.drec.youngest));  
 
    tw(tr(TR_FUNC, TrTest, "Data reclaimed:     lost(%dMB), valid(%dMB)\n",  
          stats.drec.lost[0]>>20 , stats.drec.valid[0]>>20));  
 
    tw(tr(TR_FUNC, TrTest, "Inodes reclaimed:   num(%d), valid(%d), lost(%d)\n", 
          stats.irec.num, stats.irec.valid, stats.irec.lost)); 
 
    ttw(ttr(TTrTest, "Data allocated(%dMB)\n" NL, stats.data_allocated>>20));  
    ttw(ttr(TTrTest, "Reclaim candidates: most-lost(%d), youngest(%d)\n" NL,  
            stats.drec.most_lost, stats.drec.youngest));  
 
    ttw(ttr(TTrTest, "Data reclaimed:     lost(%dMB), valid(%dMB)\n" NL,  
          stats.drec.lost[0]>>20 , stats.drec.valid[0]>>20));  
 
    ttw(ttr(TTrTest, "Inodes reclaimed:   num(%d), valid(%d), lost(%d)\n" NL, 
          stats.irec.num, stats.irec.valid, stats.irec.lost)); 
} 
 
/****************************************************************************** 
 * Test and Expect Functions 
 ******************************************************************************/ 
 
int test_expect(int n, int xn) 
{ 
    if (n == xn) 
        return 0; 
 
    tw(tr(TR_FUNC, TrTest, 
          "ERROR: expect(%d,%d): got %d, '%s', expected %d, '%s'\n", 
          n, xn, n, ffs_strerror(n), xn, ffs_strerror(xn))); 
    ttw(ttr(TTrTest, "ERROR: expect(%d,%d)" NL, n, xn)); 
 
    return -1; 
} 
 
// Expect a return code >= 0 meaning EFFS_OK. 
int test_expect_ok(int n) 
{ 
    if (n >= 0) 
        return 0; 
 
    tw(tr(TR_FUNC, TrTest, 
          "ERROR: expect_ok(%d) got %d, '%s', expected >= EFFS_OK\n", 
          n, ffs_strerror(n))); 
    ttw(ttr(TTrTest, "ERROR: expect_ok(%d)" NL, n)); 
 
    return -1; 
} 
 
int test_expect_equal(int n, int xn) 
{ 
    if (n == xn) 
        return 0; 
 
    tw(tr(TR_FUNC, TrTest, "ERROR: got %d, expected %d\n", n, xn)); 
    ttw(ttr(TTrTest, "ERROR: expect_eq(%d,%d" NL, n, xn)); 
 
    return -1; 
} 
 
int test_expect_not_equal(int n, int xn) 
{ 
    if (n != xn) 
        return 0; 
 
    tw(tr(TR_FUNC, TrTest, "ERROR: expect_ne(%d)\n", n)); 
    ttw(ttr(TTrTest, "ERROR: expect_ne(%d)" NL, n)); 
 
    return -1; 
} 
 
int test_expect_greater_than(int n, int xn) 
{ 
    if (n > xn) 
        return 0; 
 
    tw(tr(TR_FUNC, TrTest, "ERROR: expect_gt(%d,%d) got %d but expected > %d\n", 
          n, xn, n, xn)); 
    ttw(ttr(TTrTest, "ERROR: expect_gt(%d,%d)" NL, n, xn)); 
 
    return -1; 
} 
 
int test_expect_data(const void *data1, const void *data2, int size) 
{ 
    if (memcmp(data1, data2, size) == 0) 
        return 0; 
     
    tw(tr(TR_FUNC, TrTest, 
          "ERROR: expect_data(%d) got unexpected data\n", size)); 
    ttw(ttr(TTrTest, "ERROR: expect_data(%d)" NL, size)); 
 
    return -1; 
} 
 
// Check that contents of file with name  is the same as  of 
// size . 
int test_expect_file(const char *name, const void *data, int size) 
{ 
    test.numcalls++; 
    if (size > bigbuf_size) { 
        tw(tr(TR_FUNC, TrTest, "WARNING: expect_file(%d) buffer too small\n", 
              size)); 
        ttw(ttr(TTrTest, "WARNING: expect_file(%d) buffer too small" NL, size)); 
#if (TARGET == 1) 
        return 0; 
#endif 
        return -1; 
    } 
         
    error = ffs_file_read(name, bigbuf, size); 
    if (test_expect_greater_than(error, EFFS_OK - 1)) 
        return -1; 
    return test_expect_data(bigbuf, data, size); 
} 
 
int test_expect_state(struct ffs_state_s *old, struct ffs_state_s *new) 
{ 
    int old_total, new_total; 
 
    old_total = old->inodes_used - old->inodes_lost; 
    new_total = new->inodes_used - new->inodes_lost; 
 
    if (old->objects_total == new->objects_total && 
        old->objects_total == new_total && 
        new->objects_total == old_total && 
        old->bytes_used == new->bytes_used && 
        old->bytes_lost == new->bytes_lost && 
        old->bytes_free == new->bytes_free) { 
        return 0; 
    } 
 
    ttw(str(TTrTest, "ERROR: ffs state mismatch:" NL NL)); 
    tw(tr(TR_FUNC, TrTest, "ERROR: ffs state mismatch:\n\n")); 
    test_state_objects_print(old, new); 
    test_state_bytes_print(old, new); 
 
    return -1; 
} 
 
// Check if number of objects is unchanged 
int test_expect_objects(struct ffs_state_s *old, struct ffs_state_s *new) 
{ 
    int old_total, new_total; 
 
    test_ffs_state_get(new); 
 
    old_total = old->inodes_used - old->inodes_lost; 
    new_total = new->inodes_used - new->inodes_lost; 
 
    if (old->objects_total == new->objects_total && 
        old->objects_total == new_total && 
        new->objects_total == old_total) { 
        return 0; 
    } 
 
    ttw(ttr(TTrTest, "ERROR: expect_objects(%d, %d, %d, %d, %d, %d)" NL)); 
    tw(tr(TR_FUNC, TrTest, "ERROR: ffs state mismatch:\n\n")); 
    test_state_objects_print(old, new); 
 
    return -1; 
} 
 
 
/****************************************************************************** 
 * FFS Functions 
 ******************************************************************************/ 
 
effs_t tffs_file_write(const char *name, void *addr, int size, uint16 option) 
{ 
    test.numcalls++; 
    return ffs_file_write(name, addr, size, option); 
} 
 
effs_t tffs_mkdir(const char *name) 
{ 
    test.numcalls++; 
    return ffs_mkdir(name); 
} 
 
effs_t tffs_symlink(const char *name, const char *actualpath) 
{ 
    test.numcalls++; 
    return ffs_symlink(name, actualpath); 
} 
 
effs_t tffs_remove(const char *name) 
{ 
    test.numcalls++; 
    return ffs_remove(name); 
} 
 
effs_t tffs_fcontrol(const char *name, int8 action, uint16 param) 
{ 
    test.numcalls++; 
    return ffs_fcontrol(name, action, param); 
} 
 
effs_t tffs_preformat(uint16 magic) 
{ 
    test.numcalls++; 
    return ffs_preformat(magic); 
} 
 
effs_t tffs_format(const char *name, uint16 magic) 
{ 
    test.numcalls++; 
    return ffs_format(name, magic); 
} 
 
 
int tffs_fread(const char *name, void *addr, int size) 
{ 
    test.numcalls++; 
    return ffs_file_read(name, addr, size); 
} 
 
int tffs_file_read(const char *name, void *addr, int size) 
{ 
    test.numcalls++; 
    return ffs_file_read(name, addr, size); 
} 
 
int tffs_opendir(const char *name, struct dir_s *dir) 
{ 
    test.numcalls++; 
    return ffs_opendir(name, dir); 
} 
 
int tffs_readdir (struct dir_s *dir, char *name, int8 size) 
{ 
    test.numcalls++; 
    return ffs_readdir(dir, name, size); 
} 
 
int tffs_readlink(const char *name, char *addr, int size) 
{ 
    test.numcalls++; 
    return ffs_readlink(name, addr, size); 
} 
 
int tffs_rename(const char *oldname, const char *newname) 
{ 
    test.numcalls++; 
    return ffs_rename(oldname, newname); 
} 
 
effs_t tffs_stat(const char *name, struct stat_s *stat) 
{ 
    test.numcalls++; 
    return ffs_stat(name, stat); 
} 
 
effs_t tffs_fstat(fd_t fdi, struct stat_s *stat) 
{ 
    test.numcalls++; 
    return ffs_fstat(fdi, stat); 
} 
 
effs_t tffs_linkstat(const char *name, struct stat_s *stat) 
{ 
    test.numcalls++; 
    return ffs_lstat(name, stat); 
} 
 
effs_t tffs_lstat(const char *name, struct stat_s *stat) 
{ 
    test.numcalls++; 
    return ffs_lstat(name, stat); 
} 
 
effs_t tffs_xlstat(const char *name, struct xstat_s *stat) 
{ 
    test.numcalls++; 
    return ffs_xlstat(name, stat); 
} 
 
effs_t tffs_query(int8 query, void *p) 
{ 
    return ffs_query(query, p); 
} 
 
 
effs_t tffs_initialize(void) 
{ 
    effs_t myerror; 
    struct ffs_stats_s old_stats; 
 
    test.numcalls++; 
 
    memcpy(&old_stats, &stats, sizeof(struct ffs_stats_s)); 
    myerror = ffs_initialize(); 
    if (myerror < 0 && myerror != EFFS_NOFORMAT) { 
        tw(tr(TR_FUNC, TrTest, "ffs_initialize() error = %d\n", myerror)); 
        exit(1); 
    } 
    memcpy(&stats, &old_stats, sizeof(struct ffs_stats_s)); 
 
    test_ffs_params_get(); 
    return myerror; 
} 
 
effs_t tffs_exit(void) 
{ 
    test.numcalls++; 
    return ffs_exit(); 
} 
 
fd_t tffs_open(const char *pathname, ffs_options_t options) 
{ 
    test.numcalls++; 
    return ffs_open(pathname, options); 
} 
 
effs_t tffs_close(fd_t fdi) 
{ 
    test.numcalls++; 
    return ffs_close(fdi); 
} 
 
int tffs_write(fd_t fdi, void *addr, int size) 
{ 
   test.numcalls++; 
   return ffs_write(fdi, addr, size); 
} 
 
int tffs_read(fd_t fdi, void *addr, int size) 
{ 
   test.numcalls++; 
   return ffs_read(fdi, addr, size); 
} 
 
int tffs_seek(fd_t fdi, int offset, int whence) 
{ 
  test.numcalls++; 
  return ffs_seek(fdi, offset, whence); 
} 
 
effs_t tffs_truncate(const char *path, offset_t length)  
{ 
    test.numcalls++; 
    return ffs_truncate(path, length);  
} 
 
effs_t tffs_ftruncate(fd_t fdi, offset_t length)  
{ 
    test.numcalls++; 
    return ffs_ftruncate(fdi, length);  
} 
 
effs_t tffs_fdatasync(fd_t fdi)  
{ 
    test.numcalls++; 
    return ffs_fdatasync(fdi);  
} 
 
/****************************************************************************** 
 * Powerfail framework 
 ******************************************************************************/ 
 
#if (TARGET == 0) 
void tmp_fix_trace_indent(void); 
 
struct powerfail_s powerfail; 
 
// Set the mode, offset, mask in the global powerfail structure and reset 
// the rest of the powerfail struct. Note the mode PFM_RANDOM will interpret 
// the offset parameter as the average number of words to write between each 
// power fail. 
void powerfail_test_begin(int mode, uint16 offset, int mask) 
{ 
    tw(tr(TR_FUNC, TrPowerfail, "powerfail_test_begin(0x%x, 0x%x, 0x%x) \n",  
          mode, offset, mask)); 
 
    memset(&powerfail, 0 ,sizeof(powerfail)); 
    powerfail.mode   = mode; 
    powerfail.offset = offset; 
    powerfail.bytemask   = mask; 
} 
 
// NOTEME change to macro? 
void powerfail_test_end(void) 
{ 
    tw(tr(TR_FUNC, TrPowerfail, "powerfail_test_end() \n")); 
    powerfail.mode = 0; 
    powerfail.enable = 0; 
    // NOTEME: Display stats? 
} 
 
 
// NOTEME change to macro?   
// In the start of the journal code and in other critical places (start of 
// each domain), the below function is called. The function enables and 
// disables powerfail. It is important that it called before any driver 
// write in the current domain. 
void powerfail_domain_begin(int domain) 
{ 
    if (powerfail.mode == 0)  
        return; 
 
    tw(tr(TR_FUNC, TrPowerfail, "powerfail_begin_domain(0x%x)\n", domain)); 
 
    powerfail.enable = powerfail.mode & domain; 
} 
 
// NOTEME change to macro? 
// This function is called at the end of each domain. 
void powerfail_domain_end(void) 
{ 
    if (powerfail.enable == 0)  
        return; 
 
    tw(tr(TR_FUNC, TrPowerfail, "powerfail_end_domain()\n"));  
 
    powerfail.enable = 0; 
} 
 
int powerfail_suspend_test(void) 
{ 
    int mode = powerfail.mode; 
    powerfail.mode = 0; 
    powerfail.enable = 0; 
    return mode; 
} 
 
void powerfail_resume_test(int mode) 
{ 
    powerfail.mode = mode; 
} 
 
 
// The below function sets the absolute address used for powerfail. It is 
// important that the function powerfail_set_domain() is called before this 
// function as that function determine if the powerfail is enabled or not 
// for this domain. Typical 'addr' will be the address of a inode or block 
// header. 
void powerfail_set_addr(int addr) 
{ 
    if (POWERFAIL_ENABLED) { 
        tw(tr(TR_FUNC, TrPowerfail, "powerfail_set_addr(0x%x)\n", addr));  
 
        if (powerfail.mode & PFM_RANDOM)  
            return;  // Skip because this test do not use addr 
 
        else if (powerfail.mode & PFM_NEXTINODE)  
            powerfail.addr = powerfail.offset + (int)inode_addr(fs.journal.i); 
 
        else if (powerfail.mode & PFM_DIRI)  
            powerfail.addr = powerfail.offset + (int)inode_addr(fs.journal.diri); 
 
        else if (powerfail.mode & PFM_OLDI)  
            powerfail.addr = powerfail.offset + (int)inode_addr(fs.journal.oldi); 
 
        else if (powerfail.mode & PFM_JOURNAL) { 
            struct inode_s *ip = inode_addr(fs.ijournal); 
            powerfail.addr = (int)offset2addr(location2offset(ip->location) +  
                                         fs.journal_pos); 
        } 
        else if (powerfail.mode & PFM_BLOCKHEADER) 
            powerfail.addr = powerfail.offset + addr; 
        else { 
            tw(tr(TR_FUNC, TrAll, "unknown powerfail mode\n")); 
            // NOTEME use other error code? 
            powerfail_fatal_error(-1); 
        } 
 
        // Aligned to halfword? 
        if (powerfail.addr & 1) { 
            // Not aligned, aligne addr and fix mask 
            powerfail.addr--; 
            powerfail.halfwordmask = powerfail.bytemask << 8; 
        } 
        else 
            powerfail.halfwordmask = powerfail.bytemask; 
 
        tw(tr(TR_FUNC, TrPowerfail, "pf addr 0x%x, mask 0x%x \n", powerfail.addr,  
              powerfail.halfwordmask));  
    } 
} 
 
// The following function is called conditionally from within the driver's 
// write function. The condition gating the call is (powerfail.mode > 0 and 
// powerfail.enable > 0). 
void powerfail_write(volatile uint16 *addr, uint16 value)  
{ 
    int mode; 
     
   tw(tr(TR_FUNC, TrPowerfail, "powerfail_write(0x%x, 0x%x)\n", addr, value)); 
   
   if (powerfail.mode & PFM_RANDOM) { 
        // Not all these calls trigger a powerfail.. 
        if ((rand() % powerfail.offset) > 0) 
            return;  // Skip powerfail 
        powerfail_simulate_random_write_failures(addr, value); 
   } 
   else if (powerfail.mode & (PFM_INODES | PFM_JOURNAL | PFM_BLOCKHEADER)) { 
       // Is this the addr and is the mask bits changed? 
       if ((int)addr != powerfail.addr || ((*addr ^ value) & powerfail.halfwordmask) == 0) 
           return;  // Skip powerfail  
       powerfail_simulate_write_failures(addr, value);  
   } 
   else { 
        tw(tr(TR_FUNC, TrAll, "Unknown powerfail_mode\n")); 
        // NOTEME use other error code 
        powerfail_fatal_error(-2);  
   } 
 
   tmp_fix_trace_indent(); 
 
   powerfail.step++; 
 
   if (!(powerfail.mode & PFM_NOINIT)) { 
       mode = powerfail_suspend_test();  
 
       // Now re-initialise FFS. Then resume at the point we were before. 
       // We should ensure that all static variables of ffs are zeroed during 
       // re-initialization!   
       tw(tr(TR_FUNC, TrPowerfail,"re-initialise FFS (step %d)\n", powerfail.step)); 
       memset(&fs, 0, sizeof(struct fs_s)); 
       ffs_initialize(); 
 
       powerfail_resume_test(mode); 
   } 
 
   tw(tr(TR_FUNC, TrPowerfail, "jump..\n"));  
   longjmp(powerfail.env, 0); 
} 
 
 
// This function is used to simulate the Intel Strata Flash. It only 
// corrupts the cells that are modified by the write and it only corrupts 
// the cells in the way it can happen in a real device e.g. if the existing 
// cell level is 0 (11b) and a write of level 2 (01b) is corrupted, the 
// function will "corrupt" the cell by setting the state to either 0, 1 or 2 
// and not to 3 (00b) as this is not how the Strata flash work. 
int powerfail_simulate_random_write_failures(volatile uint16 *addr, uint16 src) 
{ 
    int i; 
    uint16 out_cell, flash_cell, src_cell; 
    uint16 mask, out; 
    uint16 diff; 
 
    out = *addr; 
 
    tw(tr(TR_FUNC, TrPowerfail, "pf_sim_random_wr_fail(0x%x, 0x%x)\n",  
          addr, src)); 
  
    tw(tr(TR_FUNC, TrTest,  
          "POWER FAIL AT ADDR: 0x%x. Before 0x%x\n" 
          "\t\t\t\t  Wanted 0x%x\n", addr, *addr, src)); 
 
    if (~*addr & src) { 
        ffsdrv_write_error(*addr, src); 
        // NOTEME use other error code? 
        powerfail_fatal_error(-3); 
    } 
 
    // Isolate and corrupt cell per cell  
    for (i = 0; i < 16; i++) { 
        // Move flash and src down to lowest 'cell'  
        flash_cell = *addr >> (i * 2); 
        src_cell   = src   >> (i * 2); 
        // Maskout all bits except from the 2 lowest bits 
        flash_cell &=  0x03;   
        src_cell   &= 0x03; 
 
        diff = flash_cell - src_cell; 
 
        if (diff == 0) 
            continue;  // Skip, not possible to corrupt the cell 
 
        out_cell = flash_cell - (rand() % (diff + 1)); 
 
        if (out_cell < src_cell) { 
            tw(tr(TR_NULL, TrTest,"Bad out_cell, src 0x%x, out 0x%x\n",  
                  src_cell, out_cell)); 
            // NOTEME use other error code? 
            powerfail_fatal_error(-4); 
        } 
 
        // Move the cell back to the original place  
        out_cell = out_cell << (i * 2); 
 
        // Set all bits except from the current cell 
        mask = 0x3 << (i * 2);  
        out_cell |= ~mask; 
 
        // Added the result to the output data 
        out = out_cell & out; 
    } 
 
    tw(tr(TR_NULL, TrTest,"\t\t\t\t  Result 0x%x\n", out)); 
 
    if (~*addr & out) { 
        ffsdrv_write_error(*addr, out); 
        // NOTEME use other error code? 
        powerfail_fatal_error(-5); 
    } 
 
    *addr = out; 
 
    return EFFS_OK; 
} 
 
// Simulate write failures if the powerfail.mask bits are changed by src. If 
// the mask bits are modified by src, 'write' the mask bits and return 0. If 
// the mask bits not are valid, trace and return an error. The mask bits are 
// invalid if the mask 'cell' level is higher than src. 
int powerfail_simulate_write_failures(volatile uint16 *addr, uint16 src) 
{ 
    int i; 
    uint16 out_cell, flash_cell, src_cell, mask_cell; 
    uint16 out; 
     
    out = *addr; 
 
    tw(tr(TR_FUNC, TrPowerfail, "pf_sim_wr_fail(0x%x, 0x%x)\n", addr, src)); 
  
    tw(tr(TR_FUNC, TrTest, "POWER FAIL AT ADDR: 0x%x. Before 0x%x\n" 
          "\t\t\t\t  Wanted 0x%x\n", addr, *addr, src)); 
 
    // Isolate and corrupt cell per cell  
    for (i = 0; i < 16; i++) { 
        // Move flash and src down to lowest 'cell'  
        flash_cell = *addr >> (i * 2); 
        src_cell   = src   >> (i * 2); 
        mask_cell  = ~(powerfail.halfwordmask  >> (i * 2)); 
        // Maskout all bits except from the 2 lowest bits 
        flash_cell &=  0x03;   
        src_cell   &= 0x03; 
        mask_cell   &= 0x03; 
 
        // The cell have changed and is marked by mask so check if the mask 
        // is valid. e.g. a write of '10b' cannot be set to '00b' by mask 
        if (mask_cell < src_cell) { 
            tw(tr(TR_FUNC, TrAll, "FATAL invalid mask: 0x%x\n", powerfail.halfwordmask)); 
            // NOTEME use other error code? 
            powerfail_fatal_error(-6); 
        } 
 
        // Would the cell have been changed by src and is it marked by mask? 
        if ((flash_cell == src_cell) || (mask_cell == 3)) 
            continue;  // Skip, the cell will not be changed by src or the 
                       // cell is not marked by mask 
 
        // Move the cell back to the original place  
        out_cell = mask_cell << (i * 2); 
 
        // Set all bits except from the current cell 
        out_cell |= ~(0x3 << (i * 2));  
 
        // Added the result to the output data 
        out &= out_cell; 
    } 
 
    tw(tr(TR_NULL, TrTest,"\t\t\t\t  Result 0x%x\n", out)); 
    *addr = out; 
 
    return EFFS_OK; 
} 
 
// PFM_ERASE: makes powerfail while erasing the block specified in pf 
// variable 'offset'. Instead of erasing the flash it is filled with the 
// pattern applied by the pf 'bytemask' (e.g. 0xFF, 0x0, 0xAA etc). 
 
// PFM_NEXTERASE: makes powerfail at the next block erase. The 'bytemask 'is 
// used to fill the block. 
 
// PFM_RANDOM: the 'offset' value determine how often it triggers a 
// powerfail. Difference patterns are used to fill the block */ 
void powerfail_erase(uint8 block) 
{ 
    int i, mode; 
    uint8 *addr; 
 
    tw(tr(TR_FUNC, TrPowerfail, "powerfail_erase(%d)\n", block)); 
 
    if (powerfail.mode & PFM_RANDOM) { 
        if ((rand() % powerfail.offset) > 0) 
            return;  // Skip powerfail 
         
        powerfail.bytemask = rand() % 0xFF; 
    } 
    else if (powerfail.mode & PFM_ERASE) { 
        if (powerfail.offset != block) 
            return; // Skip powerfail 
    } 
    else if (powerfail.mode & PFM_NEXTERASE) { 
        // offset is not used, bytemask is set in powerfail_test_begin()  
    } 
 
    tw(tr(TR_FUNC, TrTest, "POWER FAIL AT BLOCK ERASE: %d \n", block)); 
 
    // Fill the block with the bytemask 
    addr = block2addr(block); 
    for (i = 0; i < 1 << dev.binfo[block].size_ld; i++) { 
        *addr++ = powerfail.bytemask; 
    } 
 
    powerfail.step++; 
     
    if (!(powerfail.mode & PFM_NOINIT)) { 
        mode = powerfail_suspend_test();  
 
        // Now re-initialise FFS. Then resume at the point we were before. 
        // We should ensure that all static variables of ffs are zeroed during 
        // re-initialization!   
        tw(tr(TR_FUNC, TrPowerfail,"re-initialise FFS (step %d)\n", powerfail.step)); 
        memset(&fs, 0, sizeof(struct fs_s)); 
        ffs_initialize(); 
 
        powerfail_resume_test(mode); 
    } 
 
    tw(tr(TR_FUNC, TrPowerfail, "jump..\n"));  
    longjmp(powerfail.env, 0); 
 
} 
 
void powerfail_fatal_error(int error) 
{ 
    tw(tr(TR_FUNC, TrAll, "FATAL pf error %d\n", error)); 
    ttw(ttr(TTrFatal, "FATAL pf error %d" NL, error)); 
 
    powerfail_display_stats(); 
 
    exit(1); 
} 
 
void powerfail_display_stats(void) 
{ 
    tw(tr(TR_FUNC, TrTest, "powerfail: \n")); 
    tw(tr(TR_FUNC, TrTest, " mode:     0x%x\n", powerfail.mode)); 
    tw(tr(TR_FUNC, TrTest, " boundary: 0x%x\n", powerfail.boundary)); 
    tw(tr(TR_FUNC, TrTest, " step:     0x%x\n", powerfail.step)); 
 
    ttw(ttr(TTrAll, "powerfail:" NL)); 
    ttw(ttr(TTrAll, "mode:     0x%x" NL, powerfail.mode)); 
    ttw(ttr(TTrAll, "boundary: 0x%x" NL, powerfail.boundary)); 
    ttw(ttr(TTrAll, "step:     0x%x" NL, powerfail.step)); 
} 
 
void tmp_fix_trace_indent(void) 
{ 
    if (powerfail.boundary == JOURNAL_TEST_COMMITTING) 
        tw(tr(TR_END, TrJournal, "} dummy \n")); 
 
    if (powerfail.boundary == JOURNAL_TEST_READY) 
        tw(tr(TR_END, TrJournal, "} dummy \n")); 
 
 
    tw(tr(TR_END, TrJournal, "} dummy \n")); 
} 
 
#endif