//-----------------------------------------------------------------------------
// F320_USB_Common.c
//-----------------------------------------------------------------------------
// Copyright 2006 Silicon Laboratories, Inc.
// http://www.silabs.com
//
// Program Description:
//
// This is the module that handles common USB functions, including primary USB
// interrupt service routine, an endpoint zero handler, and fifo read and write
// routines.
//
//
// FID:            32X000035
// Target:         C8051F320
// Tool chain:     KEIL C51 7.0.0.1
//                 Silicon Laboratories IDE version 2.3
// Command Line:   See Readme.txt
// Project Name:   F320_FM_Radio
//
// Release 1.1
//    -No changes made to this module (DM)
//    -16 JAN 2006
//
// Release 1.0
//    -Initial Revision (DM)
//    -05 APR 2002
//

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------

#include "c8051f320.h"                 // SFR declarations
#include "F320_FM_RadioMain.h"         // Main project header
#include "F320_Si470x_Interface.h"     // Si470x interface header
#include "F320_USB_Register.h"         // USB core register header
#include "F320_USB_Common.h"           // USB protocol header
#include "F320_USB_Descriptor.h"       // USB descriptor definitions
#include "F320_LED_Control.h"          // LED control header

//-----------------------------------------------------------------------------
// Variable Declaration
//-----------------------------------------------------------------------------

idata setup_buffer Setup;              // Buffer for device request
idata BYTE USB_State;                  // Holds the USB State
idata unsigned int DataSize;           // Size of data to return
idata unsigned int DataSent;           // Amount of data sent
idata BYTE* DataPtr;                   // Pointer to data
// Endpoint status array
idata BYTE Ep_Status[4] = { EP_IDLE, EP_IDLE, EP_IDLE, EP_IDLE };

data WORD WriteBlock[6];               // Si470x register storage
data BYTE WriteState = 0;              // Used for In1 state machine

//-----------------------------------------------------------------------------
// Usb_ISR
//
// Return Value : None
// Parameters   : None
//
// Called after any USB type interrupt, this handler determines which type of 
// interrupt occurred, and calls the specific routine to handle it.
//
//-----------------------------------------------------------------------------
void Usb_ISR (void) interrupt 8 using USB_REGISTER_BANK
{
   data BYTE bCommon, bIn, bOut, ControlReg;
   POLL_READ_BYTE (CMINT, bCommon);    // Read all interrupt registers
   POLL_READ_BYTE (IN1INT, bIn);       // this read also clears the register
   POLL_READ_BYTE (OUT1INT, bOut);

   if (bCommon)
   {
      if (bCommon & rbSOF)
      {
         POLL_WRITE_BYTE (INDEX, 3);
         POLL_READ_BYTE (EINCSR1, ControlReg);

         if (ControlReg & rbInUNDRUN)
         {
            // Clear under run bit
            POLL_WRITE_BYTE (EINCSR1, 0x00);
            Turn_On_Stream (); 
         }
         else 
         {
            LED_StateMachine ();
            In1_StateMachine ();
         }
      }

      if (bCommon & rbRSTINT) Usb_Reset ();
      if (bCommon & rbSUSINT) Usb_Suspend ();
   }

   if (bIn & rbEP0)                  
   {
      if (TR2)
      {
         bIn &= ~rbIN3;                 // Turn off transmit complete
         Turn_Off_Stream ();
      }
                                     
      // Handle Setup packet received or transmitted
      Handle_Setup ();
   }
   
   if (bIn & rbIN3) Handle_In3 ();     // Handle In Packet sent

   if (bOut & rbOUT2) 
   {
      if (TR2)
      {
         bIn &= ~rbIN3;                // Turn off transmit complete
         Turn_Off_Stream ();
      }
   }
}

