//-----------------------------------------------------------------------------
// F320_USB_Class_Specific_Requests.c
//-----------------------------------------------------------------------------
// Copyright 2006 Silicon Laboratories, Inc.
// http://www.silabs.com
//
// Program Description:
//
// This module handles all human interface device (HID) and audio USB
// class-specific requests.
//
// FID:            32X000036
// 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 AUG 2005
//

//-----------------------------------------------------------------------------
// 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 BYTE ReturnBuffer[64];

//-----------------------------------------------------------------------------
// Get_Cur
//
// Return Value : None
// Parameters   : None
//
// Retrieves the current setting for a given audio control.  Audio
// class-specific request.
//
//-----------------------------------------------------------------------------
void Get_Cur (void) using USB_REGISTER_BANK
{
   DataPtr = ZERO_PACKET;
   DataSize = Setup.wLength.i;                                    

   if (Ep_Status[0] != EP_STALL)
   {
      // Set serviced setup packet bit, put endpoint in transmit mode and reset
      // data sent counter
      POLL_WRITE_BYTE (E0CSR, rbSOPRDY);
      Ep_Status[0] = EP_TX;
      DataSent = 0;
   }
}

//-----------------------------------------------------------------------------
// Set_Cur
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Sets current value for a given audio control.  Audio class-specific request.
//
//-----------------------------------------------------------------------------
void Set_Cur (void) using USB_REGISTER_BANK
{
   DataPtr = (BYTE *)&ReturnBuffer;    // Write data to dummy location
   DataSize = Setup.wLength.i;                                    

   if (Ep_Status[0] != EP_STALL)
   {
      // Set serviced setup packet bit, put endpoint in receive mode and reset
      // data sent counter
      POLL_WRITE_BYTE (E0CSR, rbSOPRDY);
      Ep_Status[0] = EP_RX;
      DataSent = 0;
   }
}

//-----------------------------------------------------------------------------
// Set_Idle
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Sets the idle feature on interrupt in endpoint.
//
//-----------------------------------------------------------------------------
void Set_Idle (void) using USB_REGISTER_BANK
{
   // Indicate setup packet has been serviced
   if (Ep_Status[0] != EP_STALL) POLL_WRITE_BYTE (E0CSR, rbSOPRDY);
}

//-----------------------------------------------------------------------------
// Get_Report
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Sends a given report type to the host.
//
//-----------------------------------------------------------------------------
void Get_Report (void) using USB_REGISTER_BANK
{
   data BYTE i;
   data WORD TempRegister;

   ReturnBuffer[0] = Setup.wValue.c[LSB];
   DataPtr = (BYTE*)&ReturnBuffer;

   switch (ReturnBuffer[0])
   {
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
      case 9:
      case 10:
      case 11:
      case 12:
      case 13:
      case 14:
      case 15:
      case 16:
         CONTROL_READ (Setup.wValue.c[LSB] - 1, TempRegister);
         ReturnBuffer[1] = TempRegister.c[MSB];
         ReturnBuffer[2] = TempRegister.c[LSB];
         DataSize = 3;
         break;

      case 17:
         // Retrieve all current register settings
         for (i = 0;i<16;i++)
         {
            CONTROL_READ (i, TempRegister);
            ReturnBuffer[(2*i)+1] = TempRegister.c[MSB];
            ReturnBuffer[(2*i)+2] = TempRegister.c[LSB];
         }
         DataSize = 33;
         break;

      case 20:
         for (i = 1;i<=64;i++)
         {
            ReturnBuffer[i] = Host_Data[i-1];
         }
         DataSize = 64;
         break;

      case 22:
         DataSize = 2;
         break;

      default:
         Force_Stall ();
         break;
      
   }

   if (Ep_Status[0] != EP_STALL)
   {
      // Set serviced setup packet bit, put endpoint in transmit mode and reset
      // data sent counter
      POLL_WRITE_BYTE (E0CSR, rbSOPRDY);
      Ep_Status[0] = EP_TX;
      DataSent = 0;
   }
}

//-----------------------------------------------------------------------------
// Set_Report
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Receives a report sent from the host.
//
//-----------------------------------------------------------------------------
void Set_Report (void) using USB_REGISTER_BANK
{
   DataPtr = (BYTE*)&ReturnBuffer;
   DataSize = Setup.wLength.i;

   if (Ep_Status[0] != EP_STALL)
   {
      // Set serviced setup packet bit, put endpoint in receive mode and reset
      // data sent counter
      POLL_WRITE_BYTE (E0CSR, rbSOPRDY);
      Ep_Status[0] = EP_RX;
      DataSent = 0;
   }
}

//-----------------------------------------------------------------------------
// Handle_Set_Report
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Takes appropriate action based on report content, most commands are sent 
// from the host directly to the Si470x.
//
//-----------------------------------------------------------------------------
void Handle_Set_Report (void) using USB_REGISTER_BANK
{
   data BYTE i;
   data WORD TempRegister;

   switch (ReturnBuffer[0])
   {
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
      case 9:
      case 10:
      case 11:
      case 12:
      case 13:
      case 14:
      case 15:
      case 16:
         TempRegister.c[MSB] = ReturnBuffer[1];
         TempRegister.c[LSB] = ReturnBuffer[2];
         CONTROL_WRITE (ReturnBuffer[0] - 1, TempRegister);

         Next_LED_Mode = BLINK_GREEN_LED;
         break;

      case 17:
         for (i = 0;i<16;i++)
         {
            TempRegister.c[MSB] = ReturnBuffer[(2*i)+1];
            TempRegister.c[LSB] = ReturnBuffer[(2*i)+2];
            CONTROL_WRITE (i, TempRegister);
         }

         Next_LED_Mode = BLINK_GREEN_LED;
         break;

      case 19:
         switch (ReturnBuffer[1])
         {
            case 0x34:
               if ((ReturnBuffer[2] == Host_Data[1]) && 
                  (ReturnBuffer[3] == Host_Data[2]))
               {  // Looking for command to start bootloader
                  Next_LED_Mode = SOLID_RED_LED;
                  LED_StateMachine (); // Change LED state
                  Radio_Flasher ();
               }
               break;

            case 0x35:
               // Application changing led state
               Next_LED_Mode = ReturnBuffer[2];
               LED_StateMachine (); // Change LED state
               break;

            default:
               Force_Stall ();
               break;

         }
         break;

      case 20:
         Write_Custom_Data ();
         break;

      default:
         Force_Stall ();
         break;
      
   }
}

//-----------------------------------------------------------------------------
// Write_Custom_Data
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Writes 63 bytes customizable section containing device settings, presets,
// etc.  The data format is under GUI control.
//
//-----------------------------------------------------------------------------
void Write_Custom_Data (void) using USB_REGISTER_BANK
{
   data BYTE k;
   data char xdata * Flash_Address;
   data char idata * Data_Address;

   //  Disable interrupts while writing to flash
   EA = OFF;
   EA = OFF;
   Flash_Address = 0x3600;
   FLKEY = 0xA5;
   FLKEY = 0xF1;
   // Enable writes (PSWE) and page erase (PSEE)
   PSCTL = 0x03;
   RSTSRC = 0x02;                      // Make sure VDD monitor is enabled
   *Flash_Address = 0x00;              // Dummy write to page to initiate erase
   PSCTL = 0x00;	

   Flash_Address = 0x3600;             // Start at base of page
   Data_Address = (&ReturnBuffer) + 1;
   PSCTL = 0x01;                       // Enable writes
   for (k = 0; k < 63; k++)
   {
      FLKEY = 0xA5;
      FLKEY = 0xF1;
      RSTSRC = 0x02;                   // Make sure VDD monitor is enabled
      *Flash_Address++ = *Data_Address++;	
   }

   PSCTL = 0x00; 				

   EA = ON;                            //  Enable interrupts after flash write
}

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