/* --COPYRIGHT--,BSD
 * Copyright (c) 2013, Texas Instruments Incorporated                          
 * 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 Texas Instruments Incorporated 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.                          
 * --/COPYRIGHT--*/

/*-----------------------------------------------------------------------------+
| Include files                                                                |
|-----------------------------------------------------------------------------*/
#include <USB_API/USB_Common/device.h>
                // Basic Type declarations
#include <USB_API/USB_Common/defMSP430USB.h>
#include "descriptors.h"
#include <USB_API/USB_Common/usb.h>           //USB-specific Data Structures
#include <USB_API/USB_Common/UsbIsr.h>
#include <string.h>

#include <USB_API/USB_CDC_API/UsbCdc.h>
/*----------------------------------------------------------------------------+
| External Variables                                                          |
+----------------------------------------------------------------------------*/
extern uint8_t  bFunctionSuspended;
extern __no_init tEDB0 __data16 tEndPoint0DescriptorBlock;
extern __no_init tEDB __data16 tInputEndPointDescriptorBlock[];
extern __no_init tEDB __data16 tOutputEndPointDescriptorBlock[];
extern volatile uint8_t bHostAsksUSBData;
extern volatile uint8_t bTransferInProgress;
extern volatile uint8_t bSecondUartTxDataCounter[];
extern volatile uint8_t* pbSecondUartTxData;
extern uint8_t bStatusAction;
extern uint16_t wUsbEventMask;
int16_t CdcToHostFromBuffer(uint8_t);
int16_t CdcToBufferFromHost(uint8_t);
int16_t CdcIsReceiveInProgress(uint8_t);
int16_t HidToHostFromBuffer(uint8_t);
int16_t HidToBufferFromHost(uint8_t);
int16_t HidIsReceiveInProgress(uint8_t);
extern uint16_t wUsbHidEventMask;
int16_t PHDCToHostFromBuffer(uint8_t);
int16_t PHDCToBufferFromHost(uint8_t);
int16_t PHDCIsReceiveInProgress(uint8_t);
uint16_t USB_determineFreq(void);
/*----------------------------------------------------------------------------+
| General Subroutines                                                         |
+----------------------------------------------------------------------------*/
#if defined(__TI_COMPILER_VERSION__) || (__IAR_SYSTEMS_ICC__) 
#pragma vector=USB_UBM_VECTOR
__interrupt void iUsbInterruptHandler(void)
#elif defined(__GNUC__) && (__MSP430__) 
void __attribute__ ((interrupt(USB_UBM_VECTOR))) iUsbInterruptHandler(void)
#endif 
{
    uint8_t bWakeUp = FALSE;
    //Check if the setup interrupt is pending.
    //We need to check it before other interrupts,
    //to work around that the Setup Int has lower priority then Input Endpoint 0
    if (USBIFG & SETUPIFG)
    {
        bWakeUp = SetupPacketInterruptHandler();  
#ifdef USB10_WORKAROUND
        tEndPoint0DescriptorBlock.bIEPCNFG &= ~EPCNF_UBME; // Clear ME to gate off SETUPIFG clear event
        tEndPoint0DescriptorBlock.bOEPCNFG &= ~EPCNF_UBME; // Clear ME to gate off SETUPIFG clear event
#endif
        USBIFG &= ~SETUPIFG;    // clear the interrupt bit
#ifdef USB10_WORKAROUND
        tEndPoint0DescriptorBlock.bIEPCNFG |= EPCNF_UBME; // Set ME to continue with normal operation
        tEndPoint0DescriptorBlock.bOEPCNFG |= EPCNF_UBME; // Set ME to continue with normal operation
#endif
    }   
    switch (__even_in_range(USBVECINT & 0x3f, USBVECINT_OUTPUT_ENDPOINT7))
    {
    case USBVECINT_NONE:
      break;
    case USBVECINT_PWR_DROP:
      __no_operation();
      break;
    case USBVECINT_PLL_LOCK:
      break;
    case USBVECINT_PLL_SIGNAL:
      break;
    case USBVECINT_PLL_RANGE:
      if (wUsbEventMask & kUSB_clockFaultEvent)
      {
          bWakeUp = USB_handleClockEvent();
      }
      break;
    case USBVECINT_PWR_VBUSOn:
      PWRVBUSonHandler();
      if (wUsbEventMask & kUSB_VbusOnEvent)
      {
          bWakeUp = USB_handleVbusOnEvent();
      }
      break;
    case USBVECINT_PWR_VBUSOff:
      PWRVBUSoffHandler();
      if (wUsbEventMask & kUSB_VbusOffEvent)
      {
          bWakeUp = USB_handleVbusOffEvent();
      }
      break;
    case USBVECINT_USB_TIMESTAMP:
      break;
    case USBVECINT_INPUT_ENDPOINT0:
      IEP0InterruptHandler();
      break;
    case USBVECINT_OUTPUT_ENDPOINT0:
      OEP0InterruptHandler();
      break;
    case USBVECINT_RSTR:
      USB_reset();
      if (wUsbEventMask & kUSB_UsbResetEvent)
      {
          bWakeUp = USB_handleResetEvent();
      }
      break;
    case USBVECINT_SUSR:
      USB_suspend();
      if (wUsbEventMask & kUSB_UsbSuspendEvent)
      {
          bWakeUp = USB_handleSuspendEvent();
      }
      break;
    case USBVECINT_RESR:
      USB_resume();
      if (wUsbEventMask & kUSB_UsbResumeEvent)
      {
          bWakeUp = USB_handleResumeEvent();
      }
      //-- after resume we will wake up! Independ what event handler says.
      bWakeUp = TRUE;
      break;
    case USBVECINT_SETUP_PACKET_RECEIVED:
      // NAK both IEP and OEP enpoints
      tEndPoint0DescriptorBlock.bIEPBCNT = EPBCNT_NAK;
      tEndPoint0DescriptorBlock.bOEPBCNT = EPBCNT_NAK;
      SetupPacketInterruptHandler();
      break;
    case USBVECINT_STPOW_PACKET_RECEIVED:
      break;
    case USBVECINT_INPUT_ENDPOINT1:
      break;
    case USBVECINT_INPUT_ENDPOINT2:
      //send saved bytes from buffer...
      bWakeUp = CdcToHostFromBuffer(CDC0_INTFNUM);
            break;
    case USBVECINT_INPUT_ENDPOINT3:
      break;
    case USBVECINT_INPUT_ENDPOINT4:
      break;
    case USBVECINT_INPUT_ENDPOINT5:
      break;
    case USBVECINT_INPUT_ENDPOINT6:
      break;
    case USBVECINT_INPUT_ENDPOINT7:
      break;
    case USBVECINT_OUTPUT_ENDPOINT1:
      break;
    case USBVECINT_OUTPUT_ENDPOINT2:
      //call callback function if no receive operation is underway
      if (!CdcIsReceiveInProgress(CDC0_INTFNUM) && USBCDC_bytesInUSBBuffer(CDC0_INTFNUM))
      {
          if (wUsbEventMask & kUSB_dataReceivedEvent)
          {
              bWakeUp = USBCDC_handleDataReceived(CDC0_INTFNUM);
          }
      }
      else
      {
          //complete receive opereation - copy data to user buffer
          bWakeUp = CdcToBufferFromHost(CDC0_INTFNUM);
      }
            break;
    case USBVECINT_OUTPUT_ENDPOINT3:
      break;
    case USBVECINT_OUTPUT_ENDPOINT4:
      break;
    case USBVECINT_OUTPUT_ENDPOINT5:
      break;
    case USBVECINT_OUTPUT_ENDPOINT6:
      break;
    case USBVECINT_OUTPUT_ENDPOINT7:
      break;
    default:
      break;
    }
    if (bWakeUp)
    {
         __bic_SR_register_on_exit(LPM3_bits);   // Exit LPM0-3
         __no_operation();                       // Required for debugger
    }
}

/*----------------------------------------------------------------------------+
| Interrupt Sub-routines                                                      |
+----------------------------------------------------------------------------*/
uint8_t SetupPacketInterruptHandler(void)
{
    uint8_t bTemp;
    uint8_t bWakeUp = FALSE;
    USBCTL |= FRSTE;      // Function Reset Connection Enable - set enable after first setup packet was received
    usbProcessNewSetupPacket:
    // copy the MSB of bmRequestType to DIR bit of USBCTL
    if((tSetupPacket.bmRequestType & USB_REQ_TYPE_INPUT) == USB_REQ_TYPE_INPUT)
    {
        USBCTL |= DIR;
    }
    else
    {
        USBCTL &= ~DIR;
    }
    bStatusAction = STATUS_ACTION_NOTHING;
    // clear out return data buffer
    for(bTemp=0; bTemp<USB_RETURN_DATA_LENGTH; bTemp++)
    {
        abUsbRequestReturnData[bTemp] = 0x00;
    }
    // decode and process the request
    bWakeUp = usbDecodeAndProcessUsbRequest();
    // check if there is another setup packet pending
    // if it is, abandon current one by NAKing both data endpoint 0
    if((USBIFG & STPOWIFG) != 0x00)
    {
        USBIFG &= ~(STPOWIFG | SETUPIFG);
        goto usbProcessNewSetupPacket;
    }
    return bWakeUp;
}

//----------------------------------------------------------------------------
void PWRVBUSoffHandler(void)
{
   uint16_t MCLKFreq = USB_determineFreq();
   uint16_t DelayConstant_250us = ((MCLKFreq >> 6) + (MCLKFreq >> 7) + (MCLKFreq >> 9));
   volatile uint16_t i, j;

    //wait 1 ms till enable USB 
    for(j = 0; j < 4; j++)
    {
        for (i = 0; i < (DelayConstant_250us); i++){
            _NOP();
        }
   }
    if (!(USBPWRCTL & USBBGVBV))
    {
        USBKEYPID   =    0x9628;        // set KEY and PID to 0x9628 -> access to configuration registers enabled
        bEnumerationStatus = 0x00;      // device is not enumerated
        bFunctionSuspended = FALSE;     // device is not suspended
        USBCNF     =    0;              // disable USB module
        USBPLLCTL  &=  ~UPLLEN;         // disable PLL
        USBPWRCTL &= ~(VBOFFIE + VBOFFIFG + SLDOEN);          // disable interrupt VBUSoff
        USBKEYPID   =    0x9600;        // access to configuration registers disabled
    }
}

//----------------------------------------------------------------------------
void PWRVBUSonHandler(void)
{
   uint16_t MCLKFreq = USB_determineFreq();
   uint16_t DelayConstant_250us = ((MCLKFreq >> 6) + (MCLKFreq >> 7) + (MCLKFreq >> 9));
   volatile uint16_t i, j;

    //wait 1 ms till enable USB 
    for(j = 0; j < 4; j++)
    {
        for (i = 0; i < (DelayConstant_250us); i++){
            _NOP();
        }
   }
    if (USBPWRCTL & USBBGVBV)                //Checking for USB Bandgap and VBUS valid before modifying USBPWRCTL
    {
        USBKEYPID =  0x9628;                // set KEY and PID to 0x9628 -> access to configuration registers enabled
        USBPWRCTL |= VBOFFIE;               // enable interrupt VBUSoff
        USBPWRCTL &= ~ (VBONIFG + VBOFFIFG);             // clean int flag (bouncing)
        USBKEYPID =  0x9600;                // access to configuration registers disabled
    }
}

//----------------------------------------------------------------------------
void IEP0InterruptHandler(void)
{
    USBCTL |= FRSTE;                              // Function Reset Connection Enable
    tEndPoint0DescriptorBlock.bOEPBCNT = 0x00;     
    if(bStatusAction == STATUS_ACTION_DATA_IN)
    {
        usbSendNextPacketOnIEP0();
    }
    else
    {
        tEndPoint0DescriptorBlock.bIEPCNFG |= EPCNF_STALL; // no more data
    }
}

//----------------------------------------------------------------------------
uint8_t OEP0InterruptHandler(void)
{
    uint8_t bWakeUp = FALSE;
    USBCTL |= FRSTE;                              // Function Reset Connection Enable
    tEndPoint0DescriptorBlock.bIEPBCNT = 0x00;    
    if(bStatusAction == STATUS_ACTION_DATA_OUT)
    {
        usbReceiveNextPacketOnOEP0();
        if(bStatusAction == STATUS_ACTION_NOTHING)
        {
#           ifdef _CDC_
                if(tSetupPacket.bRequest == USB_CDC_SET_LINE_CODING)
                {
                    bWakeUp = Handler_SetLineCoding();
                }
#          endif
#ifdef _HID_
                if (tSetupPacket.bRequest == USB_REQ_SET_REPORT) {
                    bWakeUp = USBHID_handleEP0SetReportDataAvailable(tSetupPacket.wIndex);
                }
#endif
          }
    }
    else
    {
        tEndPoint0DescriptorBlock.bOEPCNFG |= EPCNF_STALL; // no more data
    }
    return (bWakeUp);
}



/*----------------------------------------------------------------------------+
| End of source file                                                          |
+----------------------------------------------------------------------------*/
/*------------------------ Nothing Below This Line --------------------------*/