//-----------------------------------------------------------------------------
// Usb_Reset
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Clears USB inhibit bit and sets endpoint states to default.
//
//-----------------------------------------------------------------------------
void Usb_Reset (void) using USB_REGISTER_BANK
{
   USB_State = DEV_DEFAULT;            // Set device state to default

   // Clear USB inhibit bit, enable suspend detection, turn off ISO update
   POLL_WRITE_BYTE (POWER, 0x01);             

   Ep_Status[0] = EP_IDLE;             // Set default Endpoint Status
   Ep_Status[1] = EP_HALT;
   Ep_Status[2] = EP_HALT;
   Ep_Status[3] = EP_HALT;
}

//-----------------------------------------------------------------------------
// Handle_Setup
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Decodes incoming setup requests, loads data packets on fifo in transmit
// mode, and retrieves data from fifo in receive mode.
//
//-----------------------------------------------------------------------------
void Handle_Setup (void) using USB_REGISTER_BANK
{
   data BYTE ControlReg;               // Storage for EP control register

   POLL_WRITE_BYTE (INDEX, 0);         // Set Index to Endpoint Zero
   POLL_READ_BYTE (E0CSR, ControlReg); // Read control register

   // Handle Status Phase of Set Address command
   if (Ep_Status[0] == EP_ADDRESS)
   {
      POLL_WRITE_BYTE (FADDR, Setup.wValue.c[LSB]);
      Ep_Status[0] = EP_IDLE;
   }

   // If last packet was a sent stall, reset STSTL bit and return EP0 to idle
   if (ControlReg & rbSTSTL)
   {
      POLL_WRITE_BYTE (E0CSR, 0);
      Ep_Status[0] = EP_IDLE;
      return;
   }

   // If last setup transaction was ended prematurely set Serviced Setup End
   // bit and return EP0 to idle state
   if (ControlReg & rbSUEND)
   {
      POLL_WRITE_BYTE (E0CSR, rbSSUEND);
      Ep_Status[0] = EP_IDLE;
   }

   if (Ep_Status[0] == EP_IDLE)        // If Endpoint 0 is in idle mode
   {
      // Make sure that EP 0 has an Out Packet ready from host although if EP0 
      // is idle, this should always be the case
      if (ControlReg & rbOPRDY)
      {
         // Get Setup Packet off of Fifo, it is currently Big-Endian
         FIFO_Read (FIFO_EP0, 8, (BYTE *)&Setup);

         Setup.wValue.i = Setup.wValue.c[MSB] + 256*Setup.wValue.c[LSB];
         Setup.wIndex.i = Setup.wIndex.c[MSB] + 256*Setup.wIndex.c[LSB];
         Setup.wLength.i = Setup.wLength.c[MSB] + 256*Setup.wLength.c[LSB];
                                        
         if ((Setup.bmRequestType & TYPE_MASK) == CLASS_TYPE)
		   {
            // Call correct subroutine to handle each kind of
            // class-specific request
            switch (Setup.bRequest)               
            {
               case GET_CUR:           Get_Cur ();           break; 
               case SET_IDLE:          Set_Idle ();          break;

               // The Get Report HID command type has an overlap 
               // with Set Cur in audio driver, the request type
               // can be used to tell the difference.
               case GET_REPORT:
                  if (Setup.bmRequestType & 0x80) 
                  {
                     Get_Report ();
                  }
                  else Set_Cur ();
                  break;
               case SET_REPORT:        Set_Report ();        break;
                 
               // Send stall to host if invalid request          
               default:                Force_Stall ();       break;   
                
            }
         }
   		else
   		{
            // Call correct subroutine to handle each kind of 
            // standard request
            switch (Setup.bRequest)        
            {                              
               case GET_STATUS:        Get_Status ();        break;             
               case CLEAR_FEATURE:     Clear_Feature ();     break;
               case SET_FEATURE:       Set_Feature ();       break;
               case SET_ADDRESS:       Set_Address ();       break;
               case GET_DESCRIPTOR:    Get_Descriptor ();    break;
               case GET_CONFIGURATION: Get_Configuration (); break;
               case SET_CONFIGURATION: Set_Configuration (); break;
               case GET_INTERFACE:     Get_Interface ();     break;
               case SET_INTERFACE:     Set_Interface ();     break;

               // Send stall to host if invalid request
               default:                Force_Stall ();       break;
            }
         }
      }
   } 

   if (Ep_Status[0] == EP_TX)          // See if endpoint should transmit
   {
      if (!(ControlReg & rbINPRDY))    // Don't overwrite last packet
      {
         POLL_READ_BYTE (E0CSR, ControlReg);
		 
         // Check to see if Setup End or Out Packet received, if so do not put 
         // any new data on FIFO
         if ((!(ControlReg & rbSUEND)) || (!(ControlReg & rbOPRDY)))
         {
            // Add In Packet ready flag to E0CSR bitmask
            ControlReg = rbINPRDY;              
            if (DataSize >= EP0_PACKET_SIZE)
            {
               // Break Data into multiple packets if larger than Max Packet
               FIFO_Write (FIFO_EP0, EP0_PACKET_SIZE, (BYTE *)DataPtr);
               DataPtr  += EP0_PACKET_SIZE;
               DataSize -= EP0_PACKET_SIZE;
               DataSent += EP0_PACKET_SIZE;
            }
            else                             
            {
               // If data is less than Max Packet size or zero
               FIFO_Write (FIFO_EP0, DataSize, (BYTE *)DataPtr);
               ControlReg |= rbDATAEND;
               Ep_Status[0] = EP_IDLE;
            }
            if (DataSent == Setup.wLength.i)
            {      
               // This case exists when the host requests an even multiple of
               // your endpoint zero max packet size, and you need to exit
               // transmit mode without sending a zero length packet
               ControlReg |= rbDATAEND;
               Ep_Status[0] = EP_IDLE;
            }
            POLL_WRITE_BYTE (E0CSR, ControlReg);
         }
      }
   }

   if (Ep_Status[0] == EP_RX)          // See if endpoint should receive
   {
      POLL_READ_BYTE(E0CSR, ControlReg);
      if (ControlReg & rbOPRDY)        // Verify packet was received
      {
         ControlReg = rbSOPRDY;
   		if (DataSize >= EP0_PACKET_SIZE)
   		{
            FIFO_Read (FIFO_EP0, EP0_PACKET_SIZE, (BYTE *)DataPtr);
            DataPtr  += EP0_PACKET_SIZE;
            DataSize -= EP0_PACKET_SIZE;
            DataSent += EP0_PACKET_SIZE;
   		}
   		else
   		{
            FIFO_Read (FIFO_EP0, DataSize, (BYTE *)DataPtr);
   			ControlReg |= rbDATAEND;
   			Ep_Status[0] = EP_IDLE;
   		}
   		if (DataSent == Setup.wLength.i)
   		{
   			ControlReg |= rbDATAEND;
   			Ep_Status[0] = EP_IDLE;
   		}
         if ((Ep_Status[0] == EP_IDLE) && (Setup.bRequest == SET_REPORT))
         {
            Handle_Set_Report ();
         }
         if (Ep_Status[0] != EP_STALL) POLL_WRITE_BYTE (E0CSR, ControlReg);
      }
   }	
}

