//------------------------------------------------------------------------------
// Copyright (c) 2013 by Silicon Laboratories.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Silicon Laboratories End User
// License Agreement which accompanies this distribution, and is available at
// http://developer.silabs.com/legal/version/v10/License_Agreement_v10.htm
// Original content and implementation provided by Silicon Laboratories.
//------------------------------------------------------------------------------

#include "global.h"

#if (MCU_FAMILY == SiM3U1 && COMM_PROTOCOL == USB)

#include "USB0_ControlRequests.h"
#include "USB0_ISR.h"
#include <string.h>
#include <SI32_USB_A_Type.h>
#include <SI32_USBEP_A_Type.h>
#include <si32_device.h>

//-----------------------------------------------------------------------------
// Static Global Variables
//-----------------------------------------------------------------------------

// Store data received from the host in an OUT control transfer (ie DFU_DNLOAD)
// -or-
// Data to send to the host in an IN control transfer (ie DFU_UPLOAD)
static uint8_t* Buffer;
static uint16_t BufferSize;
static uint16_t Transferred;
static bool TransferComplete = true;

//-----------------------------------------------------------------------------
// Static Function Prototypes
//-----------------------------------------------------------------------------

static void Finish_Setup (void);

//-----------------------------------------------------------------------------
// Global Functions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Handle_Class_Request
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Handle class-specific control requests
// - Decode Incoming Setup requests
// - Wait until Class_Out_Start() is called, providing a RX buffer to receive
//   data
//
//-----------------------------------------------------------------------------
void Handle_Class_Request (void)
{
  // Make sure device is configured
  if (USB0_State != DEV_CONFIGURED)
  {
    Force_Stall();
    return;
  }

  // Handle reception of a control setup packet on EP0
  // - If no rx_buff is available (passed through Class_Out_Start)
  //   then go to a wait state
  // - Otherwise copy the setup packet and receive data phase (if applicable)
  //   to the rx_buff
  
  // Buffer is not ready yet
  if (TransferComplete)
  {
    EP_Status = EP_WAIT_RX;
  }
  else
  {
    Finish_Setup();
  }
}

// Finish handling the setup packet and move on to the IN/OUT data phase
// of the control request
void Finish_Setup(void)
{
  // Copy selected bytes from the Setup packet
  memcpy(Buffer, (((uint8_t*)&Setup) + DFU_SETUP_PACKET_COPY_OFFSET), DFU_SETUP_PACKET_COPY_SIZE);

  // Set Serviced Out packet ready
  SI32_USB_A_clear_out_packet_ready_ep0(SI32_USB_0);

  // Handle out class requests
  if ((Setup.bmRequestType & DIR_BITMASK) == DIR_OUT)
  {
    // No data phase, end the control transfer
    if (Setup.wLength == 0)
    {
      // Set Serviced Out packet ready and
      // data end to indicate transaction
      // is over
      SI32_USB_A_set_data_end_ep0(SI32_USB_0);

      // Return to IDLE state
      EP_Status = EP_IDLE;

      // Control transfer complete
      USB0_Transfer_Complete(DFU_SETUP_PACKET_COPY_SIZE);
    }
    else
    {
      // Set the DataPtr to store data in the ClassOutBuffer (after the Setup packet data)
      TransferDataPtr = Buffer + DFU_SETUP_PACKET_COPY_SIZE;
      TransferDataSize = BufferSize - DFU_SETUP_PACKET_COPY_SIZE;
      TransferDataTransferred = 0;

      // Setup the RX phase of the control transfer
      EP_Status = EP_RX;
    }
  }
  // Handle in class requests
  else
  {
    // Wait until TX data is ready from the App
    EP_Status = EP_WAIT_TX;

    // Control transfer setup complete
    USB0_Transfer_Complete(DFU_SETUP_PACKET_COPY_SIZE);
  }
}

// Pass in the RX buffer used to store the setup packet and
// OUT data phase
// -or-
// Pass in the TX buffer used to send the IN data phase
void USB0_Transfer_Start(uint8_t* buffer, uint16_t size, bool rw)
{
  __disable_irq();

  if (rw)
  {
    Buffer = buffer;
    BufferSize = size;
  }
  else
  {
    TransferDataPtr = buffer;
    TransferDataSize = size;
    TransferDataTransferred = 0;      // Reset Data Sent counter
  }
  
  TransferComplete = false;

  if (rw)
  {
    // Already received setup packet before this function was called
    if (EP_Status == EP_WAIT_RX)
    {
      Finish_Setup();
    }
  }
  else
  {
    // Set Serviced Out packet ready
    SI32_USB_A_clear_out_packet_ready_ep0(SI32_USB_0);

    // Setup the TX phase of the control transfer
    EP_Status = EP_TX;
  }

  __enable_irq();
}

// Called by the USB ISR when the OUT control request has finished, meaning
// the Setup packet and RxBuffer are populated
// -or-
// Called by the USB ISR when the IN control request has finished, meaning
// the TxBuffer has been sent
void USB0_Transfer_Complete (uint16_t bytesTransferred)
{
  Transferred = bytesTransferred;
  TransferComplete = true;
}

// Returns true if a control OUT transfer has completed
// -or-
// Returns true if a control IN transfer has completed
bool USB0_Is_Transfer_Complete (uint16_t* bytesTransferred)
{
  bool complete;

  __disable_irq();

  complete = TransferComplete;
  if (complete)
  {
    *bytesTransferred = Transferred;
  }
  __enable_irq();

  return complete;
}

#endif
