/******************************************************************************
 *
 * Generic Host Demo - Main Application
 *   
 ******************************************************************************
 * Description:
 * This file provides the main entry point to the Microchip USB Generic Host demo.
 * This demo shows how a PIC24/PIC32 system could be used to act as the host, 
 * controlling a USB device running the Microchip Generic Device demo.
 *
 ******************************************************************************
 * Filename:     main.c
 * Dependancies: See "Includes" section, below.
 *
 * Software License Agreement
 *
 * The software supplied herewith by Microchip Technology Incorporated
 * (the “Company”) for its PICmicro® Microcontroller is intended and
 * supplied to you, the Company’s customer, for use solely and
 * exclusively on Microchip PICmicro Microcontroller products. The
 * software is owned by the Company and/or its supplier, and is
 * protected under applicable copyright laws. All rights are reserved.
 * Any use in violation of the foregoing restrictions may subject the
 * user to criminal sanctions under applicable laws, as well as to
 * civil liability for the breach of the terms and conditions of this
 * license.
 * 
 * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 * Copyright (c) 2008 Microchip Technology, Inc.
 *
 ***************************************************************************
 * Change History:
 * ID			Date            Changes
 * BC			2006			Initial Creation
 * C11842		12/01/2008		Updated comments, 80MHz operation
******************************************************************************/
 
#include <string.h>
#include <stdlib.h>
#include <plib.h>
#include "GenericTypeDefs.h"
#include "HardwareProfile.h"
#include "mstimer.h"
#include "USB/usb.h"
//#include "system.h"
#include "USB/usb_host_generic.h"
#include "user.h"


/**********************
 * Configuration Bits *
 **********************/

// Configuration Bit settings
#ifndef OVERRIDE_CONFIG_BITS

    #pragma config UPLLEN   = ON            // USB PLL Enabled
    #pragma config FPLLMUL  = MUL_20        // PLL Multiplier
    #pragma config UPLLIDIV = DIV_2         // USB PLL Input Divider
    #pragma config FPLLIDIV = DIV_2         // PLL Input Divider
    #pragma config FPLLODIV = DIV_1         // PLL Output Divider
    #pragma config FPBDIV   = DIV_1         // Peripheral Clock divisor
    #pragma config FWDTEN   = OFF           // Watchdog Timer
    #pragma config WDTPS    = PS1           // Watchdog Timer Postscale
    #pragma config FCKSM    = CSDCMD        // Clock Switching & Fail Safe Clock Monitor
    #pragma config OSCIOFNC = OFF           // CLKO Enable
    #pragma config POSCMOD  = HS            // Primary Oscillator
    #pragma config IESO     = OFF           // Internal/External Switch-over
    #pragma config FSOSCEN  = OFF           // Secondary Oscillator Enable (KLO was off)
    #pragma config FNOSC    = PRIPLL        // Oscillator Selection
    #pragma config CP       = OFF           // Code Protect
    #pragma config BWP      = OFF           // Boot Flash Write Protect
    #pragma config PWP      = OFF           // Program Flash Write Protect
    #pragma config ICESEL   = ICS_PGx2      // ICE/ICD Comm Channel Select
    #pragma config DEBUG    = OFF           // Debugger Disabled for Starter Kit
    
#endif // OVERRIDE_CONFIG_BITS


/***************
 * Definitions *
 ***************/

// Switch & LED States
typedef union 
{
    BYTE    byte;
    struct _BIT
    {
        BYTE    b0 : 1;
        BYTE    b1 : 1;
        BYTE    b2 : 1;
        BYTE    b3 : 1;
        BYTE    b4 : 1;
        BYTE    b5 : 1;
        BYTE    b6 : 1;
        BYTE    b7 : 1;
    }bit;
}BIT_STATE;


// Application States
typedef enum 
{
    DEMO_INITIALIZE = 0,                // Initialize the app when a device is attached
    DEMO_STATE_IDLE,                    // Inactive State
    DEMO_STATE_RUNNING,                 // Running normally between this value and DEMO_STATE_ERROR

    DEMO_STATE_GET_DEV_VERSION,         // New device attached, send Get-Dev-FW-Version command
    DEMO_STATE_WAITING_VER_REQ,         // Waiting for Get Version command to complete
    DEMO_STATE_READ_DEV_VERSION,        // Start reading Dev FW version data
    DEMO_STATE_WAITING_READ_VER,        // Waiting for read of version data to complete
    DEMO_STATE_VERIFY_DEV_FW_VER,       // Dev FW Ver received, verify & display it

    DEMO_STATE_GET_TEMPERATURE,         // Send get-temperature command
    DEMO_STATE_WAITING_GET_TEMP,        // Waiting for get-temperature command to complete
    DEMO_STATE_READ_TEMPERATURE,        // Start reading temperature data
    DEMO_STATE_WAITING_READ_TEMP,       // Waiting for read of temperature data to complete
    DEMO_STATE_DISPLAY_TEMPERATURE,     // Temperature data received, display it

    DEMO_STATE_GET_POT,                 // Send get-POT command
    DEMO_STATE_WAITING_GET_POT,         // Waiting for get-POT command to complete
    DEMO_STATE_READ_POT,                // Start reading POT data
    DEMO_STATE_WAITING_READ_POT,        // Waiting for read of POT data to complete
    DEMO_STATE_DISPLAY_POT,             // POT data received, display it

    DEMO_STATE_SEND_SET_LED,            // Send set-LED command
    DEMO_STATE_WAITING_SET_LED,         // Waiting for set-LED command to complete
    DEMO_STATE_READ_SET_LED_RESP,       // Start reading set-LED response
    DEMO_STATE_WAITING_LED_RESP,        // Waiting for read of LED response to complete
    DEMO_STATE_UPDATE_LED_STATE,        // Set-LED command successful, update state.

    DEMO_STATE_ERROR                    // An error has occured

} DEMO_STATE;


/***********************
 * Application Globals *
 ***********************/

BYTE        DevAddr;                // Address of the device on the USB
DATA_PACKET DataPacket;             // Data to send to the device
DEMO_STATE  DemoState;              // Current state of the demo application
BYTE        MsgData[2][MSG_LENGTH]; // Msg Display data
UINT32      SwitchState;            // Bitmap of Switch states (only 3 & 6 are used)
BIT_STATE   LEDState;               // Bitmap of LED states (only 3 & 4 are used)
BOOL        DeviceAttached;         // Indicates if a device has been attached

// Initial data for the LCD display.
const char *MessageData[2] = { " Host -.- Dev -.-\n", " --.-C ----- Ohms\n" };

// Macro to fill a line of LCD data.
#define FILL_MSG_LINE(l,p)                  \
        {                                   \
            int i;                          \
            for (i=0; i<MSG_LENGTH; i++)    \
                MsgData[(l)-1][i]=(p)[i];   \
        }


/******************
 * 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.
                  
 Note:            This routine is only from "main", so it is defined as
                  a "static inline" routine to avoid function call 
                  overhead.
 *************************************************************************/

static inline BOOL InitializeSystem ( void )
{
    // Configure the device for maximum performance, but do not change the PBDIV clock divisor.
	// Given the options, this function will change the program Flash wait states,
	// RAM wait state and enable prefetch cache, but will not change the PBDIV.
    // The PBDIV value is already set via the pragma FPBDIV option above.
   	SYSTEMConfig(GetSystemClock(), SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);

    // Enable system interrupts
    INTEnableSystemMultiVectoredInt();

    //Initialize the IO channel
	InitMessages();
 
    // Init LEDs
    InitAllLEDs();

    // Init Switches
    InitAllSwitches();
    SwitchState = SW1|SW2|SW3;

    // Init the timer
    MSTimerInit(GetSystemClock());

    // Set Default demo state
    DeviceAttached = FALSE;
    DemoState      = DEMO_INITIALIZE;


    return TRUE;

} // InitializeSystem


/*************************************************************************
 Functions:       GetDataSwitchWasPressed,
                  LED3SwitchWasPressed & 
                  LED4SwitchWasPressed

 Preconditions:   Assumes SwitchState & Switches have been initialized
                  
 Input:           None

 Output:          None

 Returns:         TRUE if the switch has been pressed, FALSE otherwise

 Side Effects:    SwitchState has been updated

 Overview:        Determines if the switched named has been pressed
                  since last time the routine was called.
                  
 Note:            These routines are each only called once from 
                  "ManageDemoState", so it is defined as a "static inline"
                  routine to avoid function call overhead.
 *************************************************************************/

static inline BOOL GetDataSwitchWasPressed(void)
{
    UINT32 data_sw_state;

    data_sw_state = mPORTDReadBits(SW1);

    if( data_sw_state != (SwitchState & SW1) )
    {
        SwitchState &= ~SW1;
        SwitchState |= data_sw_state;

        if(SwitchState & SW1)
            return TRUE;                // Was pressed
    }

    return FALSE;                       // Was not pressed

} // GetDataSwitchWasPressed

static inline BOOL LED3SwitchWasPressed(void)
{
    UINT32 led3sw_state;

    led3sw_state = mPORTDReadBits(SW2);

    if( led3sw_state != (SwitchState & SW2) )
    {
        SwitchState &= ~SW2;
        SwitchState |= led3sw_state;

        if(SwitchState & SW2)
            return TRUE;                // Was pressed
    }

    return FALSE;                       // Was not pressed

} // LED3SwitchWasPressed

static inline BOOL LED4SwitchWasPressed(void)
{
    UINT32 led4sw_state;

    led4sw_state = mPORTDReadBits(SW3);

    if( led4sw_state != (SwitchState & SW3) )
    {
        SwitchState &= ~SW3;
        SwitchState |= led4sw_state;

        if(SwitchState & SW3)
            return TRUE;                // Was pressed
    }

    return FALSE;                       // Was not pressed

}//end LED4SwitchWasPressed


/*************************************************************************
 Function:        InitialMessage
 
 Preconditions:   Assumes that INTEnableSystemMultiVectoredInt and 
                  InitMessages have been called.
                  
 Input:           data    Pointer to 2-line string array to display
 
 Output:          None
 
 Returns:         None
 
 Side Effects:    The first messasge has been displayed and the MsgData 
                  strings have been modified
 
 Overview:        Displays the App's initial message
                  
 Note:            This routine is only called once from "ManageDemoState",
                  so it is defined as a "static inline" routine to avoid 
                  function call overhead.
 *************************************************************************/

static inline void InitialMessage ( const char **data )
{
    // Send initial messages.
    SendMessage1();
    SendMessage2();

    // Init the message buffers
    FILL_MSG_LINE(1, data[0]);
    FILL_MSG_LINE(2, data[1]);
    MsgData[0][6] = '0' + USB_HOST_FW_MAJOR_VER;
    MsgData[0][8] = '0' + USB_HOST_FW_MINOR_VER;

} // InitialMessage


/*************************************************************************
 * Function:        ValidateAndDisplayDeviceFwVersion
 *
 * Preconditions:   Assumes the device FW Version response packet has been
 *                  received an the message IO has been initialized.
 *                  
 * Input:           packet  Pointer to the data packet containing the 
 *                          version data.
 *
 * Output:          None
 *
 * Returns:         TRUE if the Device FW data is valid, FALSE if not.
 *
 * Side Effects:    The LCD display and message strings have been modified.
 *
 * Overview:        Validates data in the packet buffer against expected
 *                  device firmware version and displays it on the LCD and
 *                  serial port.
 *************************************************************************/

static inline BOOL ValidateAndDisplayDeviceFwVersion ( DATA_PACKET *packet )
{
    // Display Host & Device FW version (whether it's correct or not)
    MsgData[0][14] = '0' + packet->_byte[3];
    MsgData[0][16] = '0' + packet->_byte[4];
    SendMessage(1, &MsgData[0][0] );

    // Was the data valid and was the version number supported?
    if ( packet->CMD      == READ_VERSION          &&
         packet->len      == 2                     &&
         packet->_byte[3] >= DEMO_FW_MAJOR_VERSION   )
    {
        return TRUE;
    }

    // Otherwise, indicate error
    SendMessage( 2, "Dev FW Ver Error" );
    DemoState = DEMO_STATE_ERROR;
    return FALSE;

} // ValidateAndDisplayDeviceFwVersion


/*************************************************************************
 Function:        DisplayTemperature

 Preconditions:   Assumes the Messagse IO has been initialized and that
                  the data matches the format described below.
                  
 Input:           packet  Pointer to the data packet containing the 
                          temperature data.

 Output:          None

 Returns:         TRUE if the response was valid, FALSE if not.

 Side Effects:    The message strings have been modified and the message
                  displayed

 Overview:        Displays the temperature data in the packet buffer.

 Notes:           Temperature is a 16 bit number representing degrees C.
                  Bits 0 & 1 are not valid.  Bit 2 indicates temperature
                  is valid.  The remaining 13 bits represent degrees 
                  Celsius, in 1/16th (0.0625) degree increments.  In a 
                  fixed point coding scheme, the Binary-point is between
                  bits 6 and 7
                  
                  This routine is only called once from "ManageDemoState",
                  so it is defined as a "static inline" routine to avoid 
                  function call overhead.
 *************************************************************************/

static inline BOOL DisplayTemperature ( DATA_PACKET *packet )
{
    UINT32 Temperature;
    BYTE   Digit;

    // Validate the response packet.
    if ( packet->CMD != RD_TEMP )
    {
        SendMessage( 2, "Bad Temp Packet " );
        DemoState = DEMO_STATE_ERROR;
        return FALSE;
    }

    // If the data is valid, display the temp in degrees C.
    if ( packet->_byte[1] & VALID_FLAG  )
    {
        // Calculate the fractional digit.
        Temperature = 500;
        if ( DataPacket._byte[1] & 0x40 ) Temperature += 5000;
        if ( DataPacket._byte[1] & 0x20 ) Temperature += 2500;
        if ( DataPacket._byte[1] & 0x10 ) Temperature += 1250;
        if ( DataPacket._byte[1] & 0x80 ) Temperature += 0625;
        Digit = Temperature / 1000;
        MsgData[1][4]  = '0' + Digit;

        // Formulate the non-fractional portion of the temperature
        Temperature   = DataPacket._byte[2];
        Temperature <<= 1;
        if (DataPacket._byte[1] & 0x80)
            Temperature |= 1;

        // Translate to decimal in ASCII
        Digit = Temperature % 10;
        Temperature   /= 10;
        MsgData[1][2]  = '0' + Digit;
        Digit = Temperature % 10;
        Temperature   /= 10;
        MsgData[1][1]  = '0' + Digit;

        // If we're out of range, blank out the data.
        if (Temperature)
        {
            MsgData[1][4]  = '-';
            MsgData[1][2]  = '-';
            MsgData[1][1]  = '-';
        }

        // Display temperature
        if (GetDataSwitchWasPressed())
        {
            SendMessage(2, &MsgData[1][0] );
        }
    }

    return TRUE;

} // DisplayTemperature


/*************************************************************************
 Function:        DisplayPot
 
 Preconditions:   Assumes the device FW Version response packet has been
                  received and the message IO has been initialized.
                  
 Input:           packet  Pointer to the data packet containing the 
                          potentiometer data.
 
 Output:          None
 
 Returns:         TRUE if the packet is valid, FALSE if not.
 
 Side Effects:    The message strings have been modified and the message
                  displayed.
 
 Overview:        Displays the potentiometer data in the packet buffer.
                  
 Note:            This routine is only called once from "ManageDemoState",
                  so it is defined as a "static inline" routine to avoid 
                  function call overhead.
 *************************************************************************/

static inline BOOL DisplayPot ( DATA_PACKET *packet )
{
    UINT32 POT_Val;
    BYTE   Digit;
    int    i;

    // If the data is valid...
    if ( packet->CMD == RD_POT  )
    {
        // Calculate actual POT value
        POT_Val   = packet->_byte[2];
        POT_Val <<= 8;
        POT_Val  |=  packet->_byte[1];
        POT_Val  *= POT_FULL_VALUE+10;
        POT_Val  /= 1024;

        // Translate to ASCII
        for (i=0; i < 5; i++)
        {
            Digit             = POT_Val % 10;
            POT_Val          /= 10;
            MsgData[1][11-i]  = '0' + Digit;
        }

        // Display POT data
        if (GetDataSwitchWasPressed())
        {
            SendMessage(2, &MsgData[1][0] );
        }
        return TRUE;
    }

    // Otherwise, report error.
    SendMessage( 2, "Invalid POT Resp" );
    DemoState = DEMO_STATE_ERROR;
    return FALSE;

} // DisplayPot


/******************************************************************************
 Function:        void BlinkStatus(void)
 
 PreCondition:    None
 
 Input:           None
 
 Output:          None
 
 Side Effects:    The LEDs on/off state has been changed.
 
 Overview:        BlinkStatus turns on and off LEDs corresponding to
                  the demo state.
  *****************************************************************************/

void BlinkStatus(void)
{
    if(DemoState <= DEMO_STATE_IDLE)
    {
        mLED_1_Off();
    }
    else if(DemoState >= DEMO_STATE_RUNNING)
    {
        if(DemoState < DEMO_STATE_ERROR)
        {
            if (MSTimer32GetTime() > NORMAL_BLINK_INTERVAL )
            {
                MSTimer32Clear();
                mLED_1_Toggle();
            }
        }
        else    // DEMO_STATE_ERROR
        {
            if (MSTimer32GetTime() > ERROR_BLINK_INTERVAL )
            {
                MSTimer32Clear();
                mLED_1_Toggle();
            }
        }
    }

} // BlinkStatus


/**************************
 * Main Application Logic *
 **************************/

/*************************************************************************
 Function:        ManageDemoState
 
 Preconditions:   The DemoState global variable must be initialized to
                  DEMO_STATE_IDLE (0).  (This occurs on reset.)
                  
 Input:           DemoState (global)
                  Actions selected based value of DemoState on function
                  entry.
 
                  DevAddr (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.
                  
 Note:            This routine is only called from "main", so it is 
                  defined as a "static inline" routine to avoid function
                  call overhead.
 *************************************************************************/
static inline void ManageDemoState ( void )
{
    BYTE RetVal;
    DEMO_STATE  PreviousState;

    BlinkStatus();

    // Watch for device timeouts
    if (MSTimerGetTime() > DEMO_TIMEOUT_LIMIT)
    {
        SendMessage( 2, "Dev Time-out Err" );
        DemoState = DEMO_STATE_ERROR;
    }

    //
    // Main Application State Machine
    //
    PreviousState = DemoState;
    switch (DemoState)
    {
    case DEMO_INITIALIZE:
        InitialMessage(MessageData);
        mLED_2_Off();
        mLED_3_Off();
        SwitchState = SW1|SW2|SW3;
        DemoState   = DEMO_STATE_IDLE;
        break;

    // ** Idle State:  Loops here until attach **
    case DEMO_STATE_IDLE:
        MSTimerClear(); // Avoid timeout message.
        if (DeviceAttached)
        {
            DemoState = DEMO_STATE_GET_DEV_VERSION;
        }
        break;

    // ** Sequence: Read Dev FW Version **
    case DEMO_STATE_GET_DEV_VERSION:
        // Send the Read Version command
        DataPacket.CMD = READ_VERSION;
        DataPacket.len = 2;
        if (!USBHostGenericTxIsBusy(DevAddr))
        {
            if ( (RetVal=USBHostGenericWrite(DevAddr, &DataPacket, 2)) == USB_SUCCESS )
            {
                DemoState = DEMO_STATE_WAITING_VER_REQ;
            }
            else
            {
                SendMessage( 2, "Dev Write Error " );
            }
        }
        break;

    case DEMO_STATE_WAITING_VER_REQ:
        if (!USBHostGenericTxIsBusy(DevAddr) )
            DemoState = DEMO_STATE_READ_DEV_VERSION;
        break;

    case DEMO_STATE_READ_DEV_VERSION:
        if (!USBHostGenericRxIsBusy(DevAddr))
        {
            if ( (RetVal=USBHostGenericRead(DevAddr, &DataPacket, 4)) == USB_SUCCESS )
            {
                DemoState = DEMO_STATE_WAITING_READ_VER;
            }
            else
            {
                SendMessage( 2, "Dev Read Error  " );
            }
        }
        break;

    case DEMO_STATE_WAITING_READ_VER:
        if (!USBHostGenericRxIsBusy(DevAddr))
            DemoState = DEMO_STATE_VERIFY_DEV_FW_VER;
        break;

    // ** Read Dev FW Version **
    case DEMO_STATE_VERIFY_DEV_FW_VER:
        // Validate and display the version number on the LCD
        if (ValidateAndDisplayDeviceFwVersion(&DataPacket))
            DemoState = DEMO_STATE_GET_TEMPERATURE;
        else
            DemoState = DEMO_STATE_ERROR;
        break;

    // ** Read Temperature Sensor Data **
    case DEMO_STATE_GET_TEMPERATURE:
        // Send the Read Temperature command
        DataPacket.CMD = RD_TEMP;
        DataPacket.len = 2;
        if (!USBHostGenericTxIsBusy(DevAddr))
        {
            if ( (RetVal=USBHostGenericWrite(DevAddr, &DataPacket, 2)) == USB_SUCCESS)
            {
                DemoState = DEMO_STATE_WAITING_GET_TEMP;
            }
            else
            {
                SendMessage( 2, "Dev Write Error " );
            }
        }
        break;

    case DEMO_STATE_WAITING_GET_TEMP:
        if (!USBHostGenericTxIsBusy(DevAddr) )
            DemoState = DEMO_STATE_READ_TEMPERATURE;
        break;

    case DEMO_STATE_READ_TEMPERATURE:
        if (!USBHostGenericRxIsBusy(DevAddr))
        {
            if ( (RetVal=USBHostGenericRead(DevAddr, &DataPacket, 3)) == USB_SUCCESS)
            {
                DemoState = DEMO_STATE_WAITING_READ_TEMP;
            }
            else
            {
                SendMessage( 2, "Dev Read Error  " );
            }
        }
        break;

    case DEMO_STATE_WAITING_READ_TEMP:
        if (!USBHostGenericRxIsBusy(DevAddr))
            DemoState = DEMO_STATE_DISPLAY_TEMPERATURE;
        break;

    case DEMO_STATE_DISPLAY_TEMPERATURE:
        // Display the temprature data on the LCD
        DisplayTemperature(&DataPacket);
        DemoState = DEMO_STATE_GET_POT;
        break;

    // ** Read POT Sensor Data **
    case DEMO_STATE_GET_POT:
        // Send the Read POT command
        DataPacket.CMD = RD_POT;
        DataPacket.len = 2;
        if (!USBHostGenericTxIsBusy(DevAddr))
        {
            if ( (RetVal=USBHostGenericWrite(DevAddr, &DataPacket, 2)) == USB_SUCCESS)
            {
                DemoState = DEMO_STATE_WAITING_GET_POT;
            }
            else
            {
                SendMessage( 2, "Dev Write Error " );
            }
        }
        break;

    case DEMO_STATE_WAITING_GET_POT:
        if (!USBHostGenericTxIsBusy(DevAddr) )
            DemoState = DEMO_STATE_READ_POT;
        break;

    case DEMO_STATE_READ_POT:
        if (!USBHostGenericRxIsBusy(DevAddr))
        {
            if ( (RetVal=USBHostGenericRead(DevAddr, &DataPacket, 3)) == USB_SUCCESS)
            {
                DemoState = DEMO_STATE_WAITING_READ_POT;
            }
            else
            {
                SendMessage( 2, "Dev Read Error  " );
            }
        }
        break;

    case DEMO_STATE_WAITING_READ_POT:
        if (!USBHostGenericRxIsBusy(DevAddr))
            DemoState = DEMO_STATE_DISPLAY_POT;
        break;

    case DEMO_STATE_DISPLAY_POT:
        // Display the POT data on the LCD
        DisplayPot(&DataPacket);
        DemoState = DEMO_STATE_SEND_SET_LED;
        break;

    /** Sequence:  Update LEDs **/
    case DEMO_STATE_SEND_SET_LED:
        // Send the set-LED command
        DataPacket.CMD = UPDATE_LED;
        DataPacket.len = 3;
        if (LED3SwitchWasPressed())
        {
            DataPacket.led_num    = 3;      // LED 3 on original PIC18 FS USB board
            DataPacket.led_status = LEDState.bit.b3 ^ 1;
        }
        else if (
                 LED4SwitchWasPressed())
        {
            DataPacket.led_num    = 4;      // LED 4 on original PIC18 FS USB board
            DataPacket.led_status = LEDState.bit.b4 ^ 1;
        }
        else
        {
            DemoState = DEMO_STATE_GET_TEMPERATURE;
            break;
        }
        if (!USBHostGenericTxIsBusy(DevAddr))
        {
            if ( (RetVal=USBHostGenericWrite(DevAddr, &DataPacket, 3)) == USB_SUCCESS)
            {
                DemoState = DEMO_STATE_WAITING_SET_LED;
            }
            else
            {
                SendMessage( 2, "Dev Write Error " );
            }
        }
        break;

    case DEMO_STATE_WAITING_SET_LED:
        if (!USBHostGenericTxIsBusy(DevAddr) )
            DemoState = DEMO_STATE_READ_SET_LED_RESP;
        break;

    case DEMO_STATE_READ_SET_LED_RESP:
        if (!USBHostGenericRxIsBusy(DevAddr))
        {
            DataPacket.CMD = CMD_INVALID;
            if ( (RetVal=USBHostGenericRead(DevAddr, &DataPacket, 1)) == USB_SUCCESS)
            {
                DemoState = DEMO_STATE_WAITING_LED_RESP;
            }
            else
            {
                SendMessage( 2, "Dev Read Error  " );
            }
        }
        break;

    case DEMO_STATE_WAITING_LED_RESP:
        if (!USBHostGenericRxIsBusy(DevAddr))
            DemoState = DEMO_STATE_UPDATE_LED_STATE;
        break;

    case DEMO_STATE_UPDATE_LED_STATE:
        if (DataPacket.CMD == UPDATE_LED)   // CMD updated by read from device
        {
            if (DataPacket.led_num == 3)    // led_num left-over from set-LED command send
            {
                LEDState.bit.b3 ^= 1;
                mLED_2_Toggle();
            }
            else if (DataPacket.led_num == 4)
            {
                LEDState.bit.b4 ^= 1;
                mLED_3_Toggle();
            }
        }
        DemoState = DEMO_STATE_GET_TEMPERATURE;
        break;

        // ** Error state:  Hold here until detached **
    case DEMO_STATE_ERROR:
        MSTimerClear();                     // Avoid timeout message.
        if (!DeviceAttached)
            DemoState = DEMO_INITIALIZE;
        break;

    default:
        // If we should somehow happen to hit an invalid state, re-initialize
        DemoState = DEMO_INITIALIZE;
        break;
    }

    // Clear timer if we've had a state change
    if (DemoState != PreviousState)
        MSTimerClear();

} // ManageDemoState


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

/*************************************************************************
 Function:        DemoAttachEventHandler
 
 Preconditions:   A EVENT_GENERIC_ATTACH event has occured. 
                  
 Input:           pDevID      Pointer to the GENERIC_DEVICE_ID data
 
                  size        Should be sizeof(GENERIC_DEVICE_ID)
 
 Output:          DevAddr (global)
                  Updates device address when an attach occurs.
 
                  DemoState (global)
                  Updates the demo state as appropriate.
 
 Returns:         TRUE if the event was handled, FALSE if not
 
 Side Effects:    App State has been changed if data was valid.
 
 Overview:        This routine validates the attach-event data and
                  changes the state of the application appropriately.
 *************************************************************************/

static inline BOOL DemoAttachEventHandler ( GENERIC_DEVICE_ID *pDevID, DWORD size )
{
    // If data is valid, store the device address and change state
    if ( pDevID != NULL  &&  size == sizeof(GENERIC_DEVICE_ID) )
    {
        if ( pDevID->vid == 0x04D8  &&  pDevID->pid == 0x000C )
        {
            DevAddr   = pDevID->deviceAddress;
            DemoState = DEMO_STATE_GET_DEV_VERSION;
            return TRUE;
        }
    }

    // Otherwise, we have an error.
    SendMessage( 2, "Dev Attach Error" );
    DemoState = DEMO_STATE_ERROR;

    return FALSE;

} // DemoAttachEventHandler


/*************************************************************************
 * Function:        usb_custom_host_demo_EventHandler
 *
 * 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:          DevAddr (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_custom_host_demo_EventHandler ( BYTE address, USB_EVENT event, void *data, DWORD size )
{
    // Handle specific events.
    switch (event)
    {
    case EVENT_ATTACH:
        DeviceAttached  = TRUE;
        break;

    case EVENT_GENERIC_ATTACH:
        return DemoAttachEventHandler((GENERIC_DEVICE_ID *)data, size);

    case EVENT_GENERIC_DETACH:
        DevAddr         = 0;
        DemoState       = DEMO_INITIALIZE;
        DeviceAttached  = FALSE;
        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:
        // The data pointer points to a byte that represents the amount of power
        // requested in mA, divided by two.  If the device wants too much power,
        // we reject it.
        if (((USB_VBUS_POWER_EVENT_DATA*)data)->current <= (MAX_ALLOWED_CURRENT / 2))
        {
            return TRUE;
        }
        else
        {
            SendMessage(2,"Overcurrent Err!");
        }
        break;

    case EVENT_VBUS_RELEASE_POWER:
        // Turn off Vbus power.
        // Cannot turn off VBus power with the Explorer 16 through software.
        return TRUE;

    case EVENT_UNSUPPORTED_DEVICE:
        SendMessage( 2, "No Dev Support  " );
        DemoState = DEMO_STATE_ERROR;
        break;

    case EVENT_HUB_ATTACH:
        SendMessage( 2, "No Hub Support! " );
        DemoState = DEMO_STATE_ERROR;
        break;

    case EVENT_BUS_ERROR:
    case EVENT_CANNOT_ENUMERATE:
    case EVENT_OUT_OF_MEMORY:
    case EVENT_UNSPECIFIED_ERROR:
        SendMessage( 2, "Bus Error!      " );
        DemoState = DEMO_STATE_ERROR;
        return TRUE;

    default:
        break;
    }

    // Let the host layer know we ignore all other events.
    return FALSE;

} // usb_custom_host_demo_EventHandler


/********************
 * Main Entry Point *
 ********************/

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

int main ( void )
{
    // Initialize the processor
    if ( InitializeSystem() != TRUE)
    {
        while (1);
    }

    // Initialize the USB
    if ( USBInitialize(0) != TRUE )
    {
        // We're dead and we can at least tell the user why
        SendMessage( 2, "USB Init Error! " );
        DemoState = DEMO_STATE_ERROR;
        while (1)
        {
            BlinkStatus();
        }
    }

    // Main Processing Loop
    while (1)
    {
        // Maintain USB Host State
        USBTasks();

        // Maintain Demo Application State
        ManageDemoState();
    }

    return 0;

} // main


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

