881 lines
30 KiB
C
881 lines
30 KiB
C
/**************************************************************************//**
|
|
* Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved.
|
|
*
|
|
* http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt
|
|
*****************************************************************************/
|
|
|
|
#include "si_toolchain.h"
|
|
#include "efm8_usb.h"
|
|
#include <stdint.h>
|
|
#include <endian.h>
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Function Prototypes
|
|
|
|
static USB_Status_TypeDef ClearFeature(void);
|
|
static USB_Status_TypeDef GetConfiguration(void);
|
|
static USB_Status_TypeDef GetDescriptor(void);
|
|
static USB_Status_TypeDef GetInterface(void);
|
|
static USB_Status_TypeDef GetStatus(void);
|
|
static USB_Status_TypeDef SetAddress(void);
|
|
static USB_Status_TypeDef SetConfiguration(void);
|
|
static USB_Status_TypeDef SetFeature(void);
|
|
static USB_Status_TypeDef SetInterface(void);
|
|
static void USBD_ActivateAllEps(bool forceIdle);
|
|
static void EP0_Write(uint8_t *dat, uint16_t numBytes);
|
|
void SendEp0Stall(void);
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Global Variables
|
|
|
|
extern SI_SEGMENT_VARIABLE(myUsbDevice, USBD_Device_TypeDef, MEM_MODEL_SEG);
|
|
SI_SEGMENT_VARIABLE(txZero[2], uint8_t, SI_SEG_CODE);
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Static Global Variables
|
|
|
|
static uint16_t pStatus;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Chapter 9 Functions
|
|
|
|
/***************************************************************************//**
|
|
* @brief Processes Standard Request (Chapter 9 Command)
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
USB_Status_TypeDef USBDCH9_SetupCmd(void)
|
|
{
|
|
USB_Status_TypeDef status = USB_STATUS_OK;
|
|
|
|
switch (myUsbDevice.setup.bRequest)
|
|
{
|
|
case GET_STATUS:
|
|
status = GetStatus();
|
|
break;
|
|
|
|
case CLEAR_FEATURE:
|
|
status = ClearFeature();
|
|
break;
|
|
|
|
case SET_FEATURE:
|
|
status = SetFeature();
|
|
break;
|
|
|
|
case SET_ADDRESS:
|
|
status = SetAddress();
|
|
break;
|
|
|
|
case GET_DESCRIPTOR:
|
|
status = GetDescriptor();
|
|
break;
|
|
|
|
case GET_CONFIGURATION:
|
|
status = GetConfiguration();
|
|
break;
|
|
|
|
case SET_CONFIGURATION:
|
|
status = SetConfiguration();
|
|
break;
|
|
|
|
case GET_INTERFACE:
|
|
status = GetInterface();
|
|
break;
|
|
|
|
case SET_INTERFACE:
|
|
status = SetInterface();
|
|
break;
|
|
|
|
default:
|
|
status = USB_STATUS_REQ_ERR;
|
|
break;
|
|
}
|
|
|
|
// Reset index to 0 in case one of the above commands modified it
|
|
USB_SetIndex(0);
|
|
|
|
// If the command resulted in an error, send a procedural stall
|
|
if (status == USB_STATUS_REQ_ERR)
|
|
{
|
|
SendEp0Stall();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Clears the requested feature
|
|
* @details Supports CLEAR_FEATURE for Remote Wakeup and Endpoint Halt
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static USB_Status_TypeDef ClearFeature(void)
|
|
{
|
|
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
|
|
|
|
if (myUsbDevice.setup.wLength == 0)
|
|
{
|
|
switch (myUsbDevice.setup.bmRequestType.Recipient)
|
|
{
|
|
#if SLAB_USB_REMOTE_WAKEUP_ENABLED
|
|
case USB_SETUP_RECIPIENT_DEVICE:
|
|
if ((myUsbDevice.setup.wIndex == 0)
|
|
&& (myUsbDevice.setup.wValue == USB_FEATURE_DEVICE_REMOTE_WAKEUP)
|
|
&& (myUsbDevice.state >= USBD_STATE_ADDRESSED))
|
|
{
|
|
// Remote wakeup feature clear
|
|
myUsbDevice.remoteWakeupEnabled = false;
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
break;
|
|
#endif // SLAB_USB_REMOTE_WAKEUP_ENABLED
|
|
case USB_SETUP_RECIPIENT_ENDPOINT:
|
|
if (myUsbDevice.setup.wValue == USB_FEATURE_ENDPOINT_HALT)
|
|
{
|
|
// Device does not support halting endpoint 0, but do not return
|
|
// an error as this is a valid request
|
|
if (((myUsbDevice.setup.wIndex & ~USB_EP_DIR_IN) == 0)
|
|
&& (myUsbDevice.state >= USBD_STATE_ADDRESSED))
|
|
{
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
else if (((myUsbDevice.setup.wIndex & ~USB_SETUP_DIR_D2H) < SLAB_USB_NUM_EPS_USED)
|
|
&& (myUsbDevice.state == USBD_STATE_CONFIGURED))
|
|
{
|
|
retVal = USB_STATUS_OK;
|
|
USB_SetIndex((myUsbDevice.setup.wIndex & 0xFF) & ~USB_SETUP_DIR_D2H);
|
|
|
|
#if (SLAB_USB_EP1IN_USED || SLAB_USB_EP2IN_USED || SLAB_USB_EP3IN_USED)
|
|
if ((myUsbDevice.setup.wIndex & 0xFF) & USB_EP_DIR_IN)
|
|
{
|
|
USB_EpnInEndStallAndClearDataToggle();
|
|
}
|
|
#endif
|
|
#if (SLAB_USB_EP1OUT_USED || SLAB_USB_EP2OUT_USED || SLAB_USB_EP3OUT_USED)
|
|
if (((myUsbDevice.setup.wIndex & 0xFF) & USB_EP_DIR_IN) == 0)
|
|
{
|
|
USB_EpnOutEndStallAndClearDataToggle();
|
|
}
|
|
#endif
|
|
|
|
switch (myUsbDevice.setup.wIndex & 0xFF)
|
|
{
|
|
#if SLAB_USB_EP1OUT_USED
|
|
case (USB_EP_DIR_OUT | 1):
|
|
if (myUsbDevice.ep1out.state != D_EP_RECEIVING)
|
|
{
|
|
myUsbDevice.ep1out.state = D_EP_IDLE;
|
|
}
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP2OUT_USED
|
|
case (USB_EP_DIR_OUT | 2):
|
|
if (myUsbDevice.ep2out.state != D_EP_RECEIVING)
|
|
{
|
|
myUsbDevice.ep2out.state = D_EP_IDLE;
|
|
}
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP3OUT_USED
|
|
case (USB_EP_DIR_OUT | 3):
|
|
if (myUsbDevice.ep3out.state != D_EP_RECEIVING)
|
|
{
|
|
myUsbDevice.ep3out.state = D_EP_IDLE;
|
|
}
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP1IN_USED
|
|
case (USB_EP_DIR_IN | 1):
|
|
if (myUsbDevice.ep1in.state != D_EP_TRANSMITTING)
|
|
{
|
|
myUsbDevice.ep1in.state = D_EP_IDLE;
|
|
}
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP2IN_USED
|
|
case (USB_EP_DIR_IN | 2):
|
|
if (myUsbDevice.ep2in.state != D_EP_TRANSMITTING)
|
|
{
|
|
myUsbDevice.ep2in.state = D_EP_IDLE;
|
|
}
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP3IN_USED
|
|
case (USB_EP_DIR_IN | 3):
|
|
if (myUsbDevice.ep3in.state != D_EP_TRANSMITTING)
|
|
{
|
|
myUsbDevice.ep3in.state = D_EP_IDLE;
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Gets the current configuration value
|
|
* @details Zero means the device is not configured, a non-zero value
|
|
* is the configuration value of the configured device.
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static USB_Status_TypeDef GetConfiguration(void)
|
|
{
|
|
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
|
|
|
|
if ((myUsbDevice.setup.wIndex == 0)
|
|
&& (myUsbDevice.setup.wValue == 0)
|
|
&& (myUsbDevice.setup.wLength == 1)
|
|
&& (myUsbDevice.setup.bmRequestType.Direction == USB_SETUP_DIR_IN)
|
|
&& (myUsbDevice.setup.bmRequestType.Recipient == USB_SETUP_RECIPIENT_DEVICE))
|
|
{
|
|
if (myUsbDevice.state == USBD_STATE_ADDRESSED)
|
|
{
|
|
EP0_Write(txZero, 1);
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
else if (myUsbDevice.state == USBD_STATE_CONFIGURED)
|
|
{
|
|
EP0_Write(&myUsbDevice.configurationValue, 1);
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Sends the requested USB Descriptor
|
|
* @details Supports single or multiple languages (configured by
|
|
* @ref SLAB_USB_NUM_LANGUAGES).
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static USB_Status_TypeDef GetDescriptor(void)
|
|
{
|
|
#if (SLAB_USB_NUM_LANGUAGES > 1)
|
|
bool langSupported;
|
|
uint8_t lang;
|
|
#endif
|
|
|
|
uint8_t index;
|
|
uint16_t length = 0;
|
|
uint8_t *dat;
|
|
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
|
|
|
|
if (*((uint8_t *)&myUsbDevice.setup.bmRequestType) ==
|
|
(USB_SETUP_DIR_D2H | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE))
|
|
{
|
|
index = myUsbDevice.setup.wValue & 0xFF;
|
|
|
|
switch (myUsbDevice.setup.wValue >> 8)
|
|
{
|
|
case USB_DEVICE_DESCRIPTOR:
|
|
if (index != 0)
|
|
{
|
|
break;
|
|
}
|
|
dat = (uint8_t *)myUsbDevice.deviceDescriptor;
|
|
length = myUsbDevice.deviceDescriptor->bLength;
|
|
break;
|
|
|
|
case USB_CONFIG_DESCRIPTOR:
|
|
if (index != 0)
|
|
{
|
|
break;
|
|
}
|
|
dat = (uint8_t *)myUsbDevice.configDescriptor;
|
|
length = le16toh(myUsbDevice.configDescriptor->wTotalLength);
|
|
break;
|
|
|
|
case USB_STRING_DESCRIPTOR:
|
|
#if (SLAB_USB_NUM_LANGUAGES == 1)
|
|
|
|
dat = (uint8_t *)myUsbDevice.stringDescriptors[index];
|
|
|
|
// Index 0 is the language string. If SLAB_USB_NUM_LANGUAGES == 1, we
|
|
// know the length will be 4 and the format will be UTF16LE.
|
|
if (index == 0)
|
|
{
|
|
length = 4;
|
|
myUsbDevice.ep0String.encoding.type = USB_STRING_DESCRIPTOR_UTF16LE;
|
|
}
|
|
// Otherwise, verify the language is correct (either the value set as
|
|
// SLAB_USB_LANGUAGE in usbconfig.h, or 0).
|
|
else if ((myUsbDevice.setup.wIndex == 0) || (myUsbDevice.setup.wIndex == SLAB_USB_LANGUAGE))
|
|
{
|
|
// Verify the index is valid
|
|
if (index < myUsbDevice.numberOfStrings)
|
|
{
|
|
length = *(dat + USB_STRING_DESCRIPTOR_LENGTH);
|
|
myUsbDevice.ep0String.encoding.type = *(dat + USB_STRING_DESCRIPTOR_ENCODING);
|
|
dat += USB_STRING_DESCRIPTOR_LENGTH;
|
|
myUsbDevice.ep0String.encoding.init = true;
|
|
}
|
|
}
|
|
#elif (SLAB_USB_NUM_LANGUAGES > 1)
|
|
|
|
langSupported = false;
|
|
|
|
// Index 0 is the language.
|
|
if (index == 0)
|
|
{
|
|
dat = ((uint8_t *)myUsbDevice.stringDescriptors->languageArray[0][index]);
|
|
length = *((uint8_t *)dat);
|
|
myUsbDevice.ep0String.encoding.type = USB_STRING_DESCRIPTOR_UTF16LE;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, verify the language is one of the supported languages or 0.
|
|
for (lang = 0; lang < SLAB_USB_NUM_LANGUAGES; lang++)
|
|
{
|
|
if ((myUsbDevice.stringDescriptors->languageIDs[lang] == myUsbDevice.setup.wIndex)
|
|
|| (myUsbDevice.stringDescriptors->languageIDs[lang] == 0))
|
|
{
|
|
langSupported = true;
|
|
break;
|
|
}
|
|
}
|
|
if ((langSupported == true) && (index < myUsbDevice.numberOfStrings))
|
|
{
|
|
dat = ((uint8_t *)myUsbDevice.stringDescriptors->languageArray[lang][index]);
|
|
length = *(dat + USB_STRING_DESCRIPTOR_LENGTH);
|
|
myUsbDevice.ep0String.encoding.type = *(dat + USB_STRING_DESCRIPTOR_ENCODING);
|
|
dat += USB_STRING_DESCRIPTOR_LENGTH;
|
|
|
|
if (myUsbDevice.ep0String.encoding.type == USB_STRING_DESCRIPTOR_UTF16LE_PACKED)
|
|
{
|
|
myUsbDevice.ep0String.encoding.init = true;
|
|
}
|
|
else
|
|
{
|
|
myUsbDevice.ep0String.encoding.init = false;
|
|
}
|
|
}
|
|
}
|
|
#endif // ( SLAB_USB_NUM_LANGUAGES == 1 )
|
|
}
|
|
|
|
// If there is a descriptor to send, get the proper length, then call
|
|
// EP0_Write() to send.
|
|
if (length)
|
|
{
|
|
if (length > myUsbDevice.setup.wLength)
|
|
{
|
|
length = myUsbDevice.setup.wLength;
|
|
}
|
|
|
|
EP0_Write(dat, length);
|
|
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Sends the current interface alternate setting
|
|
* @details Sends 0x0000 if alternate interfaces are not supported.
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static USB_Status_TypeDef GetInterface(void)
|
|
{
|
|
uint16_t interface = myUsbDevice.setup.wIndex;
|
|
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
|
|
|
|
if ((interface < SLAB_USB_NUM_INTERFACES)
|
|
&& (myUsbDevice.setup.wLength == 1)
|
|
&& (myUsbDevice.setup.wValue == 0)
|
|
&& (*((uint8_t *)&myUsbDevice.setup.bmRequestType) ==
|
|
(USB_SETUP_DIR_D2H | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_INTERFACE)))
|
|
{
|
|
if (myUsbDevice.state == USBD_STATE_CONFIGURED)
|
|
{
|
|
#if (SLAB_USB_SUPPORT_ALT_INTERFACES)
|
|
// Return the alternate setting for the specified interface
|
|
EP0_Write(&myUsbDevice.interfaceAltSetting[interface], 1);
|
|
#else
|
|
// Alternate interfaces are not supported, so return 0x0000.
|
|
EP0_Write(&txZero, 1);
|
|
#endif
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Sends the requested Remote Wakeup, Self-Powered, or
|
|
* Endpoint Status
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static USB_Status_TypeDef GetStatus(void)
|
|
{
|
|
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
|
|
|
|
if ((myUsbDevice.setup.wLength == 2)
|
|
&& (myUsbDevice.setup.wValue == 0)
|
|
&& (myUsbDevice.setup.bmRequestType.Direction == USB_SETUP_DIR_IN)
|
|
&& (myUsbDevice.state >= USBD_STATE_ADDRESSED))
|
|
{
|
|
pStatus = htole16(0); // Default return value is 0x0000
|
|
|
|
switch (myUsbDevice.setup.bmRequestType.Recipient)
|
|
{
|
|
case USB_SETUP_RECIPIENT_DEVICE:
|
|
if (myUsbDevice.setup.wIndex == 0)
|
|
{
|
|
#if SLAB_USB_REMOTE_WAKEUP_ENABLED
|
|
// Remote wakeup feature status
|
|
if (myUsbDevice.remoteWakeupEnabled)
|
|
{
|
|
pStatus |= htole16(REMOTE_WAKEUP_ENABLED);
|
|
}
|
|
#endif // SLAB_USB_REMOTE_WAKEUP_ENABLED
|
|
|
|
#if SLAB_USB_IS_SELF_POWERED_CB
|
|
// Current self/bus power status
|
|
if (USBD_IsSelfPoweredCb())
|
|
{
|
|
pStatus |= htole16(DEVICE_IS_SELFPOWERED);
|
|
}
|
|
#elif (SLAB_USB_BUS_POWERED == 0)
|
|
pStatus |= htole16(DEVICE_IS_SELFPOWERED);
|
|
#endif // SLAB_USB_IS_SELF_POWERED_CB
|
|
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
break;
|
|
|
|
case USB_SETUP_RECIPIENT_INTERFACE:
|
|
if (myUsbDevice.setup.wIndex < SLAB_USB_NUM_INTERFACES)
|
|
{
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USB_SETUP_RECIPIENT_ENDPOINT:
|
|
// Device does not support halting endpoint 0, but do not give
|
|
// an error as this is a valid request
|
|
if (((myUsbDevice.setup.wIndex & ~USB_EP_DIR_IN) == 0)
|
|
&& (myUsbDevice.state == USBD_STATE_ADDRESSED))
|
|
{
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
else if (myUsbDevice.state == USBD_STATE_CONFIGURED)
|
|
{
|
|
switch (myUsbDevice.setup.wIndex & 0xFF)
|
|
{
|
|
#if SLAB_USB_EP1OUT_USED
|
|
case (USB_EP_DIR_OUT | 1):
|
|
if (myUsbDevice.ep1out.state == D_EP_HALT)
|
|
{
|
|
pStatus = htole16(1);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP2OUT_USED
|
|
case (USB_EP_DIR_OUT | 2):
|
|
if (myUsbDevice.ep2out.state == D_EP_HALT)
|
|
{
|
|
pStatus = htole16(1);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP3OUT_USED
|
|
case (USB_EP_DIR_OUT | 3):
|
|
if (myUsbDevice.ep3out.state == D_EP_HALT)
|
|
{
|
|
pStatus = htole16(1);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP1IN_USED
|
|
case (USB_EP_DIR_IN | 1):
|
|
if (myUsbDevice.ep1in.state == D_EP_HALT)
|
|
{
|
|
pStatus = htole16(1);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP2IN_USED
|
|
case (USB_EP_DIR_IN | 2):
|
|
if (myUsbDevice.ep2in.state == D_EP_HALT)
|
|
{
|
|
pStatus = htole16(1);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP3IN_USED
|
|
case (USB_EP_DIR_IN | 3):
|
|
if (myUsbDevice.ep3in.state == D_EP_HALT)
|
|
{
|
|
pStatus = htole16(1);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// If the command was valid, send the requested status.
|
|
if (retVal == USB_STATUS_OK)
|
|
{
|
|
EP0_Write((uint8_t *)&pStatus, 2);
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Sets the Address
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static USB_Status_TypeDef SetAddress(void)
|
|
{
|
|
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
|
|
|
|
if ((myUsbDevice.setup.wValue < 128)
|
|
&& (myUsbDevice.setup.wLength == 0)
|
|
&& (myUsbDevice.setup.bmRequestType.Recipient == USB_SETUP_RECIPIENT_DEVICE)
|
|
&& (myUsbDevice.setup.wIndex == 0))
|
|
{
|
|
// If the device is in the Default state and the address is non-zero, put
|
|
// the device in the Addressed state.
|
|
if (myUsbDevice.state == USBD_STATE_DEFAULT)
|
|
{
|
|
if (myUsbDevice.setup.wValue != 0)
|
|
{
|
|
USBD_SetUsbState(USBD_STATE_ADDRESSED);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
// If the device is already addressed and the address is zero, put the
|
|
// device in the Default state.
|
|
else if (myUsbDevice.state == USBD_STATE_ADDRESSED)
|
|
{
|
|
if (myUsbDevice.setup.wValue == 0)
|
|
{
|
|
USBD_SetUsbState(USBD_STATE_DEFAULT);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
|
|
// Set the new address if the request was valid.
|
|
if (retVal == USB_STATUS_OK)
|
|
{
|
|
USB_SetAddress(myUsbDevice.setup.wValue);
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Sets the Configuration
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static USB_Status_TypeDef SetConfiguration(void)
|
|
{
|
|
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
|
|
|
|
if (((myUsbDevice.setup.wValue >> 8) == 0)
|
|
&& (myUsbDevice.setup.bmRequestType.Recipient == USB_SETUP_RECIPIENT_DEVICE)
|
|
&& (myUsbDevice.setup.wLength == 0)
|
|
&& (myUsbDevice.setup.wIndex == 0))
|
|
{
|
|
// If the device is in the Addressed state and a valid Configuration value
|
|
// was sent, enter the Configured state.
|
|
if (myUsbDevice.state == USBD_STATE_ADDRESSED)
|
|
{
|
|
if ((myUsbDevice.setup.wValue == 0)
|
|
|| (myUsbDevice.setup.wValue == myUsbDevice.configDescriptor->bConfigurationValue))
|
|
{
|
|
myUsbDevice.configurationValue = myUsbDevice.setup.wValue;
|
|
if (myUsbDevice.setup.wValue == myUsbDevice.configDescriptor->bConfigurationValue)
|
|
{
|
|
USBD_ActivateAllEps(true);
|
|
USBD_SetUsbState(USBD_STATE_CONFIGURED);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
}
|
|
// If the device is in the Configured state and Configuration zero is sent,
|
|
// abort all transfer and enter the Addressed state.
|
|
else if (myUsbDevice.state == USBD_STATE_CONFIGURED)
|
|
{
|
|
if ((myUsbDevice.setup.wValue == 0)
|
|
|| (myUsbDevice.setup.wValue == myUsbDevice.configDescriptor->bConfigurationValue))
|
|
{
|
|
myUsbDevice.configurationValue = myUsbDevice.setup.wValue;
|
|
if (myUsbDevice.setup.wValue == 0)
|
|
{
|
|
USBD_SetUsbState(USBD_STATE_ADDRESSED);
|
|
USBD_AbortAllTransfers();
|
|
}
|
|
else
|
|
{
|
|
// Reenable device endpoints, will reset data toggles
|
|
USBD_ActivateAllEps(false);
|
|
}
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Sets the Remote Wakeup or Endpoint Halt Feature
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static USB_Status_TypeDef SetFeature(void)
|
|
{
|
|
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
|
|
|
|
if (myUsbDevice.setup.wLength == 0)
|
|
{
|
|
switch (myUsbDevice.setup.bmRequestType.Recipient)
|
|
{
|
|
#if SLAB_USB_REMOTE_WAKEUP_ENABLED
|
|
case USB_SETUP_RECIPIENT_DEVICE:
|
|
if ((myUsbDevice.setup.wIndex == 0) // ITF no. 0
|
|
&& (myUsbDevice.setup.wValue == USB_FEATURE_DEVICE_REMOTE_WAKEUP)
|
|
&& (myUsbDevice.state == USBD_STATE_CONFIGURED))
|
|
{
|
|
myUsbDevice.remoteWakeupEnabled = true;
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
break;
|
|
#endif // SLAB_USB_REMOTE_WAKEUP_ENABLED
|
|
case USB_SETUP_RECIPIENT_ENDPOINT:
|
|
// Device does not support halting endpoint 0, but do not return
|
|
// an error as this is a valid request
|
|
if (((myUsbDevice.setup.wIndex & ~USB_EP_DIR_IN) == 0)
|
|
&& (myUsbDevice.state >= USBD_STATE_ADDRESSED))
|
|
{
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
else if ((((myUsbDevice.setup.wIndex) & ~USB_SETUP_DIR_D2H) < SLAB_USB_NUM_EPS_USED)
|
|
&& (myUsbDevice.setup.wValue == USB_FEATURE_ENDPOINT_HALT)
|
|
&& (myUsbDevice.state == USBD_STATE_CONFIGURED))
|
|
{
|
|
retVal = USB_STATUS_OK;
|
|
USB_SetIndex((myUsbDevice.setup.wIndex & 0xFF) & ~USB_SETUP_DIR_D2H);
|
|
|
|
// Enable Stalls on the specified endpoint.
|
|
#if (SLAB_USB_EP1IN_USED || SLAB_USB_EP2IN_USED || SLAB_USB_EP3IN_USED)
|
|
if ((myUsbDevice.setup.wIndex & 0xFF) & USB_EP_DIR_IN)
|
|
{
|
|
USB_EpnInStall();
|
|
}
|
|
#endif
|
|
#if (SLAB_USB_EP1OUT_USED || SLAB_USB_EP2OUT_USED || SLAB_USB_EP3OUT_USED)
|
|
if (((myUsbDevice.setup.wIndex & 0xFF) & USB_EP_DIR_IN) == 0)
|
|
{
|
|
USB_EpnOutStall();
|
|
}
|
|
#endif
|
|
|
|
// Put the specified endpoint in the Halted state.
|
|
switch (myUsbDevice.setup.wIndex & 0xFF)
|
|
{
|
|
#if SLAB_USB_EP1OUT_USED
|
|
case (USB_EP_DIR_OUT | 1):
|
|
myUsbDevice.ep1out.state = D_EP_HALT;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP2OUT_USED
|
|
case (USB_EP_DIR_OUT | 2):
|
|
myUsbDevice.ep2out.state = D_EP_HALT;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP3OUT_USED
|
|
case (USB_EP_DIR_OUT | 3):
|
|
myUsbDevice.ep3out.state = D_EP_HALT;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP1IN_USED
|
|
case (USB_EP_DIR_IN | 1):
|
|
myUsbDevice.ep1in.state = D_EP_HALT;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP2IN_USED
|
|
case (USB_EP_DIR_IN | 2):
|
|
myUsbDevice.ep2in.state = D_EP_HALT;
|
|
break;
|
|
#endif
|
|
#if SLAB_USB_EP3IN_USED
|
|
case (USB_EP_DIR_IN | 3):
|
|
myUsbDevice.ep3in.state = D_EP_HALT;
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Sets the Interface and Alternate Interface (if supported)
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static USB_Status_TypeDef SetInterface(void)
|
|
{
|
|
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
|
|
uint8_t interface = (uint8_t)myUsbDevice.setup.wIndex;
|
|
uint8_t altSetting = (uint8_t)myUsbDevice.setup.wValue;
|
|
|
|
if ((interface < SLAB_USB_NUM_INTERFACES)
|
|
&& (myUsbDevice.state == USBD_STATE_CONFIGURED)
|
|
&& (myUsbDevice.setup.wLength == 0)
|
|
#if (SLAB_USB_SUPPORT_ALT_INTERFACES == 0)
|
|
&& (altSetting == 0)
|
|
#endif
|
|
&& (myUsbDevice.setup.bmRequestType.Recipient == USB_SETUP_RECIPIENT_INTERFACE))
|
|
{
|
|
#if (SLAB_USB_SUPPORT_ALT_INTERFACES)
|
|
if (USBD_SetInterfaceCb(interface, altSetting) == USB_STATUS_OK)
|
|
{
|
|
myUsbDevice.interfaceAltSetting[interface] = altSetting;
|
|
retVal = USB_STATUS_OK;
|
|
}
|
|
#else
|
|
#if (SLAB_USB_NUM_INTERFACES == 1)
|
|
// Reset data toggles on EP's
|
|
USBD_ActivateAllEps(false);
|
|
#endif // ( SLAB_USB_NUM_INTERFACES == 1 )
|
|
retVal = USB_STATUS_OK;
|
|
#endif // ( SLAB_USB_SUPPORT_ALT_INTERFACES )
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Utility Functions
|
|
|
|
/***************************************************************************//**
|
|
* @brief Enables all endpoints for data transfers
|
|
* @return Status of request (type @ref USB_Status_TypeDef)
|
|
* @note This function takes no parameters, but it uses the setup command
|
|
* stored in @ref myUsbDevice.setup.
|
|
******************************************************************************/
|
|
static void USBD_ActivateAllEps(bool forceIdle)
|
|
{
|
|
if (forceIdle == true)
|
|
{
|
|
#if SLAB_USB_EP1IN_USED
|
|
myUsbDevice.ep1in.state = D_EP_IDLE;
|
|
#endif
|
|
#if SLAB_USB_EP2IN_USED
|
|
myUsbDevice.ep2in.state = D_EP_IDLE;
|
|
#endif
|
|
#if SLAB_USB_EP3IN_USED
|
|
myUsbDevice.ep3in.state = D_EP_IDLE;
|
|
#endif
|
|
#if SLAB_USB_EP1OUT_USED
|
|
myUsbDevice.ep1out.state = D_EP_IDLE;
|
|
#endif
|
|
#if SLAB_USB_EP2OUT_USED
|
|
myUsbDevice.ep2out.state = D_EP_IDLE;
|
|
#endif
|
|
#if SLAB_USB_EP3OUT_USED
|
|
myUsbDevice.ep3out.state = D_EP_IDLE;
|
|
#endif
|
|
}
|
|
|
|
#if SLAB_USB_EP1IN_USED
|
|
USB_ActivateEp(1, // ep
|
|
SLAB_USB_EP1IN_MAX_PACKET_SIZE, // packetSize
|
|
1, // inDir
|
|
SLAB_USB_EP1OUT_USED, // splitMode
|
|
0); // isoMod
|
|
#endif // SLAB_USB_EP1IN_USED
|
|
#if SLAB_USB_EP2IN_USED
|
|
USB_ActivateEp(2, // ep
|
|
SLAB_USB_EP2IN_MAX_PACKET_SIZE, // packetSize
|
|
1, // inDir
|
|
SLAB_USB_EP2OUT_USED, // splitMode
|
|
0); // isoMod
|
|
#endif // SLAB_USB_EP2IN_USED
|
|
#if SLAB_USB_EP3IN_USED
|
|
USB_ActivateEp(3, // ep
|
|
SLAB_USB_EP3IN_MAX_PACKET_SIZE, // packetSize
|
|
1, // inDir
|
|
SLAB_USB_EP3OUT_USED, // splitMode
|
|
(SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC)); // isoMod
|
|
#endif // SLAB_USB_EP3IN_USED
|
|
#if SLAB_USB_EP1OUT_USED
|
|
USB_ActivateEp(1, // ep
|
|
SLAB_USB_EP1OUT_MAX_PACKET_SIZE, // packetSize
|
|
0, // inDir
|
|
SLAB_USB_EP1IN_USED, // splitMode
|
|
0); // isoMod
|
|
#endif // SLAB_USB_EP1OUT_USED
|
|
#if SLAB_USB_EP2OUT_USED
|
|
USB_ActivateEp(2, // ep
|
|
SLAB_USB_EP2OUT_MAX_PACKET_SIZE, // packetSize
|
|
0, // inDir
|
|
SLAB_USB_EP2IN_USED, // splitMode
|
|
0); // isoMod
|
|
#endif // SLAB_USB_EP2OUT_USED
|
|
#if SLAB_USB_EP3OUT_USED
|
|
USB_ActivateEp(3, // ep
|
|
SLAB_USB_EP3OUT_MAX_PACKET_SIZE, // packetSize
|
|
0, // inDir
|
|
SLAB_USB_EP3IN_USED, // splitMode
|
|
(SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC)); // isoMod
|
|
#endif // SLAB_USB_EP1OUT_USED
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Sets up an Endpoint 0 Write
|
|
* @param dat
|
|
* Data to transmit on Endpoint 0
|
|
* @param numBytes
|
|
* Number of bytes to transmit on Endpoint 0
|
|
******************************************************************************/
|
|
static void EP0_Write(uint8_t *dat, uint16_t numBytes)
|
|
{
|
|
if (myUsbDevice.ep0.state == D_EP_IDLE)
|
|
{
|
|
myUsbDevice.ep0.buf = (uint8_t *)dat;
|
|
myUsbDevice.ep0.remaining = numBytes;
|
|
myUsbDevice.ep0.state = D_EP_TRANSMITTING;
|
|
myUsbDevice.ep0.misc.c = 0;
|
|
}
|
|
}
|