/* Copyright (c) 2007 Axel Wachtler
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
   * Neither the name of the authors nor the names of its contributors
     may be used to endorse or promote products derived from this software
     without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE. */

/* $Id: diagnostic.c,v 1.4 2009/12/22 16:47:10 joerg_wunsch Exp $ */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#include "board.h"
#include "transceiver.h"
#include "ioutil.h"
#include "hif.h"
/* === Types =============================== */
typedef struct
{
    trx_regaddr_t offs;
    trx_regval_t  val;
} regval_t;


typedef void (*testfn_t)(void);

typedef struct
{
    const char* cmd;
    testfn_t testfn;
} test_command_t;


/* === Prototypes ========================== */
void init_hw(void);
void get_cmd(void);
void diag_echo(FLASH_STRING_T str);
void diag_printf(FLASH_STRING_T fmt, ...);

void test_help(void);
void test_all(void);
void test_regreset(void);
void test_sram(void);
void test_sram_const(uint8_t pattern);
void test_sram_runbit(void);
void test_sram_address(void);
void test_trxirq(void);
void toggle_pin(void);
#if !defined(TRX_IF_RFA1)
void spi_loop(void);
#endif
void diag_irq_handler(uint8_t cause);

/* === Globals ============================= */
static char cmdbuf[64];
static uint8_t cmdidx = 0;

/** todo: the register resetvalue list should go to at86rf*.h*/
const regval_t regrst[] = {
    {RG_TRX_STATUS,      0 },
    {RG_TRX_STATE,      0 },
    {RG_TRX_CTRL_0,      0x19 },
    {RG_PHY_TX_PWR,      0 },
    {RG_PHY_RSSI,      0 },
    {RG_PHY_ED_LEVEL,      0 },
    {RG_PHY_CC_CCA,      0x2b },
    {RG_CCA_THRES,      0xc7 },
    {RG_IRQ_MASK,      0xff },
    {RG_IRQ_STATUS,      0 },
    {RG_VREG_CTRL,      0 },
    {RG_BATMON,      0x02 },
    {RG_XOSC_CTRL,      0xf0 },
    {RG_PLL_CF,      0x5f },
    {RG_PLL_DCU,      0x20 },
    {RG_PART_NUM,      0x02 },
    {RG_VERSION_NUM,      0x01 },
    {RG_MAN_ID_0,      0x1f },
    {RG_MAN_ID_1,      0x00 },
    {RG_SHORT_ADDR_0,      0 },
    {RG_SHORT_ADDR_1,      0 },
    {RG_PAN_ID_0,      0 },
    {RG_PAN_ID_1,      0 },
    {RG_IEEE_ADDR_0,      0 },
    {RG_IEEE_ADDR_1,      0 },
    {RG_IEEE_ADDR_2,      0 },
    {RG_IEEE_ADDR_3,      0 },
    {RG_IEEE_ADDR_4,      0 },
    {RG_IEEE_ADDR_5,      0 },
    {RG_IEEE_ADDR_6,      0 },
    {RG_IEEE_ADDR_7,      0 },
#if RADIO_TYPE == RADIO_AT86RF230 || RADIO_TYPE == RADIO_AT86RF230B
    {RG_XAH_CTRL,      0x38 },
#endif    
    {RG_CSMA_SEED_0,      0xea },
    {RG_CSMA_SEED_1,      0xc2 },
    };

test_command_t testcmd[] = {
    { "all",  test_all  },
    { "rst",  test_regreset },
    { "sram", test_sram },
    { "irq",  test_trxirq },
    { "help", test_help },
    { "ts"  , toggle_pin },
#if !defined(TRX_IF_RFA1)
    { "spi",  spi_loop },
#endif
    };


volatile uint16_t irqcnt = 0;
volatile uint16_t badirqcnt = 0;

uint16_t irqcause = 0;
/* === Implementation ====================== */
int main(void)
{
uint8_t x;
    init_hw();
    for (x=0; x<255;x++)
    {
        LED_SET_VALUE((uint8_t)3);
        _delay_us(2000);
    }

    while (1)
    {
        get_cmd();
        _delay_us(200000);
    }
}


void init_hw(void)
{
    LED_INIT();
    LED_SET_VALUE(3);
    trx_io_init(SPI_RATE_1_2);
    trx_set_irq_handler(diag_irq_handler);
    hif_init(19200);
    sei();
    PRINTF("\n\r Diagnostics V1.01 for board type %d\n\r", BOARD_TYPE);
}

void test_help(void)
{
uint8_t i;
    PRINT("Availabe commands:");
    for(i=0;i<sizeof(testcmd)/sizeof(test_command_t); i++)
    {
        PRINTF("%-03d %s", i,testcmd[i].cmd);
    }

}

void test_all(void)
{
    test_regreset();
    test_sram();
    test_trxirq();
}

/**
 * @brief  Test the reset values of the chip
 *
 * Perform a chip reset and check all registers
 * according to there power on reset values.
 * Note:
 * If the transceiver is not in power on state,
 * the following errors are reported:
 *
<pre>
    ERR reset 0 offs=0x01 val=0x08 exp=0x00
    ERR reset 10 offs=0x10 val=0x04 exp=0x00
    ERR reset 11 offs=0x11 val=0x22 exp=0x02
    ERR reset 13 offs=0x18 val=0x64 exp=0x58
</pre>
 *
 *
 */
void test_regreset(void)
{
uint8_t i, err, val ;

    err = 0;

    TRX_RESET_LOW();
    TRX_SLPTR_LOW();
    DELAY_US(TRX_RESET_TIME_US);
    TRX_RESET_HIGH();

    for(i=0;i<sizeof(regrst)/sizeof(regval_t); i++)
    {
        val = trx_reg_read(regrst[i].offs);
        if (val != regrst[i].val)
        {
            PRINTF("ERR regreset nb=%d offs=0x%02x val=0x%02x exp=0x%02x",
                i, regrst[i].offs, val, regrst[i].val);
            err ++;
        }
    }
    if (err == 0)
    {
        PRINT("OK regreset");
    }
}

/**
 * @brief Test the SRAM
 *
 * This stresses the SPI bus and
 * shows if glitches disturb or influence the data
 * transfer
 *
 * If problems occur, try to reduce the SPI speed.
 */
void test_sram(void)
{

    TRX_RESET_LOW();
    TRX_SLPTR_LOW();
    DELAY_US(TRX_RESET_TIME_US);
    TRX_RESET_HIGH();
    trx_bit_write(SR_TRX_CMD,CMD_TRX_OFF);
    test_sram_const(0x00);
    test_sram_const(0x55);
    test_sram_const(0xaa);
    test_sram_const(0xff);
    test_sram_runbit();
    test_sram_address();

}

/**
 * @brief Test SRAM with constant pattern
 */
void test_sram_const(uint8_t pattern)
{
    uint8_t buf[MAX_FRAME_SIZE];
    uint8_t i, err = 0;
    /* test all 0 */
    memset(buf, pattern, sizeof(buf));
    trx_sram_write(0, sizeof(buf), buf);
    memset(buf, pattern ^ 0xff, sizeof(buf));
    trx_sram_read(0, sizeof(buf), buf);
    for (i=0;i<sizeof(buf); i++)
    {
        if (buf[i] != pattern)
        {
            PRINTF("ERR sram_const: addr=%d, pattern=0x%02x, exp=0x%02x",
             i, buf[i], pattern);
             err ++;
        }
    }

    if (err == 0)
    {
        PRINTF("OK sram_const(0x%02x)", pattern);
    }
}

/**
 * @brief Test SRAM with running 1 and running 0 pattern
 */
void test_sram_runbit(void)
{
    uint8_t buf[MAX_FRAME_SIZE];
    uint8_t i, err = 0, exp;

    /* running 1 */
    for (i=0; i<sizeof(buf); i++)
    {
        buf[i] = _BV(i&7);
    }
    trx_sram_write(0, sizeof(buf), buf);
    memset(buf, 0xff, sizeof(buf));
    trx_sram_read(0, sizeof(buf), buf);

    for (i=0;i<sizeof(buf); i++)
    {
        exp = _BV(i&7);
        if (buf[i] != exp)
        {
            PRINTF("ERR sram_runbit: addr=%d, pattern=0x%02x, exp=0x%02x",
             i, buf[i], _BV(i&7));
             err ++;
        }
    }

    /* running 0 */
    for (i=0; i<sizeof(buf); i++)
    {
        buf[i] = ~_BV(i&7);
    }
    trx_sram_write(0, sizeof(buf), buf);
    memset(buf, 0xff, sizeof(buf));
    trx_sram_read(0, sizeof(buf), buf);
    for (i=0;i<sizeof(buf); i++)
    {
        exp = ~_BV(i&7);
        if (buf[i] != exp)
        {
            PRINTF("ERR sram_runbit: addr=%d, pattern=0x%02x, exp=0x%02x",
                 i, buf[i], exp);
            err ++;
        }
    }

    if (err == 0)
    {
        PRINT("OK sram_runbit");
    }

}

/**
 * @brief Test SRAM with value = address pattern
 */
void test_sram_address(void)
{
    uint8_t buf[MAX_FRAME_SIZE];
    uint8_t i, err = 0;
    /* running 1 */
    for (i=0; i<sizeof(buf); i++)
    {
        buf[i] =i;
    }
    trx_sram_write(0, sizeof(buf), buf);
    memset(buf, 0xff, sizeof(buf));
    trx_sram_read(0, sizeof(buf), buf);

    for (i=0;i<sizeof(buf); i++)
    {
        if (buf[i] != i)
        {
            PRINTF("ERR sram_runbit: addr=%d, pattern=0x%02x, exp=0x%02x",
             i, buf[i], i);
             err ++;
        }
    }

    if (err == 0)
    {
        PRINT("OK sram_address");
    }
}


/**
 * Test IRQ occurence
 */
void test_trxirq(void)
{
uint8_t cnt;
    TRX_RESET_LOW();
    TRX_SLPTR_LOW();
    DELAY_US(TRX_RESET_TIME_US);
    TRX_RESET_HIGH();
    irqcnt = 0;
    irqcause = 0;

    trx_bit_write(SR_TRX_CMD,CMD_TRX_OFF);
    trx_bit_write(SR_TRX_CMD,CMD_PLL_ON);
    trx_bit_write(SR_CHANNEL,21);
    trx_bit_write(SR_CHANNEL,11);

    /* PLL Lock should occur within 128us */

    cnt = 32;
    do
    {
        _delay_us(180);
    }
    while (cnt-- && (irqcnt == 0) );

    if ((irqcnt > 0) && (irqcause & TRX_IRQ_PLL_LOCK))
    {
        PRINTF("OK trxirq %d, irqs=%d, badirqs=%d",
                cnt, irqcnt, badirqcnt);
    }
    else
    {
        PRINTF("ERR trxirq %d, irqs=%d, badirqs=%d cause=0x%x",
                cnt, irqcnt, badirqcnt,irqcause);
    }

}


void toggle_pin(void)
{
#if 0
extern volatile uint8_t IRQcause;
extern volatile uint8_t IRQcnt;

    PRINTF("IRQ: cause %x cnt %d",IRQcause, IRQcnt);
    PORT_SPI ^= SPI_SS;
    PRINTF("SPI: PORT %x, DDR: %x SS: %x", PORT_SPI, DDR_SPI, SPI_SS);
    //PORTB &=~0x4;
    //PRINTF("SPI: PORT %x, DDR: %x SS: %x", PORT_SPI, DDR_SPI, SPI_SS);

    PORT_TRX_SLPTR ^= MASK_TRX_SLPTR;
    PRINTF("SLP: PORT %x, DDR: %x", PORT_TRX_SLPTR, DDR_TRX_SLPTR);

    PORT_TRX_RESET ^= MASK_TRX_RESET;
    PRINTF("RST: PORT %x, DDR: %x", PORT_TRX_RESET, DDR_TRX_RESET);
#endif
}

#if !defined(TRX_IF_RFA1)
void spi_loop(void)
{
uint8_t i, val, val1, lastspsr;

    PRINTF("SPCR: 0x%02x, SPSR 0x%02x, DDR 0x%02x, PORT 0x%02x", SPCR, SPSR, DDR_SPI, PORT_SPI);
    for (i=0; i<8; i++)
    {
        val = _BV(i);
        SPDR = val;
        SPI_WAITFOR();
        lastspsr = SPSR;
        val1 = SPDR;
        PRINTF("SPI: %d : %02x -> %02x SPSR: %02x (%02x)", i, val, val1, SPSR, lastspsr);
    }
}
#endif  /* !RFA1 */


/**
 * read test command from uart
 */
void get_cmd(void)
{
    uint16_t inchar;
    uint8_t inbyte, cmdready = 0, i;

    inchar = hif_getc();

    if(inchar<0x100)
    {

        inbyte = (uint8_t)(inchar&0xff);
        if (inbyte == '\b')
        {
            if (cmdidx>0)
            {
                cmdidx--;
                hif_puts_p("\b \b");
           }
        }
        else if((inbyte == '\n') || (inbyte == '\r'))
        {
            cmdbuf[cmdidx] = 0;
            cmdidx = 0;
            cmdready = 1;
            hif_puts_p("\n\r");
        }
        else
        {
            cmdbuf[cmdidx++] = inbyte;
            hif_putc(inbyte);
        }
    }

    if (cmdready!=0)
    {
        hif_puts(">>");
        hif_puts(cmdbuf);
        hif_puts("\n\r");
        cmdready = 0;
               LED_SET_VALUE(1);

        for(i=0; i<sizeof(testcmd)/sizeof(test_command_t); i++)
        {

            if (strcmp(testcmd[i].cmd,cmdbuf)==0)
            {
               LED_SET_VALUE(0);
                testcmd[i].testfn();
               LED_SET_VALUE(1);
                break;
            }
        }
        if (i==sizeof(testcmd)/sizeof(test_command_t))
        {
            PRINTF("Unknown command '%s' - type help for available commands" , cmdbuf);
        }
    }
    LED_SET_VALUE(cmdidx);
}


void diag_irq_handler(uint8_t cause)
{

    irqcause |= cause;
    if (cause != 0)
    {
        irqcnt++;
    }
    else
    {
        badirqcnt++;
    }
    LED_SET_VALUE(irqcnt);
}
