/** \brief USB Setup Handler
 *
 * This function should be called either from the USB interrupt or the main loop when the \c USBIIF.EP0IF
 * flag has been set. Keep in mind that all bits in \c USBIIF register are cleared when the register is
 * read. A detailed description of the framework is found in the \ref section_setup_handler_usage
 * section.
 *
 * \note The USB header data is always little-endian, so if a big-endian compiler is used (such as Keil
 * C51), the 16-bit values in the \ref usbSetupHeader must be flipped before they are used.
 */
void usbfwSetupHandler(void)
{
   uint8 controlReg;
   uint8 bytesNow;
   uint8 oldEndpoint;

   // Save the old index setting, then select endpoint 0 and fetch the control register
   oldEndpoint = USBFW_GET_SELECTED_ENDPOINT();
   USBFW_SELECT_ENDPOINT(0);
   controlReg = USBCS0;

   // The last transfer was ended prematurely by a new SETUP packet
   if (controlReg & USBCS0_SETUP_END) {
      USBCS0 = USBCS0_CLR_SETUP_END;
      usbfwData.ep0Status = EP_CANCEL;
      if (ProcessFunc) ProcessFunc();
      usbfwData.ep0Status = EP_IDLE;
   }

   // A STALL handshake was transmitted to the PC
   if (controlReg & USBCS0_SENT_STALL) {
      USBCS0 = 0x00;
      usbfwData.ep0Status = EP_IDLE;
   }

   // Receive OUT packets
   if (usbfwData.ep0Status == EP_RX) {

      // Read FIFO
      bytesNow = USBCNT0;
      usbfwReadFifo(&USBF0, bytesNow, usbSetupData.pBuffer);
      usbSetupData.bytesLeft -= bytesNow;
      usbSetupData.pBuffer += bytesNow;

      // Arm the endpoint
      USBCS0 = usbSetupData.bytesLeft ? USBCS0_CLR_OUTPKT_RDY : (USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END);

      // Make a call to the appropriate request handler when done
      if (usbSetupData.bytesLeft == 0) {
         if (ProcessFunc) ProcessFunc();
         usbfwData.ep0Status = EP_IDLE;
      }

      // Return here since nothing more will happen until the next interrupt
      USBFW_SELECT_ENDPOINT(oldEndpoint);
      return;

   // Let the application handle the reception
   } else if (usbfwData.ep0Status == EP_MANUAL_RX) {
      ProcessFunc();
   }

   // Receive SETUP header
   if (usbfwData.ep0Status == EP_IDLE) {
      if (controlReg & USBCS0_OUTPKT_RDY) {
         usbfwReadFifo(&USBF0, 8, (uint8 __xdata *) &usbSetupHeader);

         // Handle control transfers individually
         ProcessFunc = NULL;
         switch (usbSetupHeader.requestType & (RT_MASK_TYPE | RT_MASK_DIR)) {

            // Standard requests with data from the host (OUT)
         case RT_STD_OUT:
            switch (usbSetupHeader.request) {
            case SET_ADDRESS:       usbsrSetAddress(); break;
            case SET_FEATURE:       usbsrSetFeature(); break;
            case CLEAR_FEATURE:     usbsrClearFeature(); break;
            case SET_CONFIGURATION: usbsrSetConfiguration(); break;
            case SET_INTERFACE:     usbsrSetInterface(); break;
            case SET_DESCRIPTOR:    /*usbsrHookSetDescriptor(); break; - unsupported */
            default:                usbfwData.ep0Status = EP_STALL; break;
            }
            break;

            // Standard requests with data to the host (IN)
         case RT_STD_IN:
            switch (usbSetupHeader.request) {
            case GET_STATUS:        usbsrGetStatus(); break;
            case GET_DESCRIPTOR:    usbsrGetDescriptor(); break;
            case GET_CONFIGURATION: usbsrGetConfiguration(); break;
            case GET_INTERFACE:     usbsrGetInterface(); break;
            case SYNCH_FRAME:       /*usbsrHookSynchFrame(); break; - unsupported */
            default:                usbfwData.ep0Status = EP_STALL; break;
            }
            break;

            // Vendor requests
         case RT_VEND_OUT:
            ProcessFunc = usbvrHookProcessOut; usbvrHookProcessOut();
            break;
         case RT_VEND_IN:
            ProcessFunc = usbvrHookProcessIn; usbvrHookProcessIn();
            break;

            // Class requests
         case RT_CLASS_OUT:
            ProcessFunc = usbcrHookProcessOut; usbcrHookProcessOut();
            break;
         case RT_CLASS_IN:
            ProcessFunc = usbcrHookProcessIn; usbcrHookProcessIn();
            break;

            // Unrecognized request: Stall the endpoint
         default:
            usbfwData.ep0Status = EP_STALL;
            break;
         }

         // Arm/stall the endpoint
         USBCS0 = (usbfwData.ep0Status == EP_STALL) ? (USBCS0_CLR_OUTPKT_RDY | USBCS0_SEND_STALL) : USBCS0_CLR_OUTPKT_RDY;
      }
   }

   // Transmit IN packets
   if (usbfwData.ep0Status == EP_TX) {
      controlReg = USBCS0_INPKT_RDY;

      // The last frame should contain 0 to (EP0_PACKET_SIZE - 1) bytes
      if (usbSetupData.bytesLeft < EP0_PACKET_SIZE) {
         bytesNow = usbSetupData.bytesLeft;
         controlReg |= USBCS0_DATA_END;

         // All other packets should have the maximum length
      } else {
         bytesNow = EP0_PACKET_SIZE;
      }

      // Load the FIFO and move the pointer
      usbfwWriteFifo(&USBF0, bytesNow, usbSetupData.pBuffer);
      usbSetupData.pBuffer += bytesNow;
      usbSetupData.bytesLeft -= bytesNow;

      // Arm the FIFO (even for a zero-length packet)
      USBCS0 = controlReg;

      // Make a call to the appropriate request handler when done
      if (bytesNow < EP0_PACKET_SIZE) {
         if (ProcessFunc) ProcessFunc();
         usbfwData.ep0Status = EP_IDLE;
      }

   // Let the application handle the transmission
   } else if (usbfwData.ep0Status == EP_MANUAL_TX) {
      ProcessFunc();
   }

   // Restore the old index setting
   USBFW_SELECT_ENDPOINT(oldEndpoint);

} // usbfwSetupHandler
/** \brief USB Setup Handler
 *
 * This function should be called either from the USB interrupt or the main loop when the
 * \c USB_IIF.EP0IF flag has been set. A detailed description of the framework is found in the
 * \ref section_setup_handler_usage section.
 */
void usbfwSetupHandler(void)
{
    uint32_t controlReg;
    uint32_t oldEndpoint;
    uint32_t bytesNow;

    //
    // Save the old index setting, then select endpoint 0 and fetch the control register
    //
    oldEndpoint = USBFW_GET_SELECTED_ENDPOINT();
    USBFW_SELECT_ENDPOINT(0);
    controlReg = HWREG(USB_CS0);

    //
    // Update the USB device address after the status stage
    //
    if(usbfwData.ep0Status == EP_ADDRESS)
    {
        if(!(controlReg & USB_CS0_OUTPKTRDY_M))
        {
            uint8_t address = usbSetupHeader.valueLsb;
            HWREG(USB_ADDR) = address;
            if(usbfwData.usbState < DEV_CONFIGURED)
            {
                if(address)
                {
                    usbfwData.usbState = DEV_ADDRESS;
                }
                else
                {
                    usbfwData.usbState = DEV_DEFAULT;
                }
            }
        }
        usbfwData.ep0Status = EP_IDLE;
    }

    //
    // A STALL handshake was transmitted to the host
    //
    if(controlReg & USB_CS0_SENTSTALL_M)
    {
        HWREG(USB_CS0) = 0x00;
        usbfwData.ep0Status = EP_IDLE;
    }

    //
    // The last transfer was ended prematurely by a new SETUP packet
    //
    if(controlReg & USB_CS0_SETUPEND_M)
    {
        HWREG(USB_CS0) = USB_CS0_CLRSETUPEND_M;
        usbfwData.ep0Status = EP_CANCEL;
        if(pProcessFunc)
        {
            pProcessFunc();
        }
        usbfwData.ep0Status = EP_IDLE;
    }

    //
    // Receive OUT packets
    //
    if(usbfwData.ep0Status == EP_RX)
    {
        if(controlReg & USB_CS0_OUTPKTRDY_M)
        {
            //
            // Read FIFO
            //
            uint32_t bytesNow = HWREG(USB_CNT0);
            usbfwReadFifo(USB_F0, bytesNow, usbSetupData.pBuffer);
            usbSetupData.bytesLeft -= bytesNow;
            usbSetupData.pBuffer = ((uint8_t*) usbSetupData.pBuffer) + bytesNow;

            //
            // Arm the endpoint
            //
            if(usbSetupData.bytesLeft)
            {
                HWREG(USB_CS0) = USB_CS0_CLROUTPKTRDY_M;
            }
            else
            {
                HWREG(USB_CS0) = USB_CS0_CLROUTPKTRDY_M | USB_CS0_DATAEND_M;
            }

            //
            // Make a call to the appropriate request handler when done
            //
            if(usbSetupData.bytesLeft == 0)
            {
                if(pProcessFunc)
                {
                    pProcessFunc();
                }
                usbfwData.ep0Status = EP_IDLE;
            }
        }

        //
        // Return here since nothing more will happen until the next interrupt
        //
        USBFW_SELECT_ENDPOINT(oldEndpoint);
        return;

        //
        // Let the application handle the reception
        //
    }
    else if(usbfwData.ep0Status == EP_MANUAL_RX)
    {
        if(pProcessFunc)
        {
            pProcessFunc();
        }
    }

    //
    // Receive SETUP header
    //
    if(usbfwData.ep0Status == EP_IDLE)
    {
        if(controlReg & USB_CS0_OUTPKTRDY_M)
        {
            usbfwReadFifo(USB_F0, 8, &usbSetupHeader);

            //
            // Handle control transfers individually
            //
            pProcessFunc = NULL;
            switch(usbSetupHeader.requestType & (RT_MASK_TYPE | RT_MASK_DIR))
            {
                //
                // Standard requests without data or with data from the host (OUT)
                //
            case RT_STD_OUT:
                switch(usbSetupHeader.request)
                {
                case USBSR_REQ_SET_ADDRESS:
                    usbsrSetAddress();
                    break;
                case USBSR_REQ_SET_FEATURE:
                    usbsrSetFeature();
                    break;
                case USBSR_REQ_CLEAR_FEATURE:
                    usbsrClearFeature();
                    break;
                case USBSR_REQ_SET_CONFIGURATION:
                    usbsrSetConfiguration();
                    break;
                case USBSR_REQ_SET_INTERFACE:
                    usbsrSetInterface();
                    break;
                case USBSR_REQ_SET_DESCRIPTOR:
                    pProcessFunc = usbsrHookSetDescriptor;
                    usbsrHookSetDescriptor();
                    break;
                default:
                    usbfwData.ep0Status = EP_STALL;
                    break;
                }
                break;

                //
                // Standard requests with data to the host (IN)
                //
            case RT_STD_IN:
                switch(usbSetupHeader.request)
                {
                case USBSR_REQ_GET_STATUS:
                    usbsrGetStatus();
                    break;
                case USBSR_REQ_GET_DESCRIPTOR:
                    usbsrGetDescriptor();
                    break;
                case USBSR_REQ_GET_CONFIGURATION:
                    usbsrGetConfiguration();
                    break;
                case USBSR_REQ_GET_INTERFACE:
                    usbsrGetInterface();
                    break;
                case USBSR_REQ_SYNCH_FRAME:
                    pProcessFunc = usbsrHookSynchFrame;
                    usbsrHookSynchFrame();
                    break;
                default:
                    usbfwData.ep0Status = EP_STALL;
                    break;
                }
                break;

                //
                // Vendor requests
                //
            case RT_VEND_OUT:
                pProcessFunc = usbvrHookProcessOut;
                usbvrHookProcessOut();
                break;
            case RT_VEND_IN:
                pProcessFunc = usbvrHookProcessIn;
                usbvrHookProcessIn();
                break;

                //
                // Class requests
                //
            case RT_CLASS_OUT:
                pProcessFunc = usbcrHookProcessOut;
                usbcrHookProcessOut();
                break;
            case RT_CLASS_IN:
                pProcessFunc = usbcrHookProcessIn;
                usbcrHookProcessIn();
                break;

                //
                // Unrecognized request: Stall the endpoint
                //
            default:
                usbfwData.ep0Status = EP_STALL;
                break;
            }

            //
            // Arm/stall the endpoint
            //
            if(usbfwData.ep0Status == EP_STALL)
            {
                HWREG(USB_CS0) = USB_CS0_CLROUTPKTRDY_M | USB_CS0_SENDSTALL_M;
            }
            else if((usbfwData.ep0Status == EP_TX) || (usbfwData.ep0Status == EP_RX))
            {
                HWREG(USB_CS0) = USB_CS0_CLROUTPKTRDY_M;
            }
            else
            {
                HWREG(USB_CS0) = USB_CS0_CLROUTPKTRDY_M | USB_CS0_DATAEND_M;
            }
        }
    }

    //
    // Transmit IN packets
    //
    if(usbfwData.ep0Status == EP_TX)
    {
        controlReg = USB_CS0_INPKTRDY_M;

        //
        // The last frame should contain 0 to (EP0_PACKET_SIZE - 1) bytes
        //
        if(usbSetupData.bytesLeft < USB_EP0_PACKET_SIZE)
        {
            bytesNow = usbSetupData.bytesLeft;
            controlReg |= USB_CS0_DATAEND_M;
        }
        else
        {
            //
            // All other packets should have the maximum length
            //
            bytesNow = USB_EP0_PACKET_SIZE;
        }

        //
        // Load the FIFO and move the pointer
        //
        usbfwWriteFifo(USB_F0, bytesNow, usbSetupData.pBuffer);
        usbSetupData.pBuffer = ((uint8_t*) usbSetupData.pBuffer) + bytesNow;
        usbSetupData.bytesLeft -= bytesNow;

        //
        // Arm the FIFO (even for a zero-length packet)
        //
        HWREG(USB_CS0) = controlReg;

        //
        // Make a call to the appropriate request handler when done
        //
        if(bytesNow < USB_EP0_PACKET_SIZE)
        {
            if(pProcessFunc)
            {
                pProcessFunc();
            }
            usbfwData.ep0Status = EP_IDLE;
        }

        //
        // Let the application handle the transmission
        //
    }
    else if(usbfwData.ep0Status == EP_MANUAL_TX)
    {
        if(pProcessFunc)
        {
            pProcessFunc();
        }
    }

    //
    // Restore the old index setting
    //
    USBFW_SELECT_ENDPOINT(oldEndpoint);
}