/*-------------------------------------------------------------------------------------*\

    $Header: //avm-fs01/Entwicklung/Basis/Generierung/AR7/filesystem.generation/avm-src/tffs-test/rcs/flashtest.c 1.1 2003/11/24 14:49:05Z mpommerenke Exp $

    $Id: flashtest.c 1.1 2003/11/24 14:49:05Z mpommerenke Exp $

    $Log: flashtest.c $
    Revision 1.1  2003/11/24 14:49:05Z  mpommerenke
    Initial revision

\*-------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include "tffs.h"
#include <string.h>

#define DEFINE_CRC32_TABLE
#include "crc32-8.h"

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#define RUN_CLOCK_VERSION   2

/*--- #define DEBUG ---*/

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if defined(DEBUG)
#define DBG(a)  { printf a ; fflush(stdout); }
#else /*--- #if defined(DEBUG) ---*/
#define DBG(a)  
#endif /*--- #else ---*/ /*--- #if defined(DEBUG) ---*/

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
struct _run_clock_hours {
    unsigned int bits;
} clock_hours;

struct _run_clock_days {
    unsigned int bits;
} clock_days;

struct _run_clock_mounths {
    unsigned int bits;
} clock_mounths;

struct _run_clock_years {
    unsigned int bits;
} clock_years;

struct _run_clock_start_major {
    unsigned int bits[2];
} clock_restart_major;

struct _run_clock_start_minor {
    unsigned int bits;
} clock_restart_minor;

char *program;


