/* === includes ========================================== */
#include "sniffer.h"

/* === macros ============================================ */

/* === types ============================================= */

/* === globals =========================================== */
sniffer_context_t ctx;
static uint8_t rxbuf[MAX_FRAME_SIZE+1];

sniffer_packet_t rxpacket;

volatile uint8_t new_frame;
volatile dbg_t dbg;

static time_stamp_t rxstarttime;

/* === prototypes ======================================== */
void scan_update_status(void);

/* === functions ========================================= */

void sniffer_init(void)
{
uint8_t val;

    /* Phase 1: initialize MCU peripherals */
    LED_INIT();
    LED_SET_VALUE(1);
    memset(&ctx, 0, sizeof(ctx));
    memset((void*)&dbg, 0, sizeof(dbg));
    timer_init();
    hif_init(38400);
    trx_io_init(DEFAULT_SPI_RATE);
    sei();

    PRINTF(NL"Sniffer V%s [%s]"NL, VERSION, BOARD_NAME);
    LED_SET_VALUE(2);
    /* Phase 2: initialize the radio transceiver */
    TRX_RESET_LOW();
    TRX_SLPTR_LOW();
    DELAY_US(TRX_RESET_TIME_US);
    TRX_RESET_HIGH();
    trx_reg_write(RG_TRX_STATE, CMD_TRX_OFF);
    DELAY_US(TRX_INIT_TIME_US);
    val = trx_reg_read(RG_TRX_STATUS);
    if (val != TRX_OFF)
    {
        PRINTF("FATAL ERROR: TRX not in state TRX_OFF [0x%02x]"NL, val);
        while(1);
    }
    /* Phase 3: enable IRQ's and enter RX-Mode */
    LED_SET_VALUE(3);
#if defined(TRX_IRQ_RX_START) && defined(TRX_IRQ_TRX_END) && defined(TRX_IRQ_UR)
    trx_reg_write(RG_IRQ_MASK, (TRX_IRQ_RX_START|TRX_IRQ_TRX_END|TRX_IRQ_UR));
#elif defined(TRX_IRQ_RX_START) && defined(TRX_IRQ_RX_END)
    trx_reg_write(RG_IRQ_MASK, (TRX_IRQ_RX_START | TRX_IRQ_RX_END));
#else
#  error "Unknown IRQ bits"
#endif
    trx_reg_write(RG_TRX_STATE, CMD_RX_ON);
    DELAY_US(180);
    val = trx_reg_read(RG_TRX_STATUS);
    if ((val != RX_ON) && (val != BUSY_RX))
    {
        PRINTF("FATAL ERROR: TRX not in state RX_ON [0x%02x]"NL, val);
        while(1);
    }
    LED_SET_VALUE(0);
    /* init context structure */
    ctx.state = IDLE;
    ctx.cchan = TRX_MIN_CHANNEL;
    ctx.cmask = TRX_SUPPORTED_CHANNELS;
}

void sniffer_start(sniffer_state_t state)
{
    switch (state)
    {
        case IDLE:
            trx_reg_write(RG_TRX_STATE, CMD_FORCE_TRX_OFF);
            ctx.state = IDLE;
            LED_SET_VALUE(0);
            break;
        case SCAN:
            ctx.state = SCAN;
            scan_init();
            break;
        case SNIFF:
            trx_reg_write(RG_TRX_STATE, CMD_RX_ON);
            ctx.state = SNIFF;
            break;

        default:
            break;
    }
}

void sniffer_stop(void)
{
sniffer_state_t curr_state;

    trx_reg_write(RG_TRX_STATE, CMD_FORCE_TRX_OFF);
    cli();
    curr_state = ctx.state;
    ctx.state = IDLE;
    sei();

    switch(curr_state)
    {
        case SCAN:
            ctx.cchan = TRX_MIN_CHANNEL;
            ctx.thdl = timer_stop(ctx.thdl);
            break;
        case SNIFF:
        case IDLE:
            break;
        default:
            PRINTF("Unknown state %d"NL,ctx.state);
            break;

    }
}

int main(void)
{

    sniffer_init();

    while(1)
    {
        ctrl_process_input();

        if(ctx.state == SCAN_DONE)
        {
            scan_update_status();
        }
        if ((new_frame > 0) && (ctx.state == SNIFF))
        {
            hif_putc(1);
            hif_putc(new_frame+sizeof(time_stamp_t));
            hif_put_blk((unsigned char *)&rxpacket.ts, sizeof(time_stamp_t));
            hif_put_blk(rxbuf, new_frame);
            hif_putc(4);
            new_frame = 0;
        }
    }
}

#if defined(DOXYGEN)
void TRX_IRQ_vect()
#elif !defined(TRX_IF_RFA1)
ISR(TRX_IRQ_vect)
{
static volatile uint8_t cause;
uint8_t ed, flen, lqi = 0, frmupl;
bool crc_ok = 0;
extern time_t systime;

    DI_TRX_IRQ();
    sei();
    cause = trx_reg_read(RG_IRQ_STATUS);

    if (dbg.irql) dbg.err = 1;
    dbg.irql ++;

    if (cause & TRX_IRQ_RX_START)
    {
        rxstarttime.time_usec = TRX_TSTAMP_REG;
        rxstarttime.time_sec = systime;
    }

    if (cause & TRX_IRQ_TRX_END)
    {
        /* Upload frame at TRX_END IRQ */
        ctx.frames++;
        ed = trx_reg_read(RG_PHY_ED_LEVEL);
        flen = trx_frame_read_crc(rxbuf, MAX_FRAME_SIZE, &crc_ok);
        trx_sram_read(flen, 1, &lqi);
        if (ctx.state == SCAN)
        {
            scan_update_frame(flen, crc_ok, lqi, ed, rxbuf);
        }
        if (ctx.state == SNIFF)
        {
            frmupl = 1;
            if (new_frame != 0)
            {
                frmupl = 0;
                ctx.missed_frames +=1;
            }
            else if(ctx.chkcrc == 1)
            {
                frmupl = crc_ok;
            }
            if (frmupl != 0)
            {
                memcpy(&rxpacket.ts, &rxstarttime, sizeof(rxstarttime));
                rxpacket.flen = flen;
                rxpacket.rxdata = rxbuf;
                rxpacket.lqi = lqi;
                rxpacket.ed = ed;
                rxpacket.crc = crc_ok;
                new_frame = flen;
            }
        }
        LED_SET_VALUE(ctx.frames);
    }

    if (cause & TRX_IRQ_UR)
    {
        ctx.irq_ur ++;
    }

    dbg.irql --;

    cli();
    EI_TRX_IRQ();
}
#endif /* defined(DOXYGEN) || !RFA1 */

#if defined(TRX_IF_RFA1)
ISR(TRX24_RX_START_vect)
{
extern time_t systime;

    rxstarttime.time_usec = TRX_TSTAMP_REG;
    rxstarttime.time_sec = systime;
}

ISR(TRX24_RX_END_vect)
{
uint8_t ed, flen, lqi = 0, frmupl;
bool crc_ok = 0;

    /* Upload frame at TRX_END IRQ */
    ctx.frames++;
    ed = trx_reg_read(RG_PHY_ED_LEVEL);
    flen = trx_frame_read_crc(rxbuf, MAX_FRAME_SIZE, &crc_ok);
    trx_sram_read(flen, 1, &lqi);
    if (ctx.state == SCAN)
    {
        scan_update_frame(flen, crc_ok, lqi, ed, rxbuf);
    }
    if (ctx.state == SNIFF)
    {
        frmupl = 1;
        if (new_frame != 0)
        {
            frmupl = 0;
            ctx.missed_frames +=1;
        }
        else if(ctx.chkcrc == 1)
        {
            frmupl = crc_ok;
        }
        if (frmupl != 0)
        {
            memcpy(&rxpacket.ts, &rxstarttime, sizeof(rxstarttime));
            rxpacket.flen = flen;
            rxpacket.rxdata = rxbuf;
            rxpacket.lqi = lqi;
            rxpacket.ed = ed;
            rxpacket.crc = crc_ok;
            new_frame = flen;
        }
    }
    LED_SET_VALUE(ctx.frames);
}
#endif  /* RFA1 */

/* EOF */
