
#include "TCPIPConfig.h"

#include "TCPIP Stack/TCPIP.h"
#include "TCPIP Stack/BerkeleyAPI.h"


#include <stdlib.h>
#include "GenericTypeDefs.h"
#include "HardwareProfile.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "lcd.h"
#include "usb_config.h"
#include "USB/usb.h"
#include "USB/usb_host_generic.h"
#include "user.h"
#include "../include/USB/timer.h"
#include "../USB/usb_host_local.h"
#include "TCPIP Stack/Tick.h"
#include "node.h"


typedef struct{		
	BYTE	id[4];
	BYTE	flags[2];
	BYTE	reserve[2];
	BYTE	type_id;
	BYTE	dest_repeater_id;
	BYTE	send_repeater_id;
	BYTE	send_terminal_id;
	BYTE	seq_high;
	BYTE	seq_low;
	BYTE	control;
	BYTE	body[41];
} packet;

typedef struct
{
	BYTE	req_id[2];
	BYTE	flags[2];
	BYTE	PicVer[2];
	BYTE	reserve[2];
	BYTE	req_callsign[8];
	BYTE	zone[8];
	BYTE	area[8];
	DWORD	ip_address;
	WORD	ssn;
	WORD	port;
} call_table;

// Application States
typedef enum
{
    USB_INITIALIZE = 0,                // Initialize the app when a device is attached
    USB_STATE_IDLE,                    // Inactive State
    USB_STATE_LOOP                    // Inactive State


} USB_STATE;


// *****************************************************************************
// *****************************************************************************
// Global Variables
// *****************************************************************************
// *****************************************************************************

AD_PARAM	AdParam;

extern BYTE		header[41], voice[12];
BYTE        deviceAddress;  // Address of the device on the USB
DATA_PACKET DataPacket;     // Data to send to the device
USB_STATE	USBState;      // Current state of the application
BYTE        LcdData[2][16]; // LCD Display data
WORD        SwitchState;    // Bitmap of Switch states (only 3 & 6 are used)

static void	send_header_USB (BYTE pkt[]);
static void	send_voice_USB (BYTE pkt[]);
static void	from_Inet(void );
static void	from_Rig(void);
static void	send_header_queue(void);
static void	send_voice_queue( void );


static void	RF_CallsignDisp( BYTE header[] );
static void	Inet_CallsignDisp( BYTE header[] );
DWORD	usb_control_msg (BYTE bmRequestType, BYTE request, WORD Value, WORD Index, BYTE *data, WORD Length, WORD timeout);
DWORD	TickGet( void);
void	LoadAdParam( void );
void	SaveAdParam( void );


extern WORD	DefaultVid;
extern WORD	DefaultPid;

extern	WORD	SSN;
static	BYTE	LCDText[32];

//******************************************************************************
//******************************************************************************
// Local Routines
//******************************************************************************
//******************************************************************************

/*************************************************************************
 * Function:        InitializeSystem
 *
 * Preconditions:   None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Returns:         TRUE if successful, FALSE if not.
 *
 * Side Effects:    See below
 *
 * Overview:        This routine initializes the processor and peripheral,
 *                  setting clock speeds and enabling any required
 *                  features.
 *************************************************************************/

BOOL InitializeSystem ( void )
{
    // Set Default demo state
    USBState = USB_INITIALIZE;
	AdParam.UpdateFlag = 0x00;

    return TRUE;
} // InitializeSystem


/*************************************************************************
 * Function:        CheckForNewAttach
 *
 * Preconditions:   None
 *
 * Input:           None
 *
 * Output:          deviceAddress (global)
 *                  Updates the device address when an attach is found.
 *
 * Returns:         TRUE if a new device has been attached.  FALSE,
 *                  otherwise.
 *
 * Side Effects:    Prints attach message
 *
 * Overview:        This routine checks to see if a new device has been
 *                  attached.  If it has, it records the address.
 *************************************************************************/