/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void init_value(void *buffer, unsigned int size) {
    DBG(("init_value(%u bytes)\n", size));
    memset(buffer, 0xFF, size);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
unsigned int bits_unset(unsigned char *ptr, unsigned int max_bits) {
    unsigned count;
    unsigned unset = 0;

    DBG(("bits_unset(%u max_bits):", max_bits));
    while(max_bits) {
        for(count = 0 ; count < 8 ; count++, max_bits--) {
            if(*ptr & (1 << count)) {
                DBG((" unset=%u\n", unset));
                return unset;
            }
            unset++;
        }
        ptr++;
    }
    DBG((" unset=%u (empty)\n", unset));
    return unset;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void unset_next_bit(unsigned char *ptr, unsigned int max_bits) {
    unsigned count;

    DBG(("unset_next_bit(%u max_bits):", max_bits));
    /*--- DBG(("*ptr: 0x%08x\n", *(unsigned int *)ptr)); ---*/
    while(max_bits) {
        for(count = 0 ; count < 8 ; count++, max_bits--) {
            if(*ptr & (1 << count)) {
                *ptr &= ~(1 << count);
                DBG((" %u left\n", max_bits));
                return;
            }
        }
        ptr++;
    }
    DBG((" error: all unset\n"));
    return;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void korr_boot(char *device) {
    char Version[4];
    int fd_read, fd_write;

    fd_read = tffs_open(O_RDONLY);
    if(fd_read < 0)
        return;
    fd_write = tffs_open(O_WRONLY);
    if(fd_write < 0) {
        tffs_close(fd_read);
        return;
    }

    if(tffs_read_value(fd_read, FLASH_FS_VERSION_COUNTER,   Version, sizeof(Version)) == 0) {
        /*--- keine Version vorhanden ---*/
        goto do_korr_boot;
    }
    if(*((unsigned int *)Version) == RUN_CLOCK_VERSION) {
        tffs_close(fd_read);
        tffs_close(fd_write);
        return; /*--- schon neue Version ---*/
    }

do_korr_boot:
    *((unsigned int *)Version) = RUN_CLOCK_VERSION;
    DBG(("korr_boot(%s): version=%u\n", device, *((unsigned int *)Version)));
    tffs_write_value(fd_write, FLASH_FS_VERSION_COUNTER,   Version, sizeof(Version));

    if(tffs_read_value(fd_read, FLASH_FS_REBOOT_MINOR,   &clock_restart_minor, sizeof(clock_restart_minor)) == 0) {
        tffs_close(fd_read);
        tffs_close(fd_write);
        DBG(("korr_boot(%s): error kein minor\n", device));
        return; /*--- kein MINOR Wert vorhanden, nichts zu korrigieren ---*/
    }

    if(bits_unset((unsigned char *)&clock_restart_minor, 32) == 32) {
        unsigned int i;
        if(tffs_read_value(fd_read, FLASH_FS_REBOOT_MAJOR,   &clock_restart_major, sizeof(clock_restart_major)) == 0) {
            tffs_close(fd_read);
            tffs_close(fd_write);
            DBG(("korr_boot(%s): error kein major\n", device));
            return; /*--- kein MAJOR Wert vorhanden, nichts zu korrigieren ---*/
        }
        i = bits_unset((unsigned char *)&clock_restart_minor, 32) + 
            bits_unset((unsigned char *)&clock_restart_major, 64);

        DBG(("korr_boot(%s): %u reboots\n", device, i));

        init_value(&clock_restart_minor, sizeof(clock_restart_minor));
        init_value(&clock_restart_major, sizeof(clock_restart_major));

        DBG(("korr_boot(%s): minor=%u\n", device, i % 32));
        while(i % 32) {
            unset_next_bit((unsigned char *)&clock_restart_minor, 32);
            i--;
        }

        i /= 32;
        DBG(("korr_boot(%s): major=%u\n", device, i));
        while(i) {
            unset_next_bit((unsigned char *)&clock_restart_major, 64);
            i--;
        }

        tffs_write_value(fd_write, FLASH_FS_REBOOT_MAJOR,   &clock_restart_major, sizeof(clock_restart_major));
        tffs_write_value(fd_write, FLASH_FS_REBOOT_MINOR,   &clock_restart_minor, sizeof(clock_restart_minor));
    }
    tffs_close(fd_read);
    tffs_close(fd_write);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void write_boot(char *device) {
    int fd_read, fd_write;
    fd_read = tffs_open(O_RDONLY);
    if(fd_read < 0)
        return;
    fd_write = tffs_open(O_WRONLY);
    if(fd_write < 0) {
        tffs_close(fd_read);
        return;
    }

    DBG(("write_boot(%s)\n", device));

    if(tffs_read_value(fd_read, FLASH_FS_REBOOT_MINOR,   &clock_restart_minor, sizeof(clock_restart_minor)) == 0) {
        init_value(&clock_restart_minor, sizeof(clock_restart_minor));
    }
    unset_next_bit((unsigned char *)&clock_restart_minor, 32);

    if(bits_unset((unsigned char *)&clock_restart_minor, 32) == 32) {
        init_value(&clock_restart_minor, sizeof(clock_restart_minor));

        if(tffs_read_value(fd_read, FLASH_FS_REBOOT_MAJOR,   &clock_restart_major, sizeof(clock_restart_major)) == 0) {
            init_value(&clock_restart_major, sizeof(clock_restart_major));
        }
        unset_next_bit((unsigned char *)&clock_restart_major, 64);

        tffs_write_value(fd_write, FLASH_FS_REBOOT_MAJOR,   &clock_restart_major, sizeof(clock_restart_major));
    }
    tffs_write_value(fd_write, FLASH_FS_REBOOT_MINOR,   &clock_restart_minor, sizeof(clock_restart_minor));

    tffs_close(fd_read);
    tffs_close(fd_write);
    return ;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void check_debug_cfg(void) {
    int fd = open("/var/flash/debug.cfg", O_RDONLY);
    if(fd > 0) {
        int fd_write;
        int dirty = 1;
        close(fd);
        fd_write = tffs_open(O_WRONLY);
        if(fd_write < 0) {
            return;
        }
        tffs_write_value(fd_write, 0x57, &dirty, sizeof(dirty));
        tffs_close(fd_write);
    }
    return;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void every_hour(char *device) {
    int fd_read, fd_write;
    fd_read = tffs_open(O_RDONLY);
    if(fd_read < 0)
        return;
    fd_write = tffs_open(O_WRONLY);
    if(fd_write < 0) {
        tffs_close(fd_read);
        return;
    }

    DBG(("every_hour(%s)\n", device));

    /*--- Stunden ---*/
    if(tffs_read_value(fd_read, FLASH_FS_HOUR_COUNTER,   &clock_hours, sizeof(clock_hours)) == 0) {
        init_value(&clock_hours, sizeof(clock_hours));
    }
    unset_next_bit((unsigned char *)&clock_hours, 24);

    /*--- sind Stunden voll, wenn ja dann zurücksetzen ---*/
    if(bits_unset((unsigned char *)&clock_hours, 24) == 24) {
        init_value(&clock_hours, sizeof(clock_hours));

        /*--- Tage ---*/
        if(tffs_read_value(fd_read, FLASH_FS_DAY_COUNTER,    &clock_days, sizeof(clock_days)) == 0) {
            init_value(&clock_days, sizeof(clock_days));
        }
        unset_next_bit((unsigned char *)&clock_days, 31);

        /*--- sind Tage voll, wenn ja zurücksetzen ---*/
        if(bits_unset((unsigned char *)&clock_days, 31) == 31) {
            init_value(&clock_days, sizeof(clock_days));

            /*--- Monate ---*/
            if(tffs_read_value(fd_read, FLASH_FS_MOUNTH_COUNTER, &clock_mounths, sizeof(clock_mounths)) == 0) {
                init_value(&clock_mounths, sizeof(clock_mounths));
            }
            unset_next_bit((unsigned char *)&clock_mounths, 12);

            /*--- sind Monate voll, wenn ja zurücksetzen ---*/
            if(bits_unset((unsigned char *)&clock_mounths, 12) == 12) {
                init_value(&clock_mounths, sizeof(clock_mounths));

                /*--- Jahre (laufen nie über) ---*/
                if(tffs_read_value(fd_read, FLASH_FS_YEAR_COUNTER,   &clock_years, sizeof(clock_years)) == 0) {
                    init_value(&clock_years, sizeof(clock_years));
                }
                unset_next_bit((unsigned char *)&clock_years, 12);
                tffs_write_value(fd_write, FLASH_FS_YEAR_COUNTER,   &clock_years, sizeof(clock_years));
            }
            tffs_write_value(fd_write, FLASH_FS_MOUNTH_COUNTER, &clock_mounths, sizeof(clock_mounths));
        }
        tffs_write_value(fd_write, FLASH_FS_DAY_COUNTER,    &clock_days, sizeof(clock_days));
    }
    tffs_write_value(fd_write, FLASH_FS_HOUR_COUNTER,   &clock_hours, sizeof(clock_hours));
    tffs_close(fd_write);
    tffs_close(fd_read);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void clear_all(char *device) {
    int fd;
    fd = tffs_open(O_WRONLY);
    if(fd < 0)
        return;

    DBG(("clear_all(%s)\n", device));

    tffs_clear_value(fd, FLASH_FS_YEAR_COUNTER);
    tffs_clear_value(fd, FLASH_FS_MOUNTH_COUNTER);
    tffs_clear_value(fd, FLASH_FS_DAY_COUNTER);
    tffs_clear_value(fd, FLASH_FS_HOUR_COUNTER);
    tffs_clear_value(fd, FLASH_FS_REBOOT_MAJOR);
    tffs_clear_value(fd, FLASH_FS_REBOOT_MINOR);

    tffs_close(fd);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void cleanup_all(char *device) {
    int fd;
    fd = tffs_open(O_WRONLY);
    if(fd < 0)
        return;

    DBG(("cleanup_all(%s)\n", device));

    tffs_cleanup(fd);
    tffs_close(fd);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int htoi(char *p) {
    unsigned int value = 0;
    if(*p == '0' && (p[1] == 'x' || p[1] == 'X')) {
        if(!p || !*p) return value; if(*p == '0') p++;
        if(!p || !*p) return value; if(*p == 'x') p++;
        for( ;; ) {
            if(!p || !*p) return value;
            value <<= 4;
            if(*p >= '0' && *p <= '9')
                value |= *p - '0';
            else if(*p >= 'a' && *p <= 'f')
                value |= *p - 'a' + 10;
            else if(*p >= 'A' && *p <= 'F')
                value |= *p - 'A' + 10;
            else
                break;
            p++;
        }
        return value;
    }
    return atoi(p);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static char *get_env(char *env[], char *name) {
    unsigned int i = 0, len;
    char *ptr;
    len = strlen(name);
    while(env[i]) {
        DBG(("env[%u]=\"%s\"\n", i, env[i]));
        if(!strncmp(env[i], name, len)) {
            ptr = strchr(env[i], '=');
            if(ptr) ptr++;
            return ptr;
        }
        i++;
    }
    return NULL;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
char *read_hidden_oem(char *device) {
    static char Buffer[64];
    int fd;
    fd = tffs_open(O_RDONLY);
    if(fd < 0) {
        DBG(("%s: open device %s failed\n", program, device));
        return NULL;
    }
    if(tffs_read_value(fd, FLASH_FS_RESERVED_COUNTER, Buffer, sizeof(Buffer))) {
        tffs_close(fd);
        return Buffer;
    }
    tffs_close(fd);
    Buffer[0] = '\0';
    return NULL;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
char *read_dirty_flag(char *device) {
    static char Buffer[64];
    int fd;
    fd = tffs_open(O_RDONLY);
    if(fd < 0) {
        DBG(("%s: open device %s failed\n", program, device));
        return NULL;
    }
    if(tffs_read_value(fd, 0x57, Buffer, sizeof(Buffer))) {
        tffs_close(fd);
        return Buffer;
    }
    tffs_close(fd);
    Buffer[0] = '\0';
    return NULL;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
char *read_rc_user_oem(char *device) {
    static char Buffer[64];
    int fd;
    fd = tffs_open(O_RDONLY);
    if(fd < 0) {
        DBG(("%s: open device %s failed\n", program, device));
        return NULL;
    }
    if(tffs_read_value(fd, FLASH_FS_ID_RC_USER, Buffer, sizeof(Buffer))) {
        tffs_close(fd);
        return Buffer;
    }
    tffs_close(fd);
    Buffer[0] = '\0';
    return NULL;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int write_hidden_oem(char *device, char *oem) {
    int fd;
    fd = tffs_open(O_WRONLY);
    if(fd < 0) {
        DBG(("%s: open device %s failed\n", program, device));
        return -1;
    }
    if(tffs_write_value(fd, FLASH_FS_RESERVED_COUNTER, oem, strlen(oem) + 1)) {
        tffs_close(fd);
        return 0;
    }
    tffs_close(fd);
    return -2;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int main(int argc, char* argv[], char *env[]) {
    unsigned int count;
    unsigned int demon = 0;
    unsigned int reinit = 0;
    unsigned int service = 0;
    char *oem, *hidden_oem;
    char *device = NULL;
    int fd;
    unsigned int sleep_value = 60 * 60;

    program = argv[0];

    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
    for(count = 1 ; count < argc ; count++) {
        if(argv[count][0] == '-') {
            switch(argv[count][1]) {
                case 'h':
                    puts("use: ");
                    puts(argv[0]);
                    puts(" [-h(elp)][-i][-d(emon)][-r] -c <device>");
                    puts("\t-h\tthis help");
                    puts("\t-d\tstart as demon");
                    puts("\t-i\tinit, reset all counters");
                    puts("\t-r\trecover tffs (cleanup)");
                    puts("\t-c\tdevice of tffs");
                    puts("\t-s\tsleep value (for test purpose)");
                    puts("\t-S\tService Code");
                    exit(-1);
                    break;
                case 'd':
                    demon = 1;
                    break;
                case 'c':
                    device = argv[++count];
                    tffs_device(device);
                    break;
                case 's':
                    sleep_value = htoi(argv[++count]);
                    break;
                case 'i':
                    reinit = 1;
                    break;
                case 'r':
                    reinit = 2;
                    break;
                case 'S':
                    service = 1;
            }
        }
    }

    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
    switch(reinit) {
        case 1:
            clear_all(device);
            return 0;
        case 2:
            cleanup_all(device);
            return 0;
        default:
            break;
    }

    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
    korr_boot(device);

    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
    oem   = get_env(env, "OEM=");
    hidden_oem = read_hidden_oem(device);
    if(hidden_oem == NULL) {
        write_hidden_oem(device, oem);
    }
    hidden_oem = read_hidden_oem(device);

    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
    if(demon == 1) {

        int pid = fork();
        if(pid == 0) { /*-- child ---*/
            close(0);
#if !defined(DEBUG)
            close(1);
#endif /*--- #if !defined(DEBUG) ---*/
            close(2);

            write_boot(device);
            check_debug_cfg();
            for( ;; ) {
                sleep(sleep_value); /*--- eine Stunde schlafen ---*/
                every_hour(device);
            }
        }
        if(pid > 0) {
            puts("run_clock demon started\n");
            return 0;
        }

        puts("run_clock demon start failed\n");
        return 0;
    }

    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
    fd = tffs_open(O_RDONLY);
    if(fd < 0) {
        DBG(("%s: open device %s failed\n", program, device));
        return -1;
    }

    if(tffs_read_value(fd, FLASH_FS_HOUR_COUNTER,   &clock_hours, sizeof(clock_hours)) == 0) {
        init_value(&clock_hours, sizeof(clock_hours));
    }
    if(tffs_read_value(fd, FLASH_FS_DAY_COUNTER,    &clock_days, sizeof(clock_days)) == 0) {
        init_value(&clock_days, sizeof(clock_days));
    }
    if(tffs_read_value(fd, FLASH_FS_MOUNTH_COUNTER, &clock_mounths, sizeof(clock_mounths)) == 0) {
        init_value(&clock_mounths, sizeof(clock_mounths));
    }
    if(tffs_read_value(fd, FLASH_FS_YEAR_COUNTER,   &clock_years, sizeof(clock_years)) == 0) {
        init_value(&clock_years, sizeof(clock_years));
    }

    if(tffs_read_value(fd, FLASH_FS_REBOOT_MAJOR,   &clock_restart_major, sizeof(clock_restart_major)) == 0) {
        init_value(&clock_restart_major, sizeof(clock_restart_major));
    }
    if(tffs_read_value(fd, FLASH_FS_REBOOT_MINOR,   &clock_restart_minor, sizeof(clock_restart_minor)) == 0) {
        init_value(&clock_restart_minor, sizeof(clock_restart_minor));
    }
    tffs_close(fd);

    if(service) {
        int i = 0;
        char *Name = NULL;
        char *Annex = "B";
        int fd;
        int dirty;
        unsigned int crc32 = 0xFFFFFFFF;
        char Buffer[512];
        fd = open("/dev/mtdblock/0", O_RDONLY);
        if(fd < 0) {
            fd = open("/dev/mtdblock2", O_RDONLY);
        }
        if(fd < 0) {
            fprintf(stderr, "[%s] error reading filesystem\n", argv[0]);
        } else {
            for( ;; ) {
                int n;
                i = read(fd, Buffer, sizeof(Buffer));
                if(i <= 0)
                    break;
                for(n = 0 ; n < i ; n++)
                    crc32 = DoCrc32_8(crc32, Buffer[n]);
            }
            close(fd);
        } 

        Annex = get_env(env, "ANNEX=");
        Name  = get_env(env, "CONFIG_PRODUKT_NAME=");

        printf("%s-%s-%02u%02u%02u-%02u%02u%02u-%06o-%06o",
                Name ? Name : "Fritz!Box unbekannt",
                Annex,

                bits_unset((unsigned char *)&clock_hours, 24),
                bits_unset((unsigned char *)&clock_days, 31),
                bits_unset((unsigned char *)&clock_mounths, 12),

                bits_unset((unsigned char *)&clock_years, 32),
                bits_unset((unsigned char *)&clock_restart_major, 64),
                bits_unset((unsigned char *)&clock_restart_minor, 32),

                crc32 >> 14,
                crc32 & ((1 << 18) - 1)
                );

        Name = read_rc_user_oem(device);
        if(Name == NULL)
            dirty = 'N';  /*--- N: 0x4E := 0100 1110 dirty: 0x04 = 14 ---*/
        else if(strlen(Name) < 10)
            dirty = 'S';  /*--- S: 0x53 := 0101 0011 dirty: 0x13 = 19 ---*/
        else 
            dirty = 'U';  /*--- S: 0x55 := 0101 0101 dirty: 0x15 = 21 ---*/

        if(read_dirty_flag(device))
            dirty &= ~0x40;

        printf("-%02u", dirty);  /* U = 0x55 := 0101 0101*/

        /*--- printf("\noem='%s'\n", oem); ---*/
        /*--- printf("hidden_oem='%s'\n", hidden_oem); ---*/
        if(!strcmp(oem, hidden_oem))
            printf("%02u", 'O');
        else
            printf("%02u", 'C');
        printf("%02u", RUN_CLOCK_VERSION);

        /*--- printf("<br>0x%x\n", crc32 >> 14); ---*/
        /*--- printf("<br>0x%x\n", crc32 & ((1 << 18) - 1)); ---*/
        return 0;
    }
    printf("running: %u hours %u days %u mounths %u years (%u starts)\n",
                bits_unset((unsigned char *)&clock_hours, 24),
                bits_unset((unsigned char *)&clock_days, 31),
                bits_unset((unsigned char *)&clock_mounths, 12),
                bits_unset((unsigned char *)&clock_years, 32),
                (bits_unset((unsigned char *)&clock_restart_major, 64) * 32) +
                bits_unset((unsigned char *)&clock_restart_minor, 32));

    return 0;
}

