/** \brief	Locates an interface descriptor
*
* The function will first go to the configuration descriptor that matches the supplied configuration
* value, and then locate the interface descriptor that matches the given interface number and alternate
* setting.
*
* \note It is not necessary to call \ref usbdpInit() before this function.
*
* \param[in]       cfgValue
*     The configuration value (\ref USB_CONFIGURATION_DESCRIPTOR.bConfigurationValue)
* \param[in]       intNumber
*     The interface number (\ref USB_INTERFACE_DESCRIPTOR.bInterfaceNumber)
* \param[in]       altSetting
*     The alternate setting (\ref USB_INTERFACE_DESCRIPTOR.bAlternateSetting)
*
* \return
*     A pointer to the \ref USB_INTERFACE_DESCRIPTOR, or \c NULL if it was not found.
*/
USB_INTERFACE_DESCRIPTOR __code* usbdpGetInterfaceDesc(uint8 cfgValue, uint8 intNumber, uint8 altSetting)
{
   USB_INTERFACE_DESCRIPTOR __code *pInterfaceDesc;

   // First get to the correct configuration
   usbdpGetConfigurationDesc(cfgValue, 0);

   // Then find a match on the interface
   while (pInterfaceDesc = usbdpFindNext(DESC_TYPE_INTERFACE, DESC_TYPE_CONFIG)) {
      if ((pInterfaceDesc->bInterfaceNumber == intNumber) && (pInterfaceDesc->bAlternateSetting == altSetting)) {
         break;
      }
   }

   return pInterfaceDesc;
} // usbdpGetInterfaceDesc
/** \brief	Locates a configuration descriptor
*
* The search will either look for a descriptor with a specific
* \ref USB_CONFIGURATION_DESCRIPTOR.bConfigurationValue, or simply take the n'th descriptor (by "index")
*
* \note It is not necessary to call \ref usbdpInit() before this function.
*
* \param[in]       cfgValue
*     The configuration value to search for (\ref USB_CONFIGURATION_DESCRIPTOR.bConfigurationValue), or
*     0 to find descriptor by index
* \param[in]       cfgIndex
*     A zero-based index for the configuration descriptor to find.
*     This value is ignored unless \c cfgValue is 0.
*
* \return
*     A pointer to the \ref USB_DEVICE_DESCRIPTOR, or \c NULL if it was not found.
*/
USB_CONFIGURATION_DESCRIPTOR __code* usbdpGetConfigurationDesc(uint8 cfgValue, uint8 cfgIndex)
{
   USB_CONFIGURATION_DESCRIPTOR __code *pConfigurationDesc;
   usbdpInit();

   // As long as there are more configuration descriptors...
   while (pConfigurationDesc = usbdpFindNext(DESC_TYPE_CONFIG, 0)) {

      // Search by value?
      if (cfgValue) {
         if (cfgValue == pConfigurationDesc->bConfigurationValue) break;

      // Search by index? (search cfgIndex+1 times)
      } else if (!cfgIndex--) {
         break;
      }
   }

   return pConfigurationDesc;
} // usbdpGetConfigurationDesc
/** \brief Internally used function that configures all endpoints for the specified interface
 *
 * The new endpoint setup overwrites the old, without any warning. Unused endpoints keep their current
 * setup. The user is responsible for ensuring that no endpoint buffers overwrite each other, and that
 * interfaces do not cause conflicts. The pUsbDblbufLutInfo table must contain an entry for each
 * interface descriptor to define endpoint double-buffering.
 *
 * \param[in]       *pInterface
 *     A pointer to the interface descriptor
 */