BOOL CheckForNewAttach ( void )
{
    // Try to get the device address, if we don't have one.
    if (deviceAddress == 0)
    {
        GENERIC_DEVICE_ID DevID;

        DevID.vid   = DefaultVid;
        DevID.pid   = DefaultPid;
        #ifdef USB_GENERIC_SUPPORT_SERIAL_NUMBERS
            DevID.serialNumberLength = 0;
            DevID.serialNumber = NULL;
        #endif

        if (USBHostGenericGetDeviceAddress(&DevID))
        {
            deviceAddress = DevID.deviceAddress;
            return TRUE;
        }
    }

    return FALSE;

} // CheckForNewAttach

/*************************************************************************
 * Function:        ManageDemoState
 *
 * Preconditions:   The DemoState global variable must be initialized to
 *                  USB_STATE_IDLE (0).  (This occurs on reset.)
 *
 * Input:           DemoState (global)
 *                  Actions selected based value of DemoState on function
 *                  entry.
 *
 *                  deviceAddress (global)
 *                  May use device address to access device, depending on
 *                  state.
 *
 *                  DataPacket (global)
 *                  May read data from packet buffer, depending on state.
 *
 * Output:          DemoState (global)
 *                  Updates demo state as appropriate.
 *
 *                  DataPacket (global)
 *                  May cause data in the packet buffer to be updated,
 *                  depending on state.
 *
 * Returns:         None
 *
 * Side Effects:    Depend on state transition
 *
 * Overview:        This routine maintains the state of the application,
 *                  updateing global data and taking actions as necessary
 *                  to maintain the custom demo operations.
 *************************************************************************/
void ManageState ( void )
{
    // Watch for device detaching
    if (USBHostGenericDeviceDetached(deviceAddress) && deviceAddress != 0)
    {
        USBState = USB_INITIALIZE;
        deviceAddress   = 0;
		memcpy (LCDText, "device detached ", 16);
		xMessage.pcMessage = LCDText;
 		xMessage.xMinDisplayTime = 5000 / portTICK_RATE_MS;
		xQueueSend( xLCDQueue, &xMessage, 0 );
    }


    switch (USBState)
    {
    case USB_INITIALIZE:
        USBState = USB_STATE_IDLE;
		AdParam.UpdateFlag = 0x00;
        break;

    /** Idle State:  Loops here until attach **/
    case USB_STATE_IDLE:
        if (CheckForNewAttach())
        {
            USBState = USB_STATE_LOOP;
        }
        break;

    case USB_STATE_LOOP:

		if (AdParam.UpdateFlag == 0x00) LoadAdParam();
		else if (AdParam.UpdateFlag == 0x02) SaveAdParam();

		from_Rig();
		from_Inet();	 	/* check message queue from Inet */
		break;
    }
} // ManageDemoState


//******************************************************************************
//******************************************************************************
// USB Support Functions
//******************************************************************************
//******************************************************************************

/*************************************************************************
 * Function:        USB_ApplicationEventHandler
 *
 * Preconditions:   The USB must be initialized.
 *
 * Input:           event       Identifies the bus event that occured
 *
 *                  data        Pointer to event-specific data
 *
 *                  size        Size of the event-specific data
 *
 * Output:          deviceAddress (global)
 *                  Updates device address when an attach or detach occurs.
 *
 *                  DemoState (global)
 *                  Updates the demo state as appropriate when events occur.
 *
 * Returns:         TRUE if the event was handled, FALSE if not
 *
 * Side Effects:    Event-specific actions have been taken.
 *
 * Overview:        This routine is called by the Host layer or client
 *                  driver to notify the application of events that occur.
 *                  If the event is recognized, it is handled and the
 *                  routine returns TRUE.  Otherwise, it is ignored (or
 *                  just "sniffed" and the routine returns FALSE.
 *************************************************************************/

