fileif.c 4.19 KB
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <PR/bbfs.h>
#include "ecc256.h"
#include "flashif.h"

static void*
fs_create(void**f, const char* file, int blocks) {
    int fd, i, bad = 0;
    FILE* fp;
    float bad_threshold = 0.f;
    u8 page[BB_FL_PAGE_SIZE], spare[BB_FL_SPARE_SIZE];
    if ((fd = open(file, O_RDWR|O_CREAT, 0666)) < 0) {
	return 0;
    }
    if ((fp = fdopen(fd, "r+b")) == 0) {
	close(fd);
	return 0;
    }
    if (getenv("FLASH_BADBLOCKS"))
	bad_threshold = atof(getenv("FLASH_BADBLOCKS"));
    memset(page, 0xff, sizeof page);
    memset(spare, 0xff, sizeof spare);
    for(i = 0; i < blocks; i++) {
	double drand48(void);
	int j;
	/* create 1% bad blocks */
	if (drand48() < bad_threshold) {
	    spare[BB_FL_BLOCK_STATUS_OFF] = 0;
	    spare[BB_FL_ECC0_OFF] = 0;
	    bad++;
	} else {
	    spare[BB_FL_BLOCK_STATUS_OFF] = 0xff;
	    spare[BB_FL_ECC0_OFF] = 0xff;
	}
	for(j = 0; j < BB_FL_BLOCK_PAGES; j++) {
	    fwrite(page, 1, sizeof page, fp);
	    fwrite(spare, 1, sizeof spare, fp);
	}
    }
    fflush(fp);
    //printf("created %d %.2f%% bad blocks\n", bad, bad*100./blocks);
    return *f = fp;
}

static void*
fs_open(void**f, const char* file, int create, int blocks) {
    FILE* fp;
    if ((fp = fopen(file, "r+b")) == 0 && create) {
	return fs_create(f, file, blocks);
    }
    return *f = fp;
}

static void
fs_close(void *f) {
    fclose((FILE*)f);
}

static int
fs_blocks(void *f) {
    FILE* fp = f;
    struct stat sb;
    if (fstat(fileno(fp), &sb) < 0) return -1;
    return sb.st_size/((BB_FL_PAGE_SIZE+BB_FL_SPARE_SIZE)*BB_FL_BLOCK_PAGES);
}

static int
fs_read_block(void* f, u32 addr, void* data, void* spare, int ecc) {
    u8 s0[BB_FL_SPARE_SIZE], calc_ecc[6];
    int i;
    u32 block_start = addr*(BB_FL_BLOCK_SIZE+BB_FL_BLOCK_PAGES*BB_FL_SPARE_SIZE);
    FILE* fp = f;

//    VERBOSE(("read block: %d\n", addr));
    /* check if it is a bad block */
    fseek(fp, block_start + BB_FL_PAGE_SIZE, 0);
    fread(s0, 1, sizeof s0, fp);
    if (s0[BB_FL_BLOCK_STATUS_OFF] != 0xff) {
//	VERBOSE(("read %d bad\n", addr));
	return -2;
    }

    for(i = 0; i < BB_FL_BLOCK_PAGES; i++) {
	if (data) {
	    fseek(fp, block_start+i*(BB_FL_PAGE_SIZE+BB_FL_SPARE_SIZE), 0);
	    fread(data+i*BB_FL_PAGE_SIZE, 1, BB_FL_PAGE_SIZE, fp);
	}
	fseek(fp, block_start+i*(BB_FL_PAGE_SIZE+BB_FL_SPARE_SIZE)+BB_FL_PAGE_SIZE, 0);
	fread(s0, 1, sizeof s0, fp);
	if (data && ecc == YES_ECC) {
	    ecc256_calculate(data+i*BB_FL_PAGE_SIZE, calc_ecc);
	    ecc256_calculate(data+i*BB_FL_PAGE_SIZE+256, calc_ecc+3);
	    if (ecc256_correct(data+i*BB_FL_PAGE_SIZE, s0+BB_FL_ECC0_OFF, calc_ecc) < 0 ||
		ecc256_correct(data+i*BB_FL_PAGE_SIZE+256, s0+BB_FL_ECC1_OFF, calc_ecc+3) < 0)
		return -2;
	}
    }
    if (spare) memcpy(spare, s0, sizeof s0);
    return 0;
}

/*
 * write a block to the flash device.  if spare is non-zero, use it
 * as the spare area data for all pages.  If ecc is enabled, compute
 * the ecc and overlay the bytes in the spare area.
 */
static int
fs_write_block(void* f, u32 addr, const void* data, const void* spare, int ecc) {
    u8 s0[BB_FL_SPARE_SIZE];
    int i;
    u32 block_start = addr*(BB_FL_BLOCK_SIZE+BB_FL_BLOCK_PAGES*BB_FL_SPARE_SIZE);
    FILE* fp = f;

    /* check if it is a bad block */
    fseek(fp, block_start+BB_FL_PAGE_SIZE, 0);
    fread(s0, 1, sizeof s0, fp);
    if (s0[BB_FL_BLOCK_STATUS_OFF] != 0xff) {
//	VERBOSE(("write %d bad\n", addr));
	return -2;
    }	
//    VERBOSE(("write %d good\n", addr));

    if (!spare)
	memset(s0, 0xff, sizeof s0);
    else
	memcpy(s0, spare, sizeof s0);

    fseek(fp, block_start, 0);
    for(i = 0; i < BB_FL_BLOCK_PAGES; i++) {
	fwrite(data+i*BB_FL_PAGE_SIZE, 1, BB_FL_PAGE_SIZE, fp);
	if (ecc == YES_ECC) {
	    ecc256_calculate(data+i*BB_FL_PAGE_SIZE, s0+BB_FL_ECC0_OFF);
	    ecc256_calculate(data+i*BB_FL_PAGE_SIZE+256, s0+BB_FL_ECC1_OFF);
	}
	fwrite(s0, 1, BB_FL_SPARE_SIZE, fp);
    }
    return 0;
}

flashif_t*
new_fileif(void) {
    flashif_t* f = malloc(sizeof(flashif_t));
    if (f) {
	memset(f, 0, sizeof(flashif_t));
	f->open = fs_open;
	f->close = fs_close;
	f->blocks = fs_blocks;
	f->read_block = fs_read_block;
	f->write_block = fs_write_block;
    }
    return f;
}