static void ConfigureEndpoints(USB_INTERFACE_DESCRIPTOR __xdata *pInterface)
{
   uint8 n;
   uint16 maxpRegValue;
   uint8 csRegValue;
   uint8 endpoint;
   USB_ENDPOINT_DESCRIPTOR __xdata *pEndpoint;
   DBLBUF_LUT_INFO __xdata *pUsbDblbufLutInfo;

   // Locate the double buffer settings
   if (!pInterface->bNumEndpoints) {
       return;
   }
   pUsbDblbufLutInfo = (DBLBUF_LUT_INFO __xdata*) usbDescriptorMarker.pUsbDblbufLut;
   while (pUsbDblbufLutInfo->pInterface != pInterface) {
      pUsbDblbufLutInfo++;
   }

   // For each endpoint in this interface
   for (n = 0; n < pInterface->bNumEndpoints; n++) {
      if (pEndpoint = usbdpFindNext(DESC_TYPE_ENDPOINT, 0)) {

         // Get the endpoint index
         endpoint = pEndpoint->bEndpointAddress & 0x0F;
         USBFW_SELECT_ENDPOINT(endpoint);

         csRegValue = 0x00;
         maxpRegValue = (pEndpoint->wMaxPacketSize + 7) >> 3;

         // For IN endpoints...
         if (pEndpoint->bEndpointAddress & 0x80) {

            // Clear data toggle, and flush twice (due to double buffering)
            USBCSIL = USBCSIL_CLR_DATA_TOG | USBCSIL_FLUSH_PACKET;
            USBCSIL = USBCSIL_FLUSH_PACKET;

            // USBCSIH
            if ((pEndpoint->bmAttributes & EP_ATTR_TYPE_BM) == EP_ATTR_ISO) csRegValue |= USBCSIH_ISO;  // ISO flag
            if (pUsbDblbufLutInfo->inMask & (1 << endpoint)) csRegValue |= USBCSIH_IN_DBL_BUF;          // Double buffering
            USBCSIH = csRegValue;

            // Max transfer size
            USBMAXI = maxpRegValue;

            // Endpoint status
            usbfwData.pEpInStatus[endpoint - 1] = EP_IDLE;

         // For OUT endpoints...
         } else {

            // Clear data toggle, and flush twice (due to double buffering)
            USBCSOL = USBCSOL_CLR_DATA_TOG | USBCSOL_FLUSH_PACKET;
            USBCSOL = USBCSOL_FLUSH_PACKET;

            // USBCSOH
            if ((pEndpoint->bmAttributes & EP_ATTR_TYPE_BM) == EP_ATTR_ISO) csRegValue |= USBCSOH_ISO;  // ISO flag
            if (pUsbDblbufLutInfo->outMask & (1 << endpoint)) csRegValue |= USBCSOH_OUT_DBL_BUF;        // Double buffering
            USBCSOH = csRegValue;

            // Max transfer size
            USBMAXO = maxpRegValue;

            // Endpoint status
            usbfwData.pEpOutStatus[endpoint - 1] = EP_IDLE;
         }
         USBFW_SELECT_ENDPOINT(0);
      }
   }
/** \brief	Locates the (one and only) device descriptor
*
* \note It is not necessary to call \ref usbdpInit() before this function.
*
* \return
*     A pointer to the \ref USB_DEVICE_DESCRIPTOR, or \c NULL if it was not found.
*/
USB_DEVICE_DESCRIPTOR __code* usbdpGetDeviceDesc(void)
{
   usbdpInit();
   return usbdpFindNext(DESC_TYPE_DEVICE, 0);
} // usbdpGetDeviceDesc
/** \brief Internally used function that configures all endpoints for the specified interface
 *
 * The new endpoint setup overwrites the old. Unused endpoints keep their current setup. The user is
 * responsible for ensuring that no endpoint buffers overwrite each other, and that interfaces do not
 * cause conflicts. The \ref pUsbInterfaceEpDblbufLut[] lookup table must contain an entry for each
 * interface descriptor to define endpoint double-buffering.
 *
 * \param[in]       *pInterface
 *     A pointer to the interface descriptor
 */
static void usbsrConfigureEndpoints(const USB_INTERFACE_DESCRIPTOR* pInterface)
{
    const USB_ENDPOINT_DESCRIPTOR* pEndpoint;
    const USB_INTERFACE_EP_DBLBUF_LUT* pUsbInterfaceEpDblbufInfo;

    //
    // Locate the double-buffer settings
    //
    if(pInterface->bNumEndpoints)
    {
        pUsbInterfaceEpDblbufInfo = (const USB_INTERFACE_EP_DBLBUF_LUT*) pUsbInterfaceEpDblbufLut;
        while(pUsbInterfaceEpDblbufInfo->pInterface != pInterface)
        {
            pUsbInterfaceEpDblbufInfo++;
        }
    }

    //
    // For each endpoint in this interface
    //
    for(uint8_t n = 0; n < pInterface->bNumEndpoints; n++)
    {
        if(pEndpoint = usbdpFindNext(USB_DESC_TYPE_ENDPOINT, 0))
        {
            //
            // Get the endpoint index
            //
            uint32_t endpoint = pEndpoint->bEndpointAddress & 0x0F;
            USBFW_SELECT_ENDPOINT(endpoint);

            uint32_t csRegValue = 0x00;
            uint32_t maxpRegValue = (pEndpoint->wMaxPacketSize + 7) >> 3;

            //
            // For IN endpoints...
            //
            if(pEndpoint->bEndpointAddress & 0x80)
            {
                //
                // Clear data toggle, and flush twice (due to double buffering)
                //
                HWREG(USB_CS0_CSIL) = USB_CSIL_CLRDATATOG_M | USB_CSIL_FLUSHPACKET_M;
                HWREG(USB_CS0_CSIL) = USB_CSIL_FLUSHPACKET_M;
                
                //
                // USBCSIH
                //
                if((pEndpoint->bmAttributes & USB_EP_ATTR_TYPE_BM) == USB_EP_ATTR_ISO)
                {
                    //
                    // ISO flag
                    //
                    csRegValue |= USB_CSIH_ISO_M;
                }
                if(pUsbInterfaceEpDblbufInfo->inMask & (1 << endpoint))
                {
                    //
                    // Double buffering
                    //
                    csRegValue |= USB_CSIH_INDBLBUF_M;
                }
                HWREG(USB_CSIH) = csRegValue;

                //
                // Max transfer size
                //
                HWREG(USB_MAXI) = maxpRegValue;

                //
                // Endpoint status
                //
                usbfwData.pEpInStatus[endpoint - 1] = EP_IDLE;

                //
                // For OUT endpoints...
                //
            }
            else
            {
                //
                // Clear data toggle, and flush twice (due to double buffering)
                //
                HWREG(USB_CSOL) = USB_CSOL_CLRDATATOG_M | USB_CSOL_FLUSHPACKET_M;
                HWREG(USB_CSOL) = USB_CSOL_FLUSHPACKET_M;

                //
                // USBCSOH
                //
                if((pEndpoint->bmAttributes & USB_EP_ATTR_TYPE_BM) == USB_EP_ATTR_ISO)
                {
                    //
                    // ISO flag
                    //
                    csRegValue |= USB_CSOH_ISO_M;    
                }
                if(pUsbInterfaceEpDblbufInfo->outMask & (1 << endpoint))
                {
                    //
                    // Double buffering
                    //
                    csRegValue |= USB_CSOH_OUTDBLBUF_M;    
                }
                HWREG(USB_CSOH) = csRegValue;

                //
                // Max transfer size
                //
                HWREG(USB_MAXO) = maxpRegValue;

                //
                // Endpoint status
                //
                usbfwData.pEpOutStatus[endpoint - 1] = EP_IDLE;
            }
            USBFW_SELECT_ENDPOINT(0);
        }
    }