BOOL USB_ApplicationEventHandler ( BYTE address, USB_EVENT event, void *data, DWORD size )
{
	BYTE	*pDesc;

    #ifdef USB_GENERIC_SUPPORT_SERIAL_NUMBERS
        BYTE i;
    #endif

    // Handle specific events.
    switch (event)
    {
        case EVENT_GENERIC_ATTACH:
            if (size == sizeof(GENERIC_DEVICE_ID))
            {
                deviceAddress   = ((GENERIC_DEVICE_ID *)data)->deviceAddress;
                USBState = USB_STATE_LOOP;
				pDesc = USBHostGetDeviceDescriptor(deviceAddress);
				pDesc += 12;
				
                return TRUE;
            }
            break;

        case EVENT_GENERIC_DETACH:
            deviceAddress   = 0;
            USBState = USB_INITIALIZE;
            return TRUE;

        case EVENT_GENERIC_TX_DONE:           // The main state machine will poll the driver.
        case EVENT_GENERIC_RX_DONE:
            return TRUE;

        case EVENT_VBUS_REQUEST_POWER:
            // We'll let anything attach.
            return TRUE;

        case EVENT_VBUS_RELEASE_POWER:
            // We aren't keeping track of power.
            return TRUE;

        case EVENT_HUB_ATTACH:
//            UART2PrintString( "\r\n***** USB Error - hubs are not supported *****\r\n" );
            return TRUE;
            break;

        case EVENT_UNSUPPORTED_DEVICE:
//            UART2PrintString( "\r\n***** USB Error - device is not supported *****\r\n" );
            return TRUE;
            break;

        case EVENT_CANNOT_ENUMERATE:
//            UART2PrintString( "\r\n***** USB Error - cannot enumerate device *****\r\n" );
            return TRUE;
            break;

        case EVENT_CLIENT_INIT_ERROR:
//            UART2PrintString( "\r\n***** USB Error - client driver initialization error *****\r\n" );
            return TRUE;
            break;

        case EVENT_OUT_OF_MEMORY:
//            UART2PrintString( "\r\n***** USB Error - out of heap memory *****\r\n" );
            return TRUE;
            break;

        case EVENT_UNSPECIFIED_ERROR:   // This should never be generated.
//            UART2PrintString( "\r\n***** USB Error - unspecified *****\r\n" );
            return TRUE;
            break;

        case EVENT_SUSPEND:
        case EVENT_DETACH:
        case EVENT_RESUME:
        case EVENT_BUS_ERROR:
            return TRUE;
            break;

        default:
            break;
    }

    return FALSE;

} // USB_ApplicationEventHandler



/*************************************************************************
 * Function:        main
 *
 * Preconditions:   None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Returns:         Never exits
 *
 * Side Effects:    Runs the application
 *
 * Overview:        This is the USB Custom Demo Application's main entry
 *                  point.
 *************************************************************************/

int prvTaskUSB ( void )
{
   // Initialize the processor and peripherals.
    if ( InitializeSystem() != TRUE )
    {
//        printf ( "\r\n\r\nCould not initialize USB Custom Demo App - system.  Halting.\r\n\r\n" );
        while (1);
    }
    if ( USBHostInit(0) == TRUE )
    {
//        printf( "\r\n\r\n***** USB Custom Demo App Initialized *****\r\n\r\n" );
    }
    else
    {
//        printf ( "\r\n\r\nCould not initialize USB Custom Demo App - USB.  Halting.\r\n\r\n" );
        while (1);
    }

    // Main Processing Loop
    while (1)
    {
        // This demo does not check for overcurrent conditions.  See the
        // USB Host - Data Logger for an example of overcurrent detection
        // with the PIC24F and the USB PICtail Plus.

        // Maintain USB Host State
        USBHostTasks();
        // Maintain Demo Application State
        ManageState();
    }

    return 0;

} // main

/**************************************************************/
/*  From INET */
void	from_Inet()
{
	static DWORD	dwTimer;
	static BYTE	dummy;
	static	packet	q_pkt;
	extern	xQueueHandle xDstarRcvQueue;
	extern	BOOL headerRead;

	if (headerRead) return;
	if (xQueuePeek (xDstarRcvQueue, &q_pkt, 0) == pdTRUE)
	{
		dwTimer = TickGet();
		if (q_pkt.type_id == 0x10) send_header_USB(q_pkt.body);
		else if (q_pkt.type_id == 0x20) send_voice_USB(q_pkt.body);
	} else {
		if((TickGet() - dwTimer) > (2 * TICK_SECOND))
		{
			/* PTT ON */
			usb_control_msg(0x40, SET_PTT, OFF, 0, &dummy, 0, 100);
		}
	}
}

void	send_header_USB(BYTE pkt[])
{
	extern	xQueueHandle xDstarRcvQueue;
	static	packet	q_pkt;
	static	BYTE	dummy;

	/* Callsign set */
    usb_control_msg(0x40, SET_MyCALL2, 0, 0, &pkt[35], 4, 100);
	usb_control_msg(0x40, SET_MyCALL, 0, 0, &pkt[27], 8, 100);
	usb_control_msg(0x40, SET_YourCALL, 0, 0, &pkt[19], 8, 100);
	usb_control_msg(0x40, SET_RPT1CALL, 0, 0, &pkt[11], 8, 100);
	usb_control_msg(0x40, SET_RPT2CALL, 0, 0, &pkt[3], 8, 100);
    usb_control_msg(0x40, SET_FLAGS, 0, 0, &pkt[0], 3, 100);

	xQueueReceive (xDstarRcvQueue, &q_pkt, 0);  /* dummy read */
	Inet_CallsignDisp (pkt);	
	/* PTT ON */
	usb_control_msg(0x40, SET_PTT, ON, 0, &dummy, 0, 100);

}

void	send_voice_USB (BYTE pkt[])
{
	static	packet	q_pkt;
	extern	xQueueHandle xDstarRcvQueue;

	usb_control_msg(0x40, PUT_DATA, 0, 0, pkt, 12, 100);
	xQueueReceive (xDstarRcvQueue, &q_pkt, 0);  /* dummy read */
}

/*************************************************/
/*  From RIG */
void	from_Rig()
{
//	static	BYTE lastframe1[3] ={0x55,0x55,0x55};
//	static	BYTE lastframe2[3] ={0x55,0xc8,0x7a};
	static	BYTE status;
	static	BYTE ssn_temp[2];
	static	DWORD length;
	static	WORD	i;
	extern	WORD voice_pnt;
	extern	BOOL	headerRead;
	extern	BYTE	rig_buffer[32];

		/* rf header send */

	if (!headerRead)
	{	
        if (usb_control_msg (0xC0, GET_HEADER, 0, 0, header, 32, 100) !=32) return;  /*RF header read? */
        usb_control_msg (0xC0, GET_HEADER, 0, 0, &header[32], 9, 100);
  		usb_control_msg (0xC0, GET_SN_VALUE, 0, 0, ssn_temp, 2, 100);
		SSN = ssn_temp[0] * 4 + (ssn_temp[1] >> 6);
		send_header_queue();
		RF_CallsignDisp(header);
		headerRead = TRUE;
		voice_pnt = 0;
	} else {
		/* rf voice send */
		length = usb_control_msg (0xC0, GET_DATA, 0, 0, rig_buffer, 32, 100);
		if (length == 0)
		{
    	    usb_control_msg (0xC0, GET_AD_STATUS, 0, 0, &status, 1, 100);	
			if (status & COS_OnOff) return;
			headerRead = FALSE;
			voice[9] = 0x55;
			voice[10] = 0x55;
			voice[11] = 0x55;
			send_voice_queue();
			voice[0] = 0x55;
			voice[1] = 0xc8;
			voice[2] = 0x7a;
			send_voice_queue();
			return;
		} else if (length <=32)
		{	
			for (i = 0 ; i < length; i++)
			{
				voice[voice_pnt] = rig_buffer[i];
				voice_pnt++;
				if (voice_pnt == 12)
				{
					voice_pnt = 0;
					send_voice_queue();
				}
			}
		}
	}
}


void	send_voice_queue() 	/* read from rig (DV packet) */
{
	packet pkt;
	extern xQueueHandle xDstarSndQueue;
	extern BYTE voice[12];
	extern WORD seq;

	memcpy (pkt.body, voice, 12);
	memcpy (pkt.id,  "DVND" ,4);
	pkt.type_id = 0x20;			/* voice */
	pkt.dest_repeater_id = 0x20;
	pkt.send_repeater_id = 'N';
	pkt.send_terminal_id = 'E';;
	pkt.seq_high = seq >> 8;
	pkt.seq_low = seq & 0xff;
	seq++;
	if (seq >= 2100) seq = 0;  /* 2 sec. */							
	xQueueSend (xDstarSndQueue, &pkt, 20 / portTICK_RATE_MS);
}


void	send_header_queue() 	/* read from rig (DV packet) */
{
	extern BYTE header[41];
	extern xQueueHandle xDstarSndQueue;
	extern	WORD	SSN;
	extern	WORD seq;
	packet pkt;
	call_table call_pkt;

/* RF header send */
		call_pkt.ssn = SSN;
//		send_dv_callEntry (call_pkt);


		memcpy (pkt.body, header, 41);
		memcpy (pkt.id,  "DVND" ,4);
		pkt.type_id = 0x10;		/* header */
		pkt.dest_repeater_id = 0x20;
		pkt.send_repeater_id = 'N';
		pkt.send_terminal_id = 'E';;
		pkt.seq_high = 0;
		pkt.seq_low = 0;
		xQueueSend (xDstarSndQueue, &pkt, 20 / portTICK_RATE_MS);
		seq	= 0;
		return;
}

void	RF_CallsignDisp(BYTE RFheader[])
{
	xLCDMessage xMessage;
	extern xQueueHandle xLCDQueue;

		memset (LCDText, 0x20, 32);
		memcpy (&LCDText[0],&RFheader[27], 8);
		memcpy (&LCDText[16],&RFheader[19], 8);
		if (strncmp (&RFheader[35],"    ",4))
		{
			LCDText[8] = '/';
			memcpy (&LCDText[9],&RFheader[35], 4);
		}
		strncpy (&LCDText[27], "Radio", 5);
		xMessage.pcMessage = LCDText;
 		xMessage.xMinDisplayTime = 2000 / portTICK_RATE_MS;
		xQueueSend( xLCDQueue, &xMessage, 0 );
	return;
}

void	Inet_CallsignDisp(BYTE RFheader[])
{
	xLCDMessage xMessage;
	extern xQueueHandle xLCDQueue;

		memset (LCDText, 0x20, 32);
		memcpy (&LCDText[0],&RFheader[27], 8);
		memcpy (&LCDText[16],&RFheader[19], 8);
		if (strncmp (&RFheader[35],"    ",4))
		{
			LCDText[8] = '/';
			memcpy (&LCDText[9],&RFheader[35], 4);
		}
		strncpy (&LCDText[27]," Inet",5);
		xMessage.pcMessage = LCDText;
 		xMessage.xMinDisplayTime = 2000 / portTICK_RATE_MS;
		xQueueSend( xLCDQueue, &xMessage, 0 );
	return;
}

DWORD  usb_control_msg (BYTE bmRequestType, BYTE bRequest, WORD wValue, WORD wIndex, BYTE *data, WORD wLength, WORD timeout)
{
	static DWORD byteCount;
	static BYTE errorCode;
	static BYTE RetCode;
	static DWORD dwRetCode;

	if (bmRequestType & 0x80)
	{
		RetCode = USBHostIssueDeviceRequest( 1, bmRequestType, bRequest, wValue, wIndex, wLength, data, USB_DEVICE_REQUEST_GET, 0xff);
		if (RetCode== USB_SUCCESS)
		{
			if (timeout == 0)
			{
				return 0ul;
			}
			while (!USBHostTransferIsComplete( 1, 0, &errorCode, &byteCount)) ;
			return byteCount;
		} else if (RetCode == USB_ENDPOINT_BUSY) return 0ul;
		else
		{
			dwRetCode = RetCode + 1000;
			return dwRetCode;
		}
	} else {
		RetCode = USBHostIssueDeviceRequest( 1, bmRequestType, bRequest, wValue, wIndex, wLength, data, USB_DEVICE_REQUEST_SET, 0xff);
		if (RetCode == USB_SUCCESS)
		{
			if (timeout == 0)
			{
				if (USBHostTransferIsComplete( 1, 0, &errorCode, &byteCount)) return byteCount;
				else return 1021ul;
			}
			while (!USBHostTransferIsComplete( 1, 0, &errorCode, &byteCount )) ;
			return byteCount;
		} else if (RetCode == USB_ENDPOINT_BUSY) return 1000ul;
		else return 1020ul;
	}
	return byteCount;
}


void	LoadAdParam()
{

		AdParam.UpdateFlag = 0x01;
  	    usb_control_msg (0xC0, GET_DelayTime, 0, 0, &AdParam.DelayTime, 1, 100);
  	    usb_control_msg (0xC0, GET_TimeOut, 0, 0, &AdParam.TimeOut, 1, 100);
  	    usb_control_msg (0xC0, GET_KeepAlive, 0, 0, &AdParam.KeepAlive, 1, 100);	
  	    usb_control_msg (0xC0, GET_JitterBufferSize, 0, 0, &AdParam.JitterSize, 1, 100);	
  	    usb_control_msg (0xC0, GET_MODE, 0, 0, &AdParam.Mode, 1, 100);	
  	    usb_control_msg (0xC0, GET_MODE2, 0, 0, &AdParam.Mode2, 1, 100);	
  	    usb_control_msg (0xC0, GET_INVERT_STATUS, 0, 0, &AdParam.Invert, 1, 100);	
  	    usb_control_msg (0xC0, GET_SN_SQ, 0, 0, (BYTE *)&AdParam.SN_Squelch, 2, 100);	
	
}

void	SaveAdParam()
{
	BYTE	dummy;
	DWORD	ret;

  	    ret = usb_control_msg (0x40, SET_DelayTime, AdParam.DelayTime, 0, &dummy, 0, 100);
  	    ret = usb_control_msg (0x40, SET_TimeOut, AdParam.TimeOut, 0, &dummy, 0, 100);
  	    ret = usb_control_msg (0x40, SET_KeepAlive, AdParam.KeepAlive, 0, &dummy, 0, 100);	
  	    ret = usb_control_msg (0x40, SET_JitterBufferSize, AdParam.JitterSize, 0, &dummy, 0, 100);	
		if (AdParam.Mode & 0x02)
		{
			usb_control_msg(0x40, SET_CRC_CHECK, ON, 0, &dummy, 0, 100);
		} else {
			usb_control_msg(0x40, SET_CRC_CHECK, OFF, 0, &dummy, 0, 100);
		}
		if (AdParam.Mode & 0x04)
		{
			usb_control_msg (0x40, SET_COS_CHECK, ON, 0, &dummy, 0, 100);
		} else {
			usb_control_msg (0x40, SET_COS_CHECK, OFF, 0, &dummy, 0, 100);
		}	
		if (AdParam.Mode & 0x08)
		{
			usb_control_msg (0x40, SET_LastFrame, ON, 0, &dummy, 0, 100);
		} else {
			usb_control_msg (0x40, SET_LastFrame, OFF, 0, &dummy, 0, 100);
		}
		if (AdParam.Mode & 0x80)
		{
			usb_control_msg (0x40, SET_HalfFull, ON, 0, &dummy, 0, 100);
		} else {
			usb_control_msg (0x40, SET_HalfFull, OFF, 0, &dummy, 0, 100);
		}
		if (AdParam.Mode2 & 0x01)
		{
			usb_control_msg (0x40, SET_HeaderGen, ON, 0, &dummy, 0, 100);
		} else {
			usb_control_msg (0x40, SET_HeaderGen, OFF, 0, &dummy, 0, 100);
		}
		if (AdParam.Mode2 & 0x02)
		{
			usb_control_msg (0x40, SET_HeaderGenType, ON, 0, &dummy, 0, 100);
		} else {
			usb_control_msg (0x40, SET_HeaderGenType, OFF, 0, &dummy, 0, 100);
		}
		if (AdParam.Invert & 0x80)
		{
			usb_control_msg (0x40, SET_AutoRxDetect, ON, 0, &dummy, 0, 100);
		} else {
			usb_control_msg (0x40, SET_AutoRxDetect, OFF, 0, &dummy, 0, 100);
		}
		if (AdParam.Invert & 0x01)
		{
			usb_control_msg (0x40, SET_TX_Invert, ON, 0, &dummy, 0, 100);
		} else {
			usb_control_msg (0x40, SET_TX_Invert, OFF, 0, &dummy, 0, 100);
		}
		if (AdParam.Invert & 0x02)
		{
			usb_control_msg (0x40, SET_RX_Invert, ON, 0, &dummy, 0, 100);
		} else {
			usb_control_msg (0x40, SET_RX_Invert, OFF, 0, &dummy, 0, 100);
		}
  	    ret = usb_control_msg (0x40, SET_SN_SQ, AdParam.SN_Squelch, 0, &dummy, 0, 100);	
		AdParam.UpdateFlag = 0x00;
			
}

/*************************************************************************
 * EOF main.c
 */

