//-----------------------------------------------------------------------------
// F320_Command_Interpreter.c
//-----------------------------------------------------------------------------
// Copyright 2006 Silicon Laboratories, Inc.
// http://www.silabs.com
//
// Program Description:
//
// This is the module updates the F320 flash image over the USB interface.
//
// FID:            32X000043
// 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
//    -Changed compiler optimization to 8 to locate all command interpreter
//    code together.  Level 9 placed a common block incorrectly (DM)
//    -Increased software version to 7 (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_USB_Register.h"         // USB core register header
#include "F320_USB_Common.h"           // USB protocol header

//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------

#define COMMAND_OK	0x01
#define COMMAND_FAILED 0x02
#define UPDATE_PAGE 28

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

idata BYTE ChipReg;
idata BYTE CmdBuff[3];
idata BYTE FlashPage;
idata unsigned int PageBase;

// HostScratchByte, SoftwareVersion, HardwareVersion
code const BYTE Host_Data[512] = { 0x00, 0x07, 0x01 };

//-----------------------------------------------------------------------------
// Radio_Flasher
//
// Return Value : None
// Parameters   : None
//
// Main command interpretter routine.  Responsible for decoding
// message type and calling correct subroutine.
//
//  The command to get into the interpretter MUST be set report 0xFF FF AA BB
// AA is the current hardware version, BB is the current software version
//
// All Command Interpretter messages are get and set report HID class requests.
// Command from the host have report format KK XX YY ZZ
//
// Byte 0 (KK) is the report ID(= 0xFF for all command interpretter messages)
// Byte 1 (XX) designates what type of command interpretter message this is.
// Byte 2 (YY) has different uses for different messages
// Byte 3 (ZZ) has different uses for different messages
// 
// Command Interpretter responses are one byte COMMAND_OK(0x01) and where
// possible COMMAND_FAILED(0x02), no response is also considered command failed
//
//-----------------------------------------------------------------------------
void Radio_Flasher (void) using USB_REGISTER_BANK
{
   EA = OFF;
   EA = OFF;
   POLL_WRITE_BYTE (INDEX, 0);
   POLL_WRITE_BYTE (E0CSR, (rbSOPRDY | rbDATAEND));
   PageBase = 0xFFFF;
   FlashPage = 0x00;
   while (1)
   {
      GetCommand ();

      switch (CmdBuff[0])
      {
         case 0x00:                    // Get Software Version Command
            SendByte (22);             // Return report ID byte

            SendByte (Host_Data[1]);
            WaitStatus ();
            break;

         case 0x01:                    // Set Page Command
            SetPage ();
            break;

         case 0x02:                    // Erase Page Command
            ErasePage ();
            break;

         case 0x03:                    // Write Page Command
            WritePage ();
            break;

         case 0x04:                    // CRC on Page Command
            CRConPage ();	
            break;

         case 0x05:                    // Read Flash Byte Command
            ReadFlashByte ();			
            break;

         case 0x06:                    // Reset Device/Bus Reset Command
            RSTSRC |= 0x10;
            break;

         case 0x07:                    // Get Hardware Version Command
            SendByte (22);             // Return report ID byte

            SendByte (Host_Data[2]);
            WaitStatus ();
            break;

         default:
            SendByte (22);             // Return report ID byte

            SendByte (COMMAND_FAILED);
            WaitStatus ();
            break;

      }
   }
   return;
}

//-----------------------------------------------------------------------------
// SetPage
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Sets the global variables FlashPage and PageBase. PageBase is the 16-bit 
// address of the first byte of the Flash page.  The contents of FlashPage 
// and PageBase are used as the target Flash pages for following operations 
// (Erase, Write, CRC).
//
//-----------------------------------------------------------------------------
void SetPage (void) using USB_REGISTER_BANK 
{
   data unsigned char Page;

   Page = CmdBuff[1];
   if ((Page >= UPDATE_PAGE))
   {
      SendByte (22);                   // Return report ID byte

      SendByte (COMMAND_FAILED);
      WaitStatus ();
   }
   else
   {
      FlashPage = Page; 
      // Convert to 16-bit address
      PageBase = (unsigned int)(FlashPage << 9);

      SendByte (22);                   // Return report ID byte

      SendByte (COMMAND_OK);
      WaitStatus ();
   }
   return;
}

//-----------------------------------------------------------------------------
// ErasePage
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// The ErasePage routine enables flash erases, writes a dummy data byte to the
// currently selected flash page and then disables flash erases.  Note the
// "address" parameter is of type xdata.  This tricks the compiler into using
// MOVX which is required to write to flash in C8051Fxxx devices.
//
//-----------------------------------------------------------------------------
void ErasePage (void) using USB_REGISTER_BANK
{
   data char xdata * FlashAddress;
   FlashAddress = PageBase;
   FLKEY = 0xA5;
   FLKEY = 0xF1;
   // Enable writes (PSWE) and page erase (PSEE)
   PSCTL = 0x03;
   RSTSRC = 0x02;                      // Make sure VDD monitor is enabled
   *FlashAddress = 0x00;               // Dummy write to page to initiate erase 
   PSCTL = 0x00;                       // Disable writes and page erase

   SendByte (22);                      // Return report ID byte
   SendByte (COMMAND_OK);
   WaitStatus ();
   return;
}

//-----------------------------------------------------------------------------
// WritePage
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// WritePage() programs 512 bytes of data to the currently selected Flash page
// starting at the beginning of the page (lowest address).  Once the download
// is complete, COMMAND_OK is sent to signal the end of the operation.
//
//-----------------------------------------------------------------------------
void WritePage (void) using USB_REGISTER_BANK
{
   data unsigned int n = 0;
   data char xdata * FlashAddress;

   FlashAddress = PageBase;            // Start at base of page
   PSCTL = 0x01;                       // Enable writes

   while (n < 512)
   {
      if (n != 0)
      {
         ChipReg = 0;
         while (!(ChipReg & rbOPRDY)) POLL_READ_BYTE (E0CSR, ChipReg); 
         POLL_WRITE_BYTE (E0CSR, rbSOPRDY);
      }

      ChipReg = 0;
      while (!(ChipReg & rbOPRDY)) POLL_READ_BYTE(E0CSR, ChipReg);

      while (USB0ADR & 0x80);          // Wait for BUSY->'0' (data ready)
      USB0ADR = (0x80 | FIFO_EP0);     // Set address and initiate read
      ChipReg = 63;
      while ((ChipReg) && (n < 512))
      {
         FLKEY = 0xA5;
         FLKEY = 0xF1;
         while (USB0ADR & 0x80);       // Wait for BUSY->'0' (data ready)
         USB0ADR = (0x80 | FIFO_EP0);  // Set address and initiate read
         ChipReg--;
         n++;
         while (USB0ADR & 0x80);       // Wait for BUSY->'0' (data ready)
         RSTSRC = 0x02;                // Make sure VDD monitor is enabled
         *FlashAddress++ = (USB0DAT ^ 0x55);	
      }

      POLL_WRITE_BYTE (E0CSR, (rbSOPRDY | rbDATAEND));
   }
   PSCTL = 0x00;                       // Disable writes
   return;
}

//-----------------------------------------------------------------------------
// CRConPage
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Returns a 16-bit CRC on the currently selected 512-byte Flash page.  The
// high byte is sent first followed by the lower byte of the CRC.  No
// additional response is transmitted.
//
//-----------------------------------------------------------------------------
void CRConPage (void) using USB_REGISTER_BANK
{
   data unsigned int k, j, CRC;
   data unsigned char code * FlashPtr;

   FlashPtr = PageBase;
   CRC = 0x0000;

   // Process each byte in the page into the running CRC
   for (k = 0; k < 512; k++)
   {
      // Read the next Flash byte and XOR it with the upper 8 bits 
      // of the running CRC.
      CRC ^= (*FlashPtr++ << 8);

      // For each bit in the upper byte of the CRC, shift CRC 
      // left, test the MSB, and if set, XOR the CRC with the
      // polynomial coefficients (0x1021)
      for (j = 0; j < 8; j++)
      {
         CRC = CRC << 1;
         if (CRC & 0x8000 ) CRC ^= 0x1021;	
      }
   }
   SendByte(21);                       // Return report ID byte

   // Send back the calculated CRC
   SendByte ((unsigned char)(CRC & 0x00FF));
   SendByte ((unsigned char)(CRC >> 8));
   WaitStatus ();
   return;
}

//-----------------------------------------------------------------------------
// ReadFlashByte
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Reads a single byte of Flash located at the 16-bit address held in the
// second and third bytes of the command buffer.
//
//-----------------------------------------------------------------------------
void ReadFlashByte (void) using USB_REGISTER_BANK
{
   data unsigned int Addr16;
   data unsigned char code * FlashPtr;

   Addr16 = CmdBuff[1];	               // Build 16-bit address
   Addr16 = Addr16 << 8;	
   FlashPtr = Addr16 | (unsigned int) CmdBuff[2]; 

   SendByte (22);                      // Return report ID byte
   SendByte (*FlashPtr);               // Return Flash byte
   WaitStatus ();
   return;	
}

//-----------------------------------------------------------------------------
// GetCommand
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Polls the USB until a command is received.
//
//-----------------------------------------------------------------------------
void GetCommand (void) using USB_REGISTER_BANK
{
   ChipReg = 0;
   while (!(ChipReg & rbOPRDY)) POLL_READ_BYTE (E0CSR, ChipReg);
   POLL_WRITE_BYTE (E0CSR, rbSOPRDY);

   ChipReg = 0;
   while (!(ChipReg & rbOPRDY))   POLL_READ_BYTE(E0CSR, ChipReg);

   while (USB0ADR & 0x80);             // Wait for BUSY->'0' (data ready)
   USB0ADR = (0x80 | FIFO_EP0);        // Set address and initiate read
   while (USB0ADR & 0x80);             // Wait for BUSY->'0' (data ready)
   CmdBuff[0] = USB0DAT;

   while (USB0ADR & 0x80);             // Wait for BUSY->'0' (data ready)
   USB0ADR = (0x80 | FIFO_EP0);        // Set address and initiate read
   while (USB0ADR & 0x80);             // Wait for BUSY->'0' (data ready)
   CmdBuff[0] = USB0DAT;

   while (USB0ADR & 0x80);             // Wait for BUSY->'0' (data ready)
   USB0ADR = (0x80 | FIFO_EP0);        // Set address and initiate read
   while (USB0ADR & 0x80);             // Wait for BUSY->'0' (data ready)
   CmdBuff[1] = USB0DAT;

   while (USB0ADR & 0x80);             // Wait for BUSY->'0' (data ready)
   USB0ADR = (0x80 | FIFO_EP0);        // Set address and initiate read
   while (USB0ADR & 0x80);             // Wait for BUSY->'0' (data ready)
   CmdBuff[2] = USB0DAT;

   POLL_WRITE_BYTE (E0CSR, (rbSOPRDY | rbDATAEND));
   ChipReg = 0;
   while (!(ChipReg & rbOPRDY))   POLL_READ_BYTE(E0CSR, ChipReg);
   POLL_WRITE_BYTE (E0CSR, rbSOPRDY);
   return;
}

//-----------------------------------------------------------------------------
// SendByte
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : bData - data to send
//
// Sends data to USB host.
//
//-----------------------------------------------------------------------------
void SendByte (BYTE bData) using USB_REGISTER_BANK
{
   while (USB0ADR & 0x80);             // Wait for BUSY->'0'
   USB0ADR = (FIFO_EP0);               // Set address

   USB0DAT = bData;
   while (USB0ADR & 0x80);
   return;
}

//-----------------------------------------------------------------------------
// WaitStatus
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Finishes the status phase of USB command.
//
//-----------------------------------------------------------------------------
void WaitStatus (void) using USB_REGISTER_BANK
{
   POLL_WRITE_BYTE (E0CSR, (rbINPRDY | rbDATAEND));
   return;
}

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