/*
 * SataController.cpp
 *
 */


#include "SataController.h"



#include "bsp_srio_mini_driver.h"

#include "rtlog.h"
#include "bsk_sprintf.h"

#include <string.h>

enum sata_ctrl_flag_t
{
    sata_init=1<<1,
    sata_ena_counters=1<<8,
    sata_rec_sw=0x80,
    sata_rec=1<<16
};

#define FILE_NAME_LEN (32)
#define DIR_NAME_LEN (32)
#define COMPOSED_FILE_NAME_LEN (48)

struct sata_recorder_ctrl_t
{
    unsigned int srioid;
    unsigned int base;

    int recording;
    int recording_level;
    int activated;

    unsigned int read_err;
    unsigned int write_err;

    unsigned int sata_sts;

    unsigned int sata_nice;

    unsigned int world_time;

    volatile unsigned int cr_init;
    volatile unsigned int cr_start;
    volatile unsigned int cr_stop;
    volatile unsigned int max_size_m;
    struct ctrl_t
    {
        uint32_t header[8];
        uint32_t ctrl;
        uint32_t repeater_enable;
        uint32_t input_sel;   //0x28
        uint32_t spare2;    //0x2C
        uint32_t ssd_address;
        uint32_t irq_dst;
        uint32_t maddr;
        uint32_t msize;

        uint32_t world_time;
        uint32_t world_time_valid;

        uint32_t raw_sector;
        uint32_t raw_sector_counter;
        uint32_t raw_buffer_address;
        uint32_t raw_ctrl;
        uint32_t raw_ctrl_id;

        uint32_t ext_valid;  //Following data are used only if ext_valid=0x12345678
    };

    double __a0;

    ctrl_t ctrl;
    uint32_t gap0[64];
    //uint32_t config[256];
    double __a1;

    struct cfg_t
    {
        uint32_t maxFileSize;
        char dirName[32];
        char  fileName[32];
        uint32_t spare[128];
    };
    cfg_t config;

    uint32_t gap1[128];  //cache line aligned


    double __a2;

    struct sts_t
    {
        uint32_t sts;
        uint32_t files;
        uint32_t kbytes;
        uint32_t cycles;
        uint32_t valids;
        uint32_t busy;

        uint32_t recorded_files;
        uint32_t free_kbytes;

        uint32_t xstate;
        uint32_t fsm;
        uint32_t fr;
        uint32_t sata_sts;
        uint32_t sata_err;
        uint32_t vfifo_sts;

        uint32_t ssize;
        uint32_t scount;

        uint32_t raw_fr;
        uint32_t raw_sector_tb;
        uint32_t raw_sector_count_tb;
        uint32_t raw_bytes_size;
        uint32_t raw_buffer_address;
        uint32_t raw_ctrl_tb;
        uint32_t raw_ctrl_id;
        uint32_t raw_op_counter;
        uint32_t raw_op_sts;

        uint32_t fn_valid_begin;
        char rec_name[COMPOSED_FILE_NAME_LEN];  //0x108
        uint32_t fn_valid_end;

        uint32_t spare[32];
    };
    sts_t sts;

    uint32_t gap2[128];

    double __a3;

    struct info_t
    {
        uint32_t cycles;
        uint32_t ack;
        uint32_t busy;
        uint32_t valid;
        uint32_t ready;
        uint32_t spare[3];
        uint32_t ipid[3];
        uint32_t fw_version;
        uint32_t dbg;
        uint32_t trail[32];
    };
    info_t info;
    uint32_t gap3[64];

    unsigned int day;
    unsigned int month;
    unsigned int year;
    unsigned int sec_from_midnight;
    unsigned int h;
    unsigned int m;
    unsigned int s;

    unsigned long kbytes_last;

    char fName[32];
    char dName[32];

    unsigned int update_filename;

    char rec_fname[COMPOSED_FILE_NAME_LEN];
    unsigned int ext_status;

    void rebuild_filename()
    {
        bsk_snprintf(config.fileName, sizeof config.fileName, "_%s-%02u%02u%02u-%02u%02u%02u",
                     fName,
                     year, month, day, h, m, s
                     );
    }

    void rebuild_dirname()
    {
        bsk_snprintf(config.dirName, sizeof config.dirName, "_%s-%02u%02u%02u-%02u%02u%02u",
                     dName,
                     year, month, day, h, m, s
                     );
    }

    sata_recorder_ctrl_t()
    {
        initialize();
    }

    void initialize()
    {
        cr_init=sata_init|sata_ena_counters;
        cr_start=sata_init|sata_ena_counters|sata_rec;
        cr_stop=sata_init|sata_ena_counters;

        ctrl.ctrl=0;
        ctrl.repeater_enable=1;
        ctrl.maddr=0;
        ctrl.msize=0;
        base=0x45000000;
        srioid=0x13;

        max_size_m=128;

        strcpy(dName, "morc");
        strcpy(fName, "morc");
        strcpy(config.dirName, dName);
        strcpy(config.fileName, fName);
    }


    void sataWrite(unsigned int offset, const void* data)
    {
        int r=bsp_srio_mini_drv_memory_write_nowait(srioid,  base+offset,  data,  256,  bsp_srio_mini_drv_no_doorbell, false);
        if (r)
            ++write_err;
    }

    void sataRead(unsigned int offset, void* data)
    {
        int r=bsp_srio_mini_drv_memory_read_nowait(srioid,  base+offset,  data,  256,  bsp_srio_mini_drv_no_doorbell);
        if (r)
            ++read_err;
#ifdef __MINGW32__
        if (offset==0x200)
        {
            if (ctrl.ctrl==cr_init)
            	sts.sts=1;
            if (ctrl.ctrl==cr_start)
            {
            	sts.sts=3;
            	++sts.kbytes;
            }
        }
#endif
    }

};

static sata_recorder_ctrl_t autoMonitorable_satactrl;



void SataController::setMaxSizeMbytes(unsigned int max_size_mbytes)
{
    autoMonitorable_satactrl.max_size_m=max_size_mbytes;
}

void SataController::setPath(const char* path)
{
    //strncpy(autoMonitorable_satactrl.config.dirName, path, sizeof autoMonitorable_satactrl.config.dirName);
    strncpy(autoMonitorable_satactrl.dName, path, (sizeof autoMonitorable_satactrl.dName)-1);
}

void SataController::setFilePrefix(const char* prefix)
{
    //strncpy(autoMonitorable_satactrl.config.fileName, prefix, sizeof autoMonitorable_satactrl.config.fileName);
    strncpy(autoMonitorable_satactrl.fName, prefix, (sizeof autoMonitorable_satactrl.fName)-1);
}

void SataController::setDate(unsigned int day, unsigned int month, unsigned int year, unsigned int sec_from_midnight)
{
    autoMonitorable_satactrl.day=day;
    autoMonitorable_satactrl.month=month;
    autoMonitorable_satactrl.year=year;
    autoMonitorable_satactrl.sec_from_midnight=sec_from_midnight;

    unsigned int tt=sec_from_midnight;
    unsigned int h=tt/3600;
    unsigned int m=(tt-h*3600)/60;
    unsigned int s=(tt-(h*3600+m*60));

    autoMonitorable_satactrl.s=s;
    autoMonitorable_satactrl.h=h;
    autoMonitorable_satactrl.m=m;

    unsigned int t=
              (((2000U+year)-1980U) << 25U)
            | (month << 21)
            | (day << 16)
            | (h << 11)
            | (m<< 5)
            | (s);

    autoMonitorable_satactrl.world_time=t;
    if (t)
    {
        autoMonitorable_satactrl.ctrl.world_time=t;
        autoMonitorable_satactrl.ctrl.world_time_valid=0x12345678;
    }
}

void SataController::setRecording(bool rec)
{
    autoMonitorable_satactrl.recording=rec ? 1 : -1;
}

bool SataController::isActive()
{
	return autoMonitorable_satactrl.activated; //recording!=0;
}

bool SataController::isRecording()
{
    return autoMonitorable_satactrl.sts.sts==3;
}

unsigned long SataController::recordedSize()
{
    return autoMonitorable_satactrl.sts.kbytes-autoMonitorable_satactrl.kbytes_last;
}

unsigned int SataController::recorderStatus()
{
    return autoMonitorable_satactrl.sts.sts;
}


static unsigned int calculateExtStatus()
{
    unsigned int sts=autoMonitorable_satactrl.sts.sts;
    unsigned int xsts=autoMonitorable_satactrl.sts.xstate;
    unsigned int fsm=autoMonitorable_satactrl.sts.fsm;

    unsigned int ext_sts=0;

    unsigned int free_bytes=autoMonitorable_satactrl.sts.free_kbytes;


    if (ext_sts>=SataController::rs_rdy)
    if (free_bytes<1024)
    {
        ext_sts=SataController::rs_full;
    }

    autoMonitorable_satactrl.ext_status=ext_sts;
    return ext_sts;
}

unsigned int SataController::extRecorderStatus()
{
    return autoMonitorable_satactrl.ext_status;
}

const char* SataController::recFile()
{
    return autoMonitorable_satactrl.rec_fname;
}

void SataController::updateStatus(const void* sts)
{

}

void SataController::pollStatus()
{
    static bool dont_build_dirname;

    static unsigned int stop_limbo;
    static bool clear_errors;
    static unsigned int error_filter;

    static int rec_sts;

    if (autoMonitorable_satactrl.sata_nice>0)
    {
        --autoMonitorable_satactrl.sata_nice;
        return;
    }

    if (autoMonitorable_satactrl.sata_sts>3)
    {
    	autoMonitorable_satactrl.recording_level=0;
    	if (autoMonitorable_satactrl.recording>2)
    	{
    		autoMonitorable_satactrl.recording=-1;
    	}
    }

    switch(autoMonitorable_satactrl.recording)
    {
    default:
    case 0:
        if (autoMonitorable_satactrl.sata_sts!=autoMonitorable_satactrl.sts.sts)
        {
            rtlog_printf("SATA", rtlog_warning|rtlog_breaking_news, "rec=%d->%d, s=0x%X->0x%X",
                         rec_sts, autoMonitorable_satactrl.recording, autoMonitorable_satactrl.sata_sts, (unsigned int)autoMonitorable_satactrl.sts.sts);
            rec_sts=autoMonitorable_satactrl.recording;
            autoMonitorable_satactrl.sata_sts=autoMonitorable_satactrl.sts.sts;
        }
        if (clear_errors)
        {
            clear_errors=false;
            error_filter=0;
            autoMonitorable_satactrl.sts.sts=0;
            rtlog_const_message("SATA", rtlog_warning|rtlog_breaking_news, "clear errors");
        }
        break;

    case 1:
        autoMonitorable_satactrl.recording=autoMonitorable_satactrl.recording_level>0 ? autoMonitorable_satactrl.recording_level : 2;
#if 0
        if (!dont_build_dirname)
        {
            autoMonitorable_satactrl.rebuild_dirname();
            autoMonitorable_satactrl.rebuild_filename();
            dont_build_dirname=true;
            //autoMonitorable_satactrl.update_filename=false;
        }
#endif
        autoMonitorable_satactrl.sata_nice=200;
        break;

    case 2:
        if (!dont_build_dirname)
        {
            autoMonitorable_satactrl.rebuild_dirname();
            autoMonitorable_satactrl.rebuild_filename();
            dont_build_dirname=true;
        }
        if (autoMonitorable_satactrl.update_filename)
            autoMonitorable_satactrl.rebuild_filename();

        autoMonitorable_satactrl.config.maxFileSize=(autoMonitorable_satactrl.max_size_m*1024)/64;
        autoMonitorable_satactrl.sataWrite(0x100, &autoMonitorable_satactrl.config);
        autoMonitorable_satactrl.recording=3;
        autoMonitorable_satactrl.sata_nice=100;
        autoMonitorable_satactrl.sata_sts=0;
        autoMonitorable_satactrl.sts.sts=0;
        autoMonitorable_satactrl.activated=1;
        break;

    case 3:
        autoMonitorable_satactrl.ctrl.ctrl=autoMonitorable_satactrl.cr_init;

        if (autoMonitorable_satactrl.world_time)
        {
            //autoMonitorable_satactrl.ctrl.world_time=autoMonitorable_satactrl.world_time;
            //autoMonitorable_satactrl.ctrl.world_time_valid=0x12345678;
        }

        autoMonitorable_satactrl.sataWrite(0, &autoMonitorable_satactrl.ctrl);

        autoMonitorable_satactrl.recording=4;
        autoMonitorable_satactrl.sata_nice=100;
        autoMonitorable_satactrl.sts.sts=0;
        autoMonitorable_satactrl.activated=1;
        autoMonitorable_satactrl.recording_level=3;
        break;

    case 4:
        {
            autoMonitorable_satactrl.activated=1;
            if (autoMonitorable_satactrl.sts.sts>0)
            {
                ++autoMonitorable_satactrl.recording;
                autoMonitorable_satactrl.recording_level=4;
            }
            else
            {
                autoMonitorable_satactrl.sataRead(0x200, &autoMonitorable_satactrl.sts);
                autoMonitorable_satactrl.sata_nice=100;
            }
        }
        break;

    case 5:
        autoMonitorable_satactrl.ctrl.ctrl=autoMonitorable_satactrl.cr_start;
        autoMonitorable_satactrl.sataWrite(0, &autoMonitorable_satactrl.ctrl);
        ++autoMonitorable_satactrl.recording;
        autoMonitorable_satactrl.sata_nice=100;
        autoMonitorable_satactrl.kbytes_last=autoMonitorable_satactrl.sts.kbytes;
        break;

    case 6:
        {
            autoMonitorable_satactrl.sataRead(0x200, &autoMonitorable_satactrl.sts);
            autoMonitorable_satactrl.sata_nice=100;
        }
        break;

    case -1:
        autoMonitorable_satactrl.sata_nice=100;
        autoMonitorable_satactrl.recording=-2;
        break;
    case -2:
        autoMonitorable_satactrl.ctrl.ctrl=autoMonitorable_satactrl.cr_stop;
        autoMonitorable_satactrl.sataWrite(0, &autoMonitorable_satactrl.ctrl);
        autoMonitorable_satactrl.recording=-3;
        autoMonitorable_satactrl.sata_nice=100;
        stop_limbo=5;
        break;
    case -3:
        if (stop_limbo>0)
        {
            autoMonitorable_satactrl.sataRead(0x200, &autoMonitorable_satactrl.sts);
            autoMonitorable_satactrl.sata_nice=50;
            --stop_limbo;
        }
        else
        {
            autoMonitorable_satactrl.recording=-4;
        }
        break;

    case -4:
        autoMonitorable_satactrl.recording=0;
        autoMonitorable_satactrl.activated=0;
        break;
    }

    if ((rec_sts!=autoMonitorable_satactrl.recording) || (autoMonitorable_satactrl.sata_sts!=autoMonitorable_satactrl.sts.sts))
    {
        int level=autoMonitorable_satactrl.sts.sts>3 ? rtlog_error : rtlog_warning;
        rtlog_printf("SATA", level|rtlog_breaking_news, "rec=%d->%d, s=0x%X->0x%X",
                     rec_sts, autoMonitorable_satactrl.recording, autoMonitorable_satactrl.sata_sts, (unsigned int)autoMonitorable_satactrl.sts.sts);
        rec_sts=autoMonitorable_satactrl.recording;
        autoMonitorable_satactrl.sata_sts=autoMonitorable_satactrl.sts.sts;
    }

    {
        if (autoMonitorable_satactrl.sts.sts>3)
        {
            if (error_filter>50)
            {
                autoMonitorable_satactrl.recording=0;
                autoMonitorable_satactrl.activated=0;
                clear_errors=true;
                //autoMonitorable_satactrl.sts.sts=0;
                //error_filter=0;
            }
            else
                ++error_filter;
        }
        else
            error_filter=0;
    }

}