//-----------------------------------------------------------------------------
// Usb_Suspend
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Power down all hardware and halt oscillator when suspend signalling present.
//
//-----------------------------------------------------------------------------
void Usb_Suspend (void) using USB_REGISTER_BANK
{
                                       //	Power Down Stuff
   P2MDOUT = 0x00;                     // Port 2 pins set open-drain
   P3MDOUT = 0x00;                     // Port 3 pins set open-drain

   USB0XCN &= ~0x43;                   // Suspend USB Transceiver
   CLKSEL = USB_INT_OSC_DIV_2;         // USB Clock now uses int osc / 2
   CLKMUL = 0x00;                      // Disable Clock Multiplier
   REG0CN |= 0x10;                     // Voltage regulator in low power mode

   OSCICN |= 0x20;                     // Force internal oscillator suspend
		
   //	Power Up Stuff
   REG0CN &= ~0x10;                    // Voltage Regulator in full-power mode

   CLKMUL = 0x00;                      // Reset CLKMUL SFR
   CLKMUL |= 0x80;                     // Enable clock multiplier

   Delay_USB ();                       // Delay for clock multiplier to begin

   CLKMUL |= 0xC0;                     // Initialize the clock multiplier
   while (!(CLKMUL & 0x20));           // Wait for multiplier to lock
		
   CLKSEL = SYS_4X_DIV_2;              // Restore Clksel SFR

   USB0XCN = (USB0XCN & 0xFC) | 0x40;  // Enable USB Transceiver

   P2MDOUT = 0x0C;                     // Port 2 pins 2, 3 set push-pull
   P3MDOUT = 0x01;                     // Port 3 pin 1 set push-pull
}

//-----------------------------------------------------------------------------
// Fifo_Read
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : 
// adr - target address
// uNumBytes - number of bytes to read
// pData - destination for read data
//
// Read data from selected endpoint FIFO.
//
//-----------------------------------------------------------------------------
void FIFO_Read (BYTE adr, UINT uNumBytes, BYTE* pData) using USB_REGISTER_BANK
{
   data UINT i;

   if (uNumBytes)                      // Check if >0 bytes requested,
   {      
      USB0ADR = (adr);                 // Set address
      USB0ADR |= 0xC0;                 // Set auto-read and initiate
                                       // first read

      // Unload <NumBytes> from the selected FIFO
      for (i=0;i<uNumBytes-1;i++)
      {         
         while (USB0ADR & 0x80);       // Wait for BUSY->'0' (data ready)
         pData[i] = USB0DAT;           // Copy data byte
      }

      USB0ADR = 0;                     // Clear auto-read

      while (USB0ADR & 0x80);          // Wait for BUSY->'0' (data ready)
      pData[i] = USB0DAT;              // Copy data byte
   }
}

//-----------------------------------------------------------------------------
// Fifo_Write
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : 
// adr - target address
// uNumBytes - number of bytes to write
// pData - location of source data
//
// Writes data to selected endpoint FIFO.
//
//-----------------------------------------------------------------------------
void FIFO_Write (BYTE adr, UINT uNumBytes, BYTE* pData) using USB_REGISTER_BANK
{
   data UINT i;

   if (uNumBytes)                      // If >0 bytes requested,
   {
      while (USB0ADR & 0x80);          // Wait for BUSY->'0'
                                       // (register available)
      USB0ADR = (adr);                 // Set address (mask out bits7-6)

      // Write <NumBytes> to the selected FIFO
      for (i=0;i<uNumBytes;i++)
      {  
         USB0DAT = pData[i];
         while (USB0ADR & 0x80);       // Wait for BUSY->'0' (data ready)
      }
   }
}

//-----------------------------------------------------------------------------
// Fifo_Purge
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : Index - which endpoint to purge
//
// Purges one packet from the selected endpoint in FIFO.
//
//-----------------------------------------------------------------------------
void FIFO_Purge (BYTE Index) using USB_REGISTER_BANK
{
   POLL_WRITE_BYTE (INDEX, Index);     // Set index
   // Flush IN Fifo
   POLL_WRITE_BYTE (EINCSR1, rbInFLUSH);
}

//-----------------------------------------------------------------------------
// Force_Stall
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Sends a procedural stall to the host.
//
//-----------------------------------------------------------------------------
void Force_Stall (void) using USB_REGISTER_BANK
{
   POLL_WRITE_BYTE (INDEX, 0);
   POLL_WRITE_BYTE (E0CSR, rbSDSTL);   // Set the send stall bit
   Ep_Status[0] = EP_STALL;            // Put the endpoint in stall state
}

//-----------------------------------------------------------------------------
// In1_StateMachine
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Write FM tuner registers to endpoint 1.
//
//-----------------------------------------------------------------------------
void In1_StateMachine (void) using USB_REGISTER_BANK
{
   switch (WriteState)
   {
      case 0:                       // Retrieve current register settings
         CONTROL_READ (10, WriteBlock[0]);
         WriteState++;
         break;
      case 1:
         CONTROL_READ (11, WriteBlock[1]);
         WriteState++;
         break;
      case 2:
         CONTROL_READ (12, WriteBlock[2]);
         WriteState++;
         break;
      case 3:
         CONTROL_READ (13, WriteBlock[3]);
         WriteState++;
         break;
      case 4:
         CONTROL_READ (14, WriteBlock[4]);
         WriteState++;
         break;
      case 5:
         CONTROL_READ (15, WriteBlock[5]);
         WriteState++;
         break;
      case 6:                       // Write data to USB FIFO
         while (USB0ADR & 0x80);    // Wait for BUSY->'0'
         USB0ADR = (FIFO_EP1);      // Set address (mask out bits7-6) 
         USB0DAT = 18;
         WriteState++;
         break;
      case 7:
         while (USB0ADR & 0x80);
         USB0ADR = (FIFO_EP1);
         USB0DAT = WriteBlock[0].c[MSB];
         WriteState++;
         while (USB0ADR & 0x80);
         USB0DAT = WriteBlock[0].c[LSB];
         break;
      case 8:
         while (USB0ADR & 0x80);
         USB0ADR = (FIFO_EP1);
         USB0DAT = WriteBlock[1].c[MSB];
         WriteState++;
         while (USB0ADR & 0x80);
         USB0DAT = WriteBlock[1].c[LSB];
         break;
      case 9:
         while (USB0ADR & 0x80);
         USB0ADR = (FIFO_EP1);
         USB0DAT = WriteBlock[2].c[MSB];
         WriteState++;
         while (USB0ADR & 0x80);
         USB0DAT = WriteBlock[2].c[LSB];
         break;
      case 10:
         while (USB0ADR & 0x80);
         USB0ADR = (FIFO_EP1);
         USB0DAT = WriteBlock[3].c[MSB];
         WriteState++;
         while (USB0ADR & 0x80);
         USB0DAT = WriteBlock[3].c[LSB];
         break;
      case 11:
         while (USB0ADR & 0x80);
         USB0ADR = (FIFO_EP1);
         USB0DAT = WriteBlock[4].c[MSB];
         WriteState++;
         while (USB0ADR & 0x80);
         USB0DAT = WriteBlock[4].c[LSB];
         break;
      case 12:
         while (USB0ADR & 0x80);
         USB0ADR = (FIFO_EP1);
         USB0DAT = WriteBlock[5].c[MSB];
         WriteState++;
         while (USB0ADR & 0x80);
         USB0DAT = WriteBlock[5].c[LSB];
         break;
      case 13:
         POLL_WRITE_BYTE (INDEX, 1);
         POLL_WRITE_BYTE (EINCSR1, rbInINPRDY);
         WriteState++;
         POLL_WRITE_BYTE (INDEX, 3);        // Set index to endpoint 3 registers
         break;
      case 14:
      case 15:
      case 16:
      case 17:
      case 18:
         WriteState++;
         break;
      case 19:
         // Flush packet if still on fifo, means host is not polling yet
         POLL_WRITE_BYTE (INDEX, 1);
         POLL_WRITE_BYTE (EINCSR1, rbInFLUSH);
         WriteState++;
         // Set index back to endpoint 3 registers
         POLL_WRITE_BYTE (INDEX, 3);
         break;
      case 20:
         WriteState = 0;
         break;
      default:
         Next_LED_Mode = BLINK_RED_LED;
         break;
   }
}

//-----------------------------------------------------------------------------
// Delay_USB
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Used for a 500 microsecond pause during hardware configuration.
// There are two identical versions of this routine, one for the main program
// and another for the USB interrupt.  This version is for the USB interrupt.
// (assuming 24 MHz system clock)
//
//-----------------------------------------------------------------------------
void Delay_USB (void) using USB_REGISTER_BANK
{
   data volatile int x;
   for (x = 0;x < 500;x)
      x++;
}

//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------