/*******************************************************************************
 *
 *      A U D I O   R E C O R D E R
 *
 *      Copyright A Levido 2013 - All Rights Reserved
 *
 ******************************************************************************/
#ifdef __XC32
    #include <xc.h>          
#endif

#include <plib.h>
#include "system.h"
#include "menu.h"
#include "./USB/usb.h"

#pragma config FNOSC = PRIPLL           // Primary oscillator with PLL
#pragma config POSCMOD = EC             // External Clock mode
#pragma config FPLLIDIV = DIV_3         // 12MHz / 3 = 4MHz
#pragma config FPLLMUL = MUL_20         // 4MHz x 20 = 80MHz
#pragma config FPLLODIV = DIV_1         // 80MHz / 1 = 80MHz
#pragma config OSCIOFNC = ON            // CLKO pin is I/O
#pragma config FPBDIV = DIV_2           // Peripheral clock 80MHz / 2 = 40MHz
#pragma config FSOSCEN = ON             // Secondary oscilator on (32kHz)
#pragma config IESO = OFF               // Internal/external switchover disabled
#pragma config FCKSM = CSECMD           // Clk switch allowed, clk monitor off
#pragma config PWP = OFF

// USB
#pragma config FUSBIDIO = OFF           // USB ID pin is I/O
#pragma config FVBUSONIO = ON           // USB bus on pin enabled
#pragma config UPLLEN = ON              // USB PLL on
#pragma config UPLLIDIV = DIV_3         // 12MHz / 3 = 4MHz

// General
#pragma config FWDTEN = OFF             // Watchdog timer disabled
#pragma config WDTPS = 14               // Watchdog timeout is ~16 seconds
#pragma config ICESEL = ICS_PGx2        // debug pins share PGD2/PGC2
#pragma config FSRSSEL = PRIORITY_6

/* Event Queueu */
#define Q_LEN                       10
volatile EVENT eventQueue[Q_LEN];
volatile EVENT currentEvt;
volatile UINT32 qhead;
volatile UINT32 qtail;
extern UINT16 usbMode;

/* Main Function **************************************************************/

BOOL inInterruptHandler;
jmp_buf errJump, intJump;
INT main(void)
{
 
    UINT32 intStatus;

    DDPCONbits.JTAGEN = 0;
    SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);

    // I/O Ports
    PORTSetPinsAnalogIn(IOPORT_B, BIT_0 | BIT_1 | BIT_2 | BIT_5);
    PORTSetPinsDigitalIn(IOPORT_B,  BIT_8 | BIT_9 | BIT_10 | BIT_12 | BIT_13);
    PORTSetPinsDigitalOut(IOPORT_B, BIT_11 | BIT_14 | BIT_15);
    PORTSetPinsDigitalIn(IOPORT_C, BIT_12 | BIT_13 | BIT_14);
    PORTSetPinsDigitalOut(IOPORT_C, BIT_15);

    PORTSetPinsDigitalOut(IOPORT_D, BIT_0 | BIT_1 | BIT_2 | BIT_3 | BIT_4 | BIT_5 | BIT_6 | BIT_7 | BIT_8 | BIT_9 | BIT_10 | BIT_11);
    PORTSetPinsDigitalOut(IOPORT_E, BIT_0 | BIT_1 | BIT_2 | BIT_3 | BIT_4 | BIT_5 | BIT_6 | BIT_7 );
    PORTSetPinsDigitalIn(IOPORT_F, BIT_4);
    PORTSetPinsDigitalOut(IOPORT_F, BIT_0 | BIT_1 | BIT_3 | BIT_5);
    PORTSetPinsDigitalIn(IOPORT_G, BIT_2 | BIT_3 | BIT_6 | BIT_7 | BIT_9);
    PORTSetPinsDigitalOut(IOPORT_G, BIT_8);


    RED_LED_OFF;
    BAT_CHG_500;


    // Configure interrupt system
    INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR);
    INTEnableInterrupts();

   // USBDeviceInit();
    watchdogInit();
    menuInit();
    qhead = 0;
    qtail = 0;
    enqueueEvent(START, 0, 0, 0);
    while(1){
        UINT32 errcode;

        if(errcode = setjmp(errJump)){
            currentEvt.type = ERROR;
            currentEvt.code = errcode;
            menuProcess(currentEvt);
            INTEnableInterrupts();
            continue;
        }

        while(qhead == qtail) {
           if(usbMode == ATTACHED){
                if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) continue;
                MSDTasks();
            }
        }
        intStatus = INTDisableInterrupts();
        currentEvt.type = eventQueue[qtail].type;
        currentEvt.code = eventQueue[qtail].code;
        currentEvt.x = eventQueue[qtail].x;
        currentEvt.y = eventQueue[qtail].y;
        qtail++;
        if(qtail == Q_LEN) { qtail = 0; }
        INTRestoreInterrupts(intStatus);
        menuProcess(currentEvt);
        
    }
}

/* Enqueue and event **********************************************************/
void enqueueEvent(enum EVTYPE type, UINT16 code, INT16 x, INT16 y)
{
    UINT32 intStatus;

    intStatus = INTDisableInterrupts();
    eventQueue[qhead].type = type;
    eventQueue[qhead].code = code;
    eventQueue[qhead].x = x;
    eventQueue[qhead].y = y;
    qhead++;
    if(qhead == Q_LEN) { qhead = 0; }
    if(qhead == qtail) { fatalError(2); }
    INTRestoreInterrupts(intStatus);
}

void enqueueHighPriorityEvent(enum EVTYPE type, UINT16 code, INT16 x, INT16 y)
{
    UINT32 intStatus = INTDisableInterrupts();
    --qtail;
    if( qtail < 0 || qtail >= Q_LEN )
        qtail = Q_LEN-1;

    eventQueue[qtail].type = type;
    eventQueue[qtail].code = code;
    eventQueue[qtail].x = x;
    eventQueue[qtail].y = y;
    INTRestoreInterrupts(intStatus);
}

/* Exception & Error Handling *************************************************/
static UINT32 _excep_code;
static UINT32 _excep_addr;
static UINT32 i;
#define FLASH_RATE      2500000
#define FLASH_GAP       3 * FLASH_RATE;
UINT32 excount;

void fatalError(UINT32 code)
{
    while(1)
    {
        for(i = 0; i < 2 * code; i++){
            excount = FLASH_RATE;
            while(excount--);
            RED_LED_TOGGLE;
        }
        excount = FLASH_GAP;
        while(excount--);
    }
}

void _general_exception_handler(void)
{   
    // Get the exception cause and address
    _excep_code = ((_CP0_GET_CAUSE() & 0x0000007C) >> 2);
    _excep_addr = _CP0_GET_EPC();

    _CP0_SET_STATUS(_CP0_GET_STATUS()&0xFFFFFFE); /* Disable Interrupts */

    while(1)
    {
        for(i = 0; i < 2 *_excep_code; i++){
            excount = FLASH_RATE;
            while(excount--);
            RED_LED_TOGGLE;
        }
        excount = FLASH_GAP;
        while(excount--);
    }
}

