Skip to content
Permalink
7bdb68e5a1
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
2853 lines (2480 sloc) 105 KB
/***************************************************************************//**
* \file cy_usb_dev.c
* \version 2.10
*
* Provides API implementation of the USBFS device middleware.
*
********************************************************************************
* \copyright
* (c) 2018-2021, Cypress Semiconductor Corporation (an Infineon company) or
* an affiliate of Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#include "cy_usb_dev.h"
#if (defined(CY_IP_MXUSBFS) || defined(CY_IP_M0S8USBDSS))
/*******************************************************************************
* Internal Macro
*******************************************************************************/
#define GET_CFG_WORD(addr) CY_USB_DEV_GET_CFG_WORD(addr)
#define VAL2IDX(val) ( (uint32_t) (val) - 1U)
#define GET_UINT16(lsb, msb) ((lsb) | (uint16_t) (((uint16_t) (msb)) << 8UL))
/* Decode setup request bmRequestType */
#define SETUP_RQST_RCPT_Pos (0)
#define SETUP_RQST_RCPT_Msk (0x1FU)
#define SETUP_RQST_TYPE_Pos (5)
#define SETUP_RQST_TYPE_Msk (0x60u)
#define SETUP_RQST_DIR_Pos (7)
#define SETUP_RQST_DIR_Msk (0x80U)
/* Setup request layout in the buffer */
#define SETUP_RQST_POS (0)
#define SETUP_RQST_TYPE_POS (1)
#define SETUP_VALUE_LSB_POS (2)
#define SETUP_VALUE_MSB_POS (3)
#define SETUP_INDEX_LSB_POS (4)
#define SETUP_INDEX_MSB_POS (5)
#define SETUP_LENGTH_LSB_POS (6)
#define SETUP_LENGTH_MSB_POS (7)
/* Field position in the descriptors */
#define CONFIG_DESCR_LENGTH_LSB_POS (2) /* Configuration descriptor length (LSB) byte position */
#define CONFIG_DESCR_LENGTH_MSB_POS (3) /* Configuration descriptor length (MSB) byte position */
#define BOS_DESCR_LENGTH_LSB_POS (2) /* BOS descriptor length (LSB) byte position */
#define BOS_DESCR_LENGTH_MSB_POS (3) /* BOS descriptor length (MSB) byte position */
#define CONFIG_DESCR_ATTRIB_POS (7) /* Configuration descriptor attribute byte position */
#define DEVICE_DESCR_ISN_STRING_POS (16) /* Position of Serial Number in Device Descriptor */
#define STRING_DESCR_LENGTH_POS (0) /* Position inside string descriptor where length is stored */
#define STRING_DESCR_TYPE_POS (1) /* Position inside string descriptor where type is stored */
/* bmAttributes in configuration descriptor */
#define CONFIG_ATTR_SELF_POWERED_MASK (0x40U) /* Configuration attribute Self-Powered mask */
#define CONFIG_ATTR_REMOTE_WAKEUP_MASK (0x20U) /* Configuration attribute Remote Wakeup mask */
/* Fixed string descriptor indexes */
#define STRING_LANGID_INDEX (0U)
#define STRING_IMSOS_INDEX (0xEEU)
#define EXT_OS_DESC_LENGTH_BYTE0_POS (0x0U)
#define EXT_OS_DESC_LENGTH_BYTE1_POS (0x1U)
#define EXT_OS_DESC_LENGTH_BYTE2_POS (0x2U)
#define EXT_OS_DESC_LENGTH_BYTE3_POS (0x3U)
/*******************************************************************************
* Static Functions Prototypes
*******************************************************************************/
static int32_t HandleTimeout(int32_t milliseconds);
#if defined(CY_IP_MXUSBFS)
static void InitSerialNumberString(cy_stc_usb_dev_context_t *context);
#endif /* defined(CY_IP_MXUSBFS) */
static cy_en_usb_dev_status_t ConvertEndpointStateToStatus(cy_en_usb_dev_ep_state_t epState);
static void BusResetCallback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext);
static void Ep0SetupCallback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext);
static void Ep0InCallback (USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext);
static void Ep0OutCallback (USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext);
static cy_en_usb_dev_status_t HandleSetup(cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t HandleIn (cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t HandleOut (cy_stc_usb_dev_context_t *context);
static void DecodeSetupPacket (uint8_t const *data, cy_stc_usb_dev_setup_packet_t *packet);
static cy_en_usb_dev_status_t HandleStandardRequests (cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t GetDescriptorRequest (cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t GetConfigurationRequest(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t GetInterfaceRequest (cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t GetStatusRequest (cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t const *context);
static cy_en_usb_dev_status_t ClearFeatureRequest (cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t SetAddressRequest (cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t SetConfigurationRequest(cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t SetInterfaceRequest (cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t SetFeatureRequest (cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t HandleClassRequests(cy_stc_usb_dev_class_ll_item_t *curItem,
cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t HandleClassRequestsCompleted(cy_stc_usb_dev_class_ll_item_t *curItem,
cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t GetExtOsStringDescriptors(cy_stc_usb_dev_ms_os_string_t const *msOsString,
cy_stc_usb_dev_control_transfer_t *transfer);
static cy_en_usb_dev_status_t HandleVendorRequests(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t HandleVendorRequestsCompleted(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t CallSetInterfaceCallbacks(uint32_t interface,
uint32_t alternate,
cy_stc_usb_dev_class_ll_item_t *curItem,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t ConfigureDataEndpoints(uint32_t config, cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t InterfaceRemoveDataEndpoints(uint32_t numEndpoints,
cy_stc_usb_dev_endpoint_t const * const *epsPool,
cy_stc_usb_dev_context_t *context);
static cy_en_usb_dev_status_t InterfaceAddDataEndpoints(uint32_t numEndpoints,
cy_stc_usb_dev_endpoint_t const * const *epsPool,
cy_stc_usb_dev_context_t *context);
/*******************************************************************************
* Function Name: Cy_USB_Dev_Init
****************************************************************************//**
*
* Initialize the USB Device stack and underneath USBFS hardware driver.
*
* \param base
* The pointer to the USBFS instance.
*
* \param drvConfig
* The pointer to the USBFS driver configuration structure.
*
* \param drvContext
* The pointer to the USBFS driver context structure allocated by the user.
* The structure is used during the USBFS driver operation for internal
* configuration and data retention. The user must not modify anything in
* this structure.
*
* \param device
* The pointer to the device structure \ref cy_stc_usb_dev_device_t.
*
* \param config
* The pointer to the driver configuration structure \ref cy_stc_usb_dev_config_t.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for
* internal configuration and data retention. The user must not modify anything
* in this structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
* \note
* The configuration of USB clocks, pins, and interrupts is not handled by this
* function and must be done on the application level.
*
*******************************************************************************/
cy_en_usb_dev_status_t Cy_USB_Dev_Init(USBFS_Type *base,
struct cy_stc_usbfs_dev_drv_config const *drvConfig,
struct cy_stc_usbfs_dev_drv_context *drvContext,
cy_stc_usb_dev_device_t const *device,
cy_stc_usb_dev_config_t const *config,
cy_stc_usb_dev_context_t *context)
{
/* Input parameters verification */
if ((NULL == device) || (NULL == config) || (NULL == context) ||
(NULL == base) || (NULL == drvConfig) || (NULL == drvContext))
{
return CY_USB_DEV_BAD_PARAM;
}
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_DRV_HW_ERROR;
/* Store driver information in device */
context->drvBase = base;
context->drvContext = drvContext;
/* Set default state */
context->state = CY_USB_DEV_DISABLED;
context->configuration = 0U;
context->classRoot = NULL;
/* Configure endpoint 0 buffer */
context->ControlTransfer.buffer = config->ep0Buffer;
context->ControlTransfer.bufferSize = config->ep0BufferSize;
/* Store link to descriptors */
context->devDescriptors = device;
/* Initialize delay function */
context->handleTimeout = &HandleTimeout;
/* Initialize event callback */
context->eventsCallback = NULL;
#if defined(CY_IP_MXUSBFS)
/* Initialize serial string descriptor and set pointer */
InitSerialNumberString(context);
#endif /* defined(CY_IP_MXUSBFS) */
context->getSerialNumString = NULL;
/* Initialize vendor-specific callbacks */
context->vndRequestReceived = NULL;
context->vndRequestCompleted = NULL;
/* Link driver and device context */
Cy_USBFS_Dev_Drv_SetDevContext(base, context, drvContext);
/* Configure driver */
retStatus = (cy_en_usb_dev_status_t) Cy_USBFS_Dev_Drv_Init(base, drvConfig, drvContext);
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Hook device handlers to be called by driver */
Cy_USBFS_Dev_Drv_RegisterServiceCallback(base, CY_USB_DEV_BUS_RESET, &BusResetCallback, drvContext);
Cy_USBFS_Dev_Drv_RegisterServiceCallback(base, CY_USB_DEV_EP0_SETUP, &Ep0SetupCallback, drvContext);
Cy_USBFS_Dev_Drv_RegisterServiceCallback(base, CY_USB_DEV_EP0_IN, &Ep0InCallback, drvContext);
Cy_USBFS_Dev_Drv_RegisterServiceCallback(base, CY_USB_DEV_EP0_OUT, &Ep0OutCallback, drvContext);
}
return retStatus;
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_DeInit
****************************************************************************//**
*
* De-initialize the USB Device stack and underneath hardware driver.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
*******************************************************************************/
void Cy_USB_Dev_DeInit(cy_stc_usb_dev_context_t *context)
{
Cy_USBFS_Dev_Drv_DeInit(context->drvBase, context->drvContext);
/* IDe-initialize all callbacks */
context->classRoot = NULL;
context->eventsCallback = NULL;
context->getSerialNumString = NULL;
context->vndRequestReceived = NULL;
context->vndRequestCompleted = NULL;
}
/*******************************************************************************
* Function Name: HandleTimeout
****************************************************************************//**
*
* Waits for 1 millisecond and returns updated number of milliseconds that remain
* to wait before timeout expires.
*
* \param milliseconds
* Number of milliseconds that remain to wait before timeout expires.
*
* \return
* Updated number of milliseconds remain to wait.
*
*******************************************************************************/
static int32_t HandleTimeout(int32_t milliseconds)
{
Cy_SysLib_Delay(1U); /* Wait for 1 millisecond */
return (milliseconds - 1);
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_Connect
****************************************************************************//**
*
* Enables pull-up on D+ (hardware supports only full-speed device) line to
* signal USB Device connection on USB Bus.
*
* \param blocking
* Wait until device is configured.
*
* \param timeout
* Defines in milliseconds the time for which this function can block.
* If that time expires, the USB Device is disconnected and the function returns.
* To wait forever, pass \ref CY_USB_DEV_WAIT_FOREVER.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
*******************************************************************************/
cy_en_usb_dev_status_t Cy_USB_Dev_Connect(bool blocking, int32_t timeout, cy_stc_usb_dev_context_t *context)
{
/* Returns SUCCESS except timeout when timeout is used */
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_SUCCESS;
context->state = CY_USB_DEV_POWERED;
Cy_USBFS_Dev_Drv_Enable(context->drvBase, context->drvContext);
if (blocking)
{
if (CY_USB_DEV_WAIT_FOREVER == timeout)
{
/* Wait until device is configured */
while (CY_USB_DEV_CONFIGURED != context->state)
{
(void) context->handleTimeout(timeout);
}
}
else
{
/* Wait until device is configured or timeout */
while ((CY_USB_DEV_CONFIGURED != context->state) && (timeout > 0))
{
timeout = context->handleTimeout(timeout);
}
/* Timeout expired disconnect USB Device */
if (0 == timeout)
{
Cy_USB_Dev_Disconnect(context);
retStatus = CY_USB_DEV_TIMEOUT;
}
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_Disconnect
****************************************************************************//**
*
* Disables pull-up on D+ (hardware supports only full-speed device) line to
* signal USB Device disconnection on USB Bus.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
*******************************************************************************/
void Cy_USB_Dev_Disconnect(cy_stc_usb_dev_context_t *context)
{
Cy_USBFS_Dev_Drv_Disable(context->drvBase, context->drvContext);
/* Set device in the default state */
context->state = CY_USB_DEV_DISABLED;
context->configuration = 0U;
}
/*******************************************************************************
* Function Name: ConvertEndpointStateToStatus
****************************************************************************//**
*
* Converts endpoint state to the USB Device status code.
*
* \param epState
* Endpoint state cy_en_usb_dev_ep_state_t.
* The state CY_USB_DEV_EP_IDLE converted to \ref CY_USB_DEV_DRV_HW_DISABLED
* to indicate that current endpoint configuration was changed.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
static cy_en_usb_dev_status_t ConvertEndpointStateToStatus(cy_en_usb_dev_ep_state_t epState)
{
cy_en_usb_dev_status_t retStatus;
switch(epState)
{
case CY_USB_DEV_EP_COMPLETED:
retStatus = CY_USB_DEV_SUCCESS;
break;
case CY_USB_DEV_EP_PENDING:
retStatus = CY_USB_DEV_DRV_HW_BUSY;
break;
case CY_USB_DEV_EP_IDLE: /* Endpoint configuration is changed */
case CY_USB_DEV_EP_STALLED:
retStatus = CY_USB_DEV_DRV_HW_DISABLED;
break;
case CY_USB_DEV_EP_INVALID:
case CY_USB_DEV_EP_DISABLED:
retStatus = CY_USB_DEV_BAD_PARAM;
break;
default:
retStatus = CY_USB_DEV_BAD_PARAM;
break;
}
return retStatus;
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_AbortEpTransfer
****************************************************************************//**
*
* Aborts pending read or write endpoint operation.
* If there is any bus activity after abort operation requested the function
* waits for its completion or timeout. The timeout is time to transfer
* bulk or interrupt packet of maximum playload size. If this bus activity is
* a transfer to the aborting endpoint the received data is lost and endpoint
* transfer completion callbacks is not invoked.
* After function returns new read or write endpoint operation can be submitted.
*
* \param endpoint
* The data endpoint number.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
* \note
* The abort operation is not supported for ISOC endpoints because
* these endpoints do not have handshake and are always accessible by the
* USB Host. Therefore, abort can cause unexpected behavior.
*
*******************************************************************************/
cy_en_usb_dev_status_t Cy_USB_Dev_AbortEpTransfer(uint32_t endpoint,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus;
/* Request abort operation, on exit abort complete */
retStatus = (cy_en_usb_dev_status_t) Cy_USBFS_Dev_Drv_Abort(context->drvBase, endpoint, context->drvContext);
if (CY_USB_DEV_SUCCESS != retStatus)
{
retStatus = CY_USB_DEV_DRV_HW_ERROR;
}
return retStatus;
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_StartReadEp
****************************************************************************//**
*
* Start a reading on a certain endpoint.
*
* \param endpoint
* The OUT data endpoint number.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
* \note
* The read is not allowed for OUT endpoints after SET_CONFIGURATION or
* SET_INTERFACE request therefore this function must be called before reading data
* from OUT endpoints.
*
*******************************************************************************/
cy_en_usb_dev_status_t Cy_USB_Dev_StartReadEp(uint32_t endpoint, cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus;
cy_en_usb_dev_ep_state_t epState;
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
/* Check that endpoint is ready for read operation */
if ((CY_USB_DEV_EP_IDLE == epState) || (CY_USB_DEV_EP_COMPLETED == epState))
{
/* Enable endpoint to be written by host */
Cy_USBFS_Dev_Drv_EnableOutEndpoint(context->drvBase, endpoint, context->drvContext);
retStatus = CY_USB_DEV_SUCCESS;
}
else
{
/* Use endpoint state to get status */
retStatus = ConvertEndpointStateToStatus(epState);
}
return retStatus;
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_ReadEpBlocking
****************************************************************************//**
*
* Read data received from USB Host from a certain endpoint. Before calling
* this function, \ref Cy_USB_Dev_StartReadEp must be called.
* This function is blocking and returns after successful USB Host transfer,
* or an error or timeout occurred.
*
* \param endpoint
* The OUT data endpoint number.
*
* \param buffer
* The pointer to buffer that stores data that was read. \n
* Allocate buffer using \ref CY_USB_DEV_ALLOC_ENDPOINT_BUFFER macro to make
* it USBFS driver configuration independent (See \ref group_usb_dev_ep_buf_alloc
* for more information).
*
* \param size
* The number of bytes to read.
* This value must be less or equal to endpoint maximum packet size.
*
* \param actSize
* The number of bytes that were actually read.
*
* \param timeout
* Defines in milliseconds the time for which this function can block.
* If that time expires the function returns.
* To wait forever pass \ref CY_USB_DEV_WAIT_FOREVER.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
cy_en_usb_dev_status_t Cy_USB_Dev_ReadEpBlocking(uint32_t endpoint, uint8_t *buffer,
uint32_t size, uint32_t *actSize, int32_t timeout,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_SUCCESS;
cy_en_usb_dev_ep_state_t epState;
/* Get endpoint state before check it */
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
if (CY_USB_DEV_WAIT_FOREVER == timeout)
{
/* Wait until transfer is completed */
while (CY_USB_DEV_EP_PENDING == epState)
{
(void) context->handleTimeout(timeout);
/* Update endpoint state */
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
}
}
else
{
/* Wait until transfer is completed or for timeout */
while ((CY_USB_DEV_EP_PENDING == epState) && (timeout > 0))
{
timeout = context->handleTimeout(timeout);
/* Update endpoint state */
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
}
/* Timeout expired */
if (0 == timeout)
{
/* Abort write operation */
(void) Cy_USB_Dev_AbortEpTransfer(endpoint, context);
retStatus = CY_USB_DEV_TIMEOUT;
}
}
/* Clear actual number of read bytes */
*actSize = 0U;
/* Read data from endpoint buffer after completion */
if (CY_USB_DEV_EP_COMPLETED == epState)
{
retStatus = (cy_en_usb_dev_status_t)
Cy_USBFS_Dev_Drv_ReadOutEndpoint(context->drvBase,
endpoint, buffer, size, actSize, context->drvContext);
if (CY_USB_DEV_SUCCESS != retStatus)
{
retStatus = CY_USB_DEV_DRV_HW_ERROR;
}
}
if ((CY_USB_DEV_TIMEOUT != retStatus) && (CY_USB_DEV_DRV_HW_ERROR != retStatus))
{
/* Use endpoint state to get status */
retStatus = ConvertEndpointStateToStatus(epState);
}
return retStatus;
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_ReadEpNonBlocking
****************************************************************************//**
*
* Read data received from USB Host from a certain endpoint. Before calling
* this function, \ref Cy_USB_Dev_StartReadEp must be called.
*
* \param endpoint
* The OUT data endpoint number.
*
* \param buffer
* The pointer to buffer that stores data that was read. \n
* Allocate buffer using \ref CY_USB_DEV_ALLOC_ENDPOINT_BUFFER macro to make
* it USBFS driver configuration independent (See \ref group_usb_dev_ep_buf_alloc
* for more information).
*
* \param size
* The number of bytes to read.
* This value must be less than or equal to endpoint maximum packet size.
*
* \param actSize
* The number of bytes that were actually read.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
cy_en_usb_dev_status_t Cy_USB_Dev_ReadEpNonBlocking(uint32_t endpoint, uint8_t *buffer,
uint32_t size, uint32_t *actSize, cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus;
cy_en_usb_dev_ep_state_t epState;
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
/* Read data from endpoint buffer after completion */
if (CY_USB_DEV_EP_COMPLETED == epState)
{
retStatus = (cy_en_usb_dev_status_t)
Cy_USBFS_Dev_Drv_ReadOutEndpoint(context->drvBase,
endpoint, buffer, size, actSize, context->drvContext);
if (CY_USB_DEV_SUCCESS != retStatus)
{
retStatus = CY_USB_DEV_DRV_HW_ERROR;
}
}
else
{
/* Clear actual number of read bytes */
*actSize = 0U;
/* Use endpoint state to get status */
retStatus = ConvertEndpointStateToStatus(epState);
}
return retStatus;
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_WriteEpBlocking
****************************************************************************//**
*
* Write data to be transferred to USB Host from a certain endpoint.
* This function is blocking and returns after successful USB Host transfer,
* or an error or timeout occurred.
*
* \param endpoint
* The IN data endpoint number.
*
* \param buffer
* The pointer to the buffer containing data bytes to write. \n
* Allocate buffer using \ref CY_USB_DEV_ALLOC_ENDPOINT_BUFFER macro to make
* it USBFS driver configuration independent (See \ref group_usb_dev_ep_buf_alloc
* for more information).
*
* \param size
* The number of bytes to write.
* This value must be less than or equal to endpoint maximum packet size.
*
* \param timeout
* Defines in milliseconds the time for which this function can block.
* If that time expires, the function returns.
* To wait forever, pass \ref CY_USB_DEV_WAIT_FOREVER.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
cy_en_usb_dev_status_t Cy_USB_Dev_WriteEpBlocking(uint32_t endpoint, uint8_t const *buffer,
uint32_t size, int32_t timeout, cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_BAD_PARAM;
cy_en_usb_dev_ep_state_t epState;
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
/* Check that endpoint is ready for write operation */
if ((CY_USB_DEV_EP_IDLE == epState) || (CY_USB_DEV_EP_COMPLETED == epState))
{
retStatus = (cy_en_usb_dev_status_t) Cy_USBFS_Dev_Drv_LoadInEndpoint(context->drvBase,
endpoint, buffer, size, context->drvContext);
/* Check endpoint load status */
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Update endpoint state after load operation */
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
if (CY_USB_DEV_WAIT_FOREVER == timeout)
{
/* Wait until transfer is completed */
while (CY_USB_DEV_EP_PENDING == epState)
{
(void) context->handleTimeout(timeout);
/* Update endpoint state */
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
}
}
else
{
/* Wait until transfer is completed or for timeout */
while ((CY_USB_DEV_EP_PENDING == epState) && (timeout > 0))
{
timeout = context->handleTimeout(timeout);
/* Update endpoint state */
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
}
/* Timeout expired */
if (0 == timeout)
{
/* Abort write operation */
(void) Cy_USB_Dev_AbortEpTransfer(endpoint, context);
retStatus = CY_USB_DEV_TIMEOUT;
}
}
}
else
{
retStatus = CY_USB_DEV_DRV_HW_ERROR;
}
}
if ((CY_USB_DEV_TIMEOUT != retStatus) && (CY_USB_DEV_DRV_HW_ERROR != retStatus))
{
/* Use endpoint state to get status */
retStatus = ConvertEndpointStateToStatus(epState);
}
return retStatus;
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_WriteEpNonBlocking
****************************************************************************//**
*
* Write data to be transferred to USB Host from a certain endpoint.
*
* \param endpoint
* The IN data endpoint number.
*
* \param buffer
* The pointer to the buffer containing data bytes to write. \n
* Allocate buffer using \ref CY_USB_DEV_ALLOC_ENDPOINT_BUFFER macro to make
* it USBFS driver configuration independent (See \ref group_usb_dev_ep_buf_alloc
* for more information).
*
* \param size
* The number of bytes to write.
* This value must be less than or equal to endpoint maximum packet size.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
cy_en_usb_dev_status_t Cy_USB_Dev_WriteEpNonBlocking(uint32_t endpoint, uint8_t const *buffer,
uint32_t size, cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus;
cy_en_usb_dev_ep_state_t epState;
epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
/* Check that endpoint is ready for operation */
if ((CY_USB_DEV_EP_IDLE == epState) || (CY_USB_DEV_EP_COMPLETED == epState))
{
retStatus = (cy_en_usb_dev_status_t)
Cy_USBFS_Dev_Drv_LoadInEndpoint(context->drvBase,
endpoint, buffer, size, context->drvContext);
/* Write data into the endpoint buffer */
if (CY_USB_DEV_SUCCESS != retStatus)
{
retStatus = CY_USB_DEV_DRV_HW_ERROR;
}
}
else
{
retStatus = ConvertEndpointStateToStatus(epState);
}
return retStatus;
}
/*******************************************************************************
* Function Name: BusResetCallback
****************************************************************************//**
*
* Handles Bus Reset interrupt.
*
* \param base
* The pointer to the USBFS instance.
*
* \param drvContext
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
*******************************************************************************/
static void BusResetCallback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext)
{
/* Get device context from the driver context */
cy_stc_usb_dev_context_t *context = (cy_stc_usb_dev_context_t *) Cy_USBFS_Dev_Drv_GetDevContext(base, drvContext);
/* Get linked list of classes */
cy_stc_usb_dev_class_ll_item_t *curItem = context->classRoot;
/* Set device in the DEFAULT state */
context->state = CY_USB_DEV_DEFAULT;
context->status = 0U; /* Reset remote wakeup and power state */
context->configuration = 0U;
/* Notify Bus Reset event for device instance */
if (NULL != context->eventsCallback)
{
/* Input parameters are zeros. Ignore return for Bus Reset event. */
(void) context->eventsCallback(CY_USB_DEV_EVENT_BUS_RESET, 0UL, 0UL, context);
}
/* Notify Bus Reset event for all class instances */
while (NULL != curItem)
{
if (NULL != curItem->classObj->busReset)
{
/* Execute callback */
curItem->classObj->busReset(curItem->classData, context);
}
/* Move to next element */
curItem = curItem->next;
}
}
/*******************************************************************************
* Function Name: DecodeSetupPacket
****************************************************************************//**
*
* Decodes setup packet (populates \ref cy_stc_usb_dev_setup_packet_t).
*
* \param data
* The pointer to buffer with setup packet (raw data).
*
* \param packet
* The pointer to structure that holds setup packet.
*
*******************************************************************************/
static void DecodeSetupPacket(uint8_t const *data, cy_stc_usb_dev_setup_packet_t *packet)
{
/* Fill elements of setup packet structure from raw data */
packet->bmRequestType.direction = (uint8_t) _FLD2VAL(SETUP_RQST_DIR, data[SETUP_RQST_POS]);
packet->bmRequestType.type = (uint8_t) _FLD2VAL(SETUP_RQST_TYPE, data[SETUP_RQST_POS]);
packet->bmRequestType.recipient = (uint8_t) _FLD2VAL(SETUP_RQST_RCPT, data[SETUP_RQST_POS]);
packet->bRequest = (uint8_t) data[SETUP_RQST_TYPE_POS];
packet->wValue = GET_UINT16(data[SETUP_VALUE_LSB_POS], data[SETUP_VALUE_MSB_POS]);
packet->wIndex = GET_UINT16(data[SETUP_INDEX_LSB_POS], data[SETUP_INDEX_MSB_POS]);
packet->wLength = GET_UINT16(data[SETUP_LENGTH_LSB_POS], data[SETUP_LENGTH_MSB_POS]);
}
/*******************************************************************************
* Function Name: HandleSetup
****************************************************************************//**
*
* Handles setup packet received event (generated from Endpoint 0 Interrupt).
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
static cy_en_usb_dev_status_t HandleSetup(cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
cy_stc_usb_dev_control_transfer_t *transfer = &context->ControlTransfer;
/* Get setup packet from hardware */
Cy_USBFS_Dev_Drv_Ep0GetSetup(context->drvBase, transfer->buffer, context->drvContext);
/* Decode setup packet */
DecodeSetupPacket(transfer->buffer, &transfer->setup);
/* Prepare for new transfer */
transfer->ptr = NULL;
transfer->remaining = 0U;
transfer->size = 0U;
transfer->direction = transfer->setup.bmRequestType.direction;
transfer->zlp = false;
transfer->notify = false;
/* Handle Setup request depends on type */
switch (transfer->setup.bmRequestType.type)
{
case CY_USB_DEV_STANDARD_TYPE:
case CY_USB_DEV_CLASS_TYPE:
{
if (CY_USB_DEV_STANDARD_TYPE == transfer->setup.bmRequestType.type)
{
/* Handle Standard requests */
retStatus = HandleStandardRequests(transfer, context);
}
/* Try handle by Class requests handler */
if (CY_USB_DEV_SUCCESS != retStatus)
{
retStatus = HandleClassRequests(context->classRoot, transfer, context);
}
}
break;
case CY_USB_DEV_VENDOR_TYPE:
retStatus = HandleVendorRequests(transfer, context);
break;
default:
/* Unknown request type: CY_USB_DEV_REQUEST_NOT_HANDLED */
break;
}
/* Process standard requests */
/* Continue processing if request was handled */
if (CY_USB_DEV_SUCCESS == retStatus)
{
retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
/* Check transfer setup before continue transfer */
if (transfer->setup.bmRequestType.direction == transfer->direction)
{
/* Check transfer length */
if (transfer->setup.wLength > 0U)
{
if (CY_USB_DEV_DIR_DEVICE_TO_HOST == transfer->direction)
{
/* IN data stage is required */
/* Transfer must be less than or equal to the size requested by the host */
if (transfer->remaining > transfer->setup.wLength)
{
transfer->remaining = transfer->setup.wLength;
}
retStatus = CY_USB_DEV_SUCCESS;
}
else
{
/* OUT data stage is required */
/* Transfer must be equal to the size requested by the Host and
* buffer for data must be large enough
*/
if ((transfer->remaining == transfer->setup.wLength) &&
(transfer->remaining <= transfer->bufferSize))
{
retStatus = CY_USB_DEV_SUCCESS;
}
}
}
else
{
/* No data stage: transfer size must be zero */
if (0U == transfer->remaining)
{
retStatus = CY_USB_DEV_SUCCESS;
}
}
}
/* Execute transfer if transfer setup correctly */
if (CY_USB_DEV_SUCCESS == retStatus)
{
if (transfer->setup.wLength > 0U)
{
/* Data or retStatus stage if applicable */
if (CY_USB_DEV_DIR_DEVICE_TO_HOST == transfer->direction)
{
/* Define whether send zero length packet at the end of transfer */
if (transfer->setup.wLength > transfer->remaining)
{
/* Transfer is a multiple of EP0 max packet size */
transfer->zlp = (0U == (transfer->remaining % Cy_USBFS_Dev_Drv_GetEp0MaxPacket(context->drvBase)));
}
/* Handle data stage (IN direction) */
(void) HandleIn(context);
}
else
{
/* Set buffer to accept Host data */
transfer->ptr = transfer->buffer;
/* Start data stage (OUT direction) */
(void) Cy_USBFS_Dev_Drv_Ep0Read(context->drvBase, transfer->ptr, (uint32_t) transfer->remaining, context->drvContext);
}
}
else
{
/* No data state: move to status stage (IN direction) */
(void) Cy_USBFS_Dev_Drv_Ep0Write(context->drvBase, NULL, 0U, context->drvContext);
}
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: HandleIn
****************************************************************************//**
*
* Handles IN packet received event (generated from Endpoint 0 Interrupt).
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
static cy_en_usb_dev_status_t HandleIn(cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
cy_stc_usb_dev_control_transfer_t *transfer = &context->ControlTransfer;
if (CY_USB_DEV_DIR_DEVICE_TO_HOST == transfer->direction)
{
/* Data stage (direction IN) */
/* Send data packet or zero length packet. Skip processing after zero length is sent */
if (false == ((0U == transfer->remaining) && (!transfer->zlp)))
{
if ((transfer->remaining == 0U) && (transfer->zlp))
{
/* Zero length packet is send by code below */
transfer->zlp = false;
}
/* Write to endpoint 0 */
uint16_t packetSize = (uint16_t) Cy_USBFS_Dev_Drv_Ep0Write(context->drvBase,
transfer->ptr,
(uint32_t) transfer->remaining,
context->drvContext);
/* Update transfer */
transfer->ptr += packetSize;
transfer->remaining -= packetSize;
/* After last packet has been written move to move to status stage.
* Do not wait for the next IN event because it can be dropped if
* ACK from host is corrupted.
* For more info on this see section 8.5.3.3 of the USB2.0 specification.
* */
if ((0U == transfer->remaining) && (transfer->zlp == false))
{
/* Data stage completed: move to status stage (direction OUT) */
(void) Cy_USBFS_Dev_Drv_Ep0Read(context->drvBase, NULL, 0UL, context->drvContext);
}
}
retStatus = CY_USB_DEV_SUCCESS;
}
return retStatus;
}
/*******************************************************************************
* Function Name: HandleOut
****************************************************************************//**
*
* Handles OUT packet received event (generated from Endpoint 0 Interrupt).
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
static cy_en_usb_dev_status_t HandleOut(cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
cy_stc_usb_dev_control_transfer_t *transfer = &context->ControlTransfer;
if (CY_USB_DEV_DIR_HOST_TO_DEVICE == transfer->direction)
{
/* Control transfer: DATA stage (direction OUT) */
/* Read from endpoint 0 */
uint16_t packetSize = (uint16_t) Cy_USBFS_Dev_Drv_Ep0ReadResult(context->drvBase, context->drvContext);
/* Check whether transfer size is valid */
if (packetSize <= transfer->remaining)
{
/* Update transfer counters */
transfer->ptr += packetSize;
transfer->size += packetSize;
transfer->remaining -= packetSize;
/* Check whether all bytes received */
if (transfer->remaining > 0U)
{
/* Continue: there are more bytes to receive */
Cy_USBFS_Dev_Drv_Ep0Read(context->drvBase,
transfer->ptr,
(uint32_t) transfer->remaining,
context->drvContext);
retStatus = CY_USB_DEV_SUCCESS;
}
else
{
/* Notify class layer DATA stage completed */
if (transfer->notify)
{
/* Clear notify and reset buffer pointer */
transfer->notify = false;
transfer->ptr = transfer->buffer;
/* Handle Setup request depends on type */
switch(transfer->setup.bmRequestType.type)
{
case CY_USB_DEV_STANDARD_TYPE:
/* Return CY_USB_DEV_REQUEST_NOT_HANDLED because
* Standard request handler does not use notification.
*/
break;
case CY_USB_DEV_CLASS_TYPE:
retStatus = HandleClassRequestsCompleted(context->classRoot, transfer, context);
break;
case CY_USB_DEV_VENDOR_TYPE:
retStatus = HandleVendorRequestsCompleted(transfer, context);
break;
default:
/* Unknown request type: return CY_USB_DEV_REQUEST_NOT_HANDLED */
break;
}
}
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Move to STATUS stage (direction IN) */
(void) Cy_USBFS_Dev_Drv_Ep0Write(context->drvBase, NULL, 0U, context->drvContext);
}
}
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: Ep0SetupCallback
****************************************************************************//**
*
* Implements callback for setup received event (generated from Endpoint 0
* Interrupt). The Endpoint 0 is STALLED if processing was successful.
*
* \param base
* The pointer to the USBFS instance.
*
* \param drvContext
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
*******************************************************************************/
static void Ep0SetupCallback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext)
{
/* Get device context from the driver context */
cy_stc_usb_dev_context_t *context = (cy_stc_usb_dev_context_t *) Cy_USBFS_Dev_Drv_GetDevContext(base, drvContext);
/* Endpoint 0 setup event */
if (CY_USB_DEV_SUCCESS != HandleSetup(context))
{
/* Protocol stall */
Cy_USBFS_Dev_Drv_Ep0Stall(base);
}
}
/*******************************************************************************
* Function Name: Ep0InCallback
****************************************************************************//**
*
* Implements callback for IN packet received event (generated from Endpoint 0
* Interrupt). The Endpoint 0 is STALLED if processing was successful.
*
* \param base
* The pointer to the USBFS instance.
*
* \param drvContext
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
*******************************************************************************/
static void Ep0InCallback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext)
{
/* Get device context from the driver context */
cy_stc_usb_dev_context_t *context = (cy_stc_usb_dev_context_t *) Cy_USBFS_Dev_Drv_GetDevContext(base, drvContext);
/* Endpoint 0 IN packet received event */
if (CY_USB_DEV_SUCCESS != HandleIn(context))
{
/* Protocol stall */
Cy_USBFS_Dev_Drv_Ep0Stall(base);
}
}
/*******************************************************************************
* Function Name: Ep0OutCallback
****************************************************************************//**
*
* Implements callback for OUT packet received event (generated from Endpoint 0
* Interrupt). The Endpoint 0 is STALLED if processing was successful.
*
* \param base
* The pointer to the USBFS instance.
*
* \param drvContext
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
*******************************************************************************/
static void Ep0OutCallback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext)
{
/* Get device context from the driver context */
cy_stc_usb_dev_context_t *context = (cy_stc_usb_dev_context_t *) Cy_USBFS_Dev_Drv_GetDevContext(base, drvContext);
/* Endpoint 0 OUT packet received */
if (CY_USB_DEV_SUCCESS != HandleOut(context))
{
/* Protocol stall */
Cy_USBFS_Dev_Drv_Ep0Stall(base);
}
}
#if defined(CY_IP_MXUSBFS)
/*******************************************************************************
* Function Name: InitSerialNumberString
****************************************************************************//**
*
* Initializes serial number string using silicon ID.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
*******************************************************************************/
static void InitSerialNumberString(cy_stc_usb_dev_context_t *context)
{
const uint8_t hex[] = "0123456789ABCDEF";
uint8_t *strDescr = context->serialNumDescr;
uint8_t *id;
uint32_t i = 0U;
uint32_t j = 0U;
uint64_t tmp;
/* Place header: length and type */
strDescr[STRING_DESCR_LENGTH_POS] = CY_USB_DEV_SN_STRING_DESR_LENGTH;
strDescr[STRING_DESCR_TYPE_POS] = CY_USB_DEV_STRING_DESCR;
/* Get unique ID - 8 bytes */
tmp = Cy_SysLib_GetUniqueId();
/* Get start address of uint64_t */
id = (uint8_t *) &tmp;
/* Fill descriptor using unique silicon ID */
for (i = 2U; i < CY_USB_DEV_SN_STRING_DESR_LENGTH; i += 4U)
{
strDescr[i + 0U] = hex[(id[j] & 0x0FU)];
strDescr[i + 1U] = 0U;
strDescr[i + 2U] = hex[(id[j] >> 4U)];
strDescr[i + 3U] = 0U;
++j;
}
}
#endif /* defined(CY_IP_MXUSBFS) */
/*******************************************************************************
* Function Name: GetDescriptorRequest
****************************************************************************//**
*
* Handles GET_DESCRIPTOR standard request.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t GetDescriptorRequest(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
switch (CY_USB_DEV_GET_DESCR_TYPE(transfer->setup.wValue))
{
case CY_USB_DEV_DEVICE_DESCR:
{
/* Return device descriptor */
transfer->ptr = (uint8_t *) context->devDescriptors->deviceDescriptor;
transfer->remaining = CY_USB_DEV_DEVICE_DESCR_LENGTH;
retStatus = CY_USB_DEV_SUCCESS;
}
break;
case CY_USB_DEV_CONFIG_DESCR:
{
/* Get configuration index */
uint32_t idx = CY_USB_DEV_GET_DESCR_IDX(transfer->setup.wValue);
/* Check whether requested configuration descriptor exists */
if (idx < context->devDescriptors->numConfigurations)
{
uint8_t *configDescr = (uint8_t *) context->devDescriptors->configurations[idx]->configDescriptor;
/* Return configuration descriptor */
transfer->ptr = configDescr;
transfer->remaining = GET_UINT16(configDescr[CONFIG_DESCR_LENGTH_LSB_POS],
configDescr[CONFIG_DESCR_LENGTH_MSB_POS]);
retStatus = CY_USB_DEV_SUCCESS;
}
}
break;
case CY_USB_DEV_BOS_DESCR:
{
uint8_t *bosDescr = (uint8_t *) context->devDescriptors->bosDescriptor;
/* Check if BOS descriptor exists */
if (NULL != bosDescr)
{
/* Return BOS descriptor */
transfer->ptr = bosDescr;
transfer->remaining = GET_UINT16(bosDescr[BOS_DESCR_LENGTH_LSB_POS],
bosDescr[BOS_DESCR_LENGTH_MSB_POS]);
retStatus = CY_USB_DEV_SUCCESS;
}
}
break;
case CY_USB_DEV_STRING_DESCR:
{
uint8_t *strDescr = NULL;
/* Get string index */
uint32_t idx = CY_USB_DEV_GET_DESCR_IDX(transfer->setup.wValue);
/* Get pool of strings */
cy_stc_usb_dev_string_t const *descr = context->devDescriptors->strings;
/* Special case: Microsoft OS Descriptors String descriptor */
if (idx == STRING_IMSOS_INDEX)
{
if (NULL != descr->osStringDescriptors)
{
/* Get string */
strDescr = (uint8_t *) descr->osStringDescriptors->msOsDescriptor;
retStatus = CY_USB_DEV_SUCCESS;
}
}
/* Other string descriptors (included serial number string) */
else
{
/* Check that string exists */
if ((NULL != descr->stringDescriptors) && (descr->numStrings > 0U) && (idx < descr->numStrings))
{
/* Get string from descriptors */
strDescr = (uint8_t *) descr->stringDescriptors[idx];
/* Check whether requested string is serial number */
if ((idx != STRING_LANGID_INDEX) &&
(idx == context->devDescriptors->deviceDescriptor[DEVICE_DESCR_ISN_STRING_POS]))
{
/* Check options for serial string */
if (strDescr != NULL)
{
/* Serial number is part of device descriptor */
retStatus = CY_USB_DEV_SUCCESS;
}
else
{
if (context->getSerialNumString != NULL)
{
/* Get serial number using callback */
strDescr = context->getSerialNumString();
if (strDescr != NULL)
{
retStatus = CY_USB_DEV_SUCCESS;
}
}
else
{
/* Get serial number using silicon ID */
strDescr = &context->serialNumDescr[0];
retStatus = CY_USB_DEV_SUCCESS;
}
}
}
else
{
retStatus = CY_USB_DEV_SUCCESS;
}
}
}
/* Return string if it was found */
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* User defined descriptor */
transfer->ptr = strDescr;
transfer->remaining = strDescr[STRING_DESCR_LENGTH_POS];
}
}
break;
case CY_USB_DEV_INTERFACE_DESCR:
case CY_USB_DEV_ENDPOINT_DESCR:
/* These descriptor types are not supported */
break;
default:
/* The descriptor type was not recognized */
break;
}
return retStatus;
}
/*******************************************************************************
* Function Name: GetConfigurationRequest
****************************************************************************//**
*
* Handles SET_CONFIGURATION standard request.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t GetConfigurationRequest(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context)
{
/* Send the device configuration */
transfer->ptr = (uint8_t *) &context->configuration;
transfer->remaining = (uint16_t)sizeof(context->configuration);
return CY_USB_DEV_SUCCESS;
}
/*******************************************************************************
* Function Name: GetInterfaceRequest
****************************************************************************//**
*
* Handles GET_INTERFACE standard request.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t GetInterfaceRequest(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
uint32_t interface = transfer->setup.wIndex;
if (CY_USB_DEV_CONFIGURED != context->state)
{
return CY_USB_DEV_REQUEST_NOT_HANDLED;
}
/* Check whether interface exists in current configuration */
if (interface < context->devDescriptors->configurations[VAL2IDX(context->configuration)]->numInterfaces)
{
/* Return current alternate setting for an interface */
transfer->ptr = (uint8_t *) &context->alternate[interface];
transfer->remaining = (uint16_t)sizeof(context->alternate[interface]);
retStatus = CY_USB_DEV_SUCCESS;
}
return retStatus;
}
/*******************************************************************************
* Function Name: GetStatusRequest
****************************************************************************//**
*
* Handles GET_STATUS standard request.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t GetStatusRequest(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t const *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
uint16_t response;
/* Endpoint or interface must be zero if device is not configured */
if (context->state != CY_USB_DEV_CONFIGURED)
{
if (0U != transfer->setup.wIndex)
{
return CY_USB_DEV_REQUEST_NOT_HANDLED;
}
}
/* Find request recipient */
switch (transfer->setup.bmRequestType.recipient)
{
case CY_USB_DEV_RECIPIENT_DEVICE:
{
/* Return device status: powered or remote wakeup */
response = (uint16_t) context->status;
retStatus = CY_USB_DEV_SUCCESS;
}
break;
case CY_USB_DEV_RECIPIENT_INTERFACE:
{
/* All status bits are Reserved (Reset to zero) */
response = 0U;
retStatus = CY_USB_DEV_SUCCESS;
}
break;
case CY_USB_DEV_RECIPIENT_ENDPOINT:
{
uint32_t endpoint = CY_USB_DEV_EPADDR2EP(transfer->setup.wIndex);
cy_en_usb_dev_ep_state_t epState = Cy_USBFS_Dev_Drv_GetEndpointState(context->drvBase, endpoint, context->drvContext);
/* Check that valid endpoint is requested */
if ((CY_USB_DEV_EP_INVALID != epState) && (CY_USB_DEV_EP_DISABLED != epState))
{
/* Get endpoint state */
response = (CY_USB_DEV_EP_STALLED == epState) ? CY_USB_DEV_ENDPOINT_STATUS_HALT : 0U;
retStatus = CY_USB_DEV_SUCCESS;
}
}
break;
default:
{
/* Do nothing. */
break;
}
}
/* Put response into the buffer */
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Send the status to host */
transfer->buffer[0U] = CY_LO8(response);
transfer->buffer[1U] = CY_HI8(response);
transfer->ptr = transfer->buffer;
transfer->remaining = (uint16_t)sizeof(response);
}
return retStatus;
}
/*******************************************************************************
* Function Name: ClearFeatureRequest
****************************************************************************//**
*
* Handles CLEAR_FEATURE standard request.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t ClearFeatureRequest(cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
/* Endpoint or interface must be zero if device is not configured */
if (context->state != CY_USB_DEV_CONFIGURED)
{
if (transfer->setup.wIndex != 0U)
{
return CY_USB_DEV_REQUEST_NOT_HANDLED;
}
}
/* Find request recipient */
switch (transfer->setup.bmRequestType.recipient)
{
case CY_USB_DEV_RECIPIENT_DEVICE:
{
/* Check feature selector: TEST_MODE is not supported */
if (transfer->setup.wValue == CY_USB_DEV_DEVICE_REMOTE_WAKEUP)
{
context->status &= (uint8_t) ~CY_USB_DEV_STATUS_REMOTE_WAKEUP_MASK;
retStatus = CY_USB_DEV_SUCCESS;
}
}
break;
case CY_USB_DEV_RECIPIENT_INTERFACE:
/* There is no feature selector this recipient */
break;
case CY_USB_DEV_RECIPIENT_ENDPOINT:
{
/* Check that feature selector is ENDPOINT_HALT */
if (transfer->setup.wValue == CY_USB_DEV_ENDPOINT_HALT)
{
uint32_t endpoint = CY_USB_DEV_EPADDR2EP(transfer->setup.wIndex);
/* Only enabled and data endpoints can be STALLED */
retStatus = (cy_en_usb_dev_status_t)
Cy_USBFS_Dev_Drv_UnStallEndpoint(context->drvBase,
endpoint, context->drvContext);
if (CY_USB_DEV_SUCCESS != retStatus)
{
retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
}
}
}
break;
default:
/* Unknown recipient */
break;
}
return retStatus;
}
/*******************************************************************************
* Function Name: SetAddressRequest
****************************************************************************//**
*
* Handles SET_ADDRESS standard request.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t SetAddressRequest(cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context)
{
uint8_t devAddress = CY_LO8(transfer->setup.wValue);
/* Request to change address after status status */
Cy_USBFS_Dev_Drv_SetAddress(context->drvBase, devAddress, context->drvContext);
/* Set device state depends on address value */
context->state = (0U != devAddress) ? CY_USB_DEV_ADDRESSED : CY_USB_DEV_DEFAULT;
return CY_USB_DEV_SUCCESS;
}
/*******************************************************************************
* Function Name: ConfigureDataEndpoints
****************************************************************************//**
*
* Configures data endpoints that belong to a certain configuration.
*
* \param config
* Configuration index (configuration value - 1).
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of executed operation \ref cy_en_usb_dev_status_t.
* CY_USB_DEV_SUCCESS is returned if no data endpoints are to be configured.
*
*******************************************************************************/
static cy_en_usb_dev_status_t ConfigureDataEndpoints(uint32_t config, cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_SUCCESS;
uint32_t intf, alt, ep;
uint32_t numIntr = context->devDescriptors->configurations[config]->numInterfaces;
cy_stc_usb_dev_interface_t const **descr = context->devDescriptors->configurations[config]->interfaces;
/* Go through all interfaces that belong to configuration */
for (intf = 0U; intf < numIntr; ++intf)
{
uint32_t mask = context->devDescriptors->configurations[config]->interfaces[intf]->endpointsMask;
uint32_t endpoint = 1UL;
/* Use mask to find only endpoints that belong to interface */
while (0U != mask)
{
/* Configure enabled endpoint */
if (0U != (mask & 0x01U))
{
bool configured = false;
cy_stc_usb_dev_ep_config_t epConfig = {0};
epConfig.enableEndpoint = false;
epConfig.allocBuffer = true;
/* Go through all alternate */
for (alt = 0U; alt < descr[intf]->numAlternates; ++alt)
{
/* Go thorough all endpoints that belong to alternate */
for (ep = 0U; ep < descr[intf]->alternates[alt]->numEndpoints; ++ep)
{
cy_stc_usbdev_endpoint_descr_t const *epDescr = (cy_stc_usbdev_endpoint_descr_t const *) ((const void *)(descr[intf]->alternates[alt]->endpoints[ep]->endpointDescriptor));
/* Find endpoint that needs to be configured */
if (CY_USB_DEV_EPADDR2EP(epDescr->bEndpointAddress) == endpoint)
{
if (false == configured)
{
/* Enable endpoint that belongs to alternate 0 */
if (0U == alt)
{
epConfig.enableEndpoint = true;
}
/* Initialize endpoint configuration structure */
epConfig.endpointAddr = epDescr->bEndpointAddress;
epConfig.attributes = epDescr->bmAttributes;
epConfig.maxPacketSize = GET_CFG_WORD(&epDescr->wMaxPacketSize);
epConfig.bufferSize = GET_CFG_WORD(&epDescr->wMaxPacketSize);
/* Set configuration of the 1st endpoint instance */
configured = true;
}
else
{
/* Find maximum packet size for this endpoint in all alternates */
if (epConfig.bufferSize < GET_CFG_WORD(&epDescr->wMaxPacketSize))
{
epConfig.bufferSize = GET_CFG_WORD(&epDescr->wMaxPacketSize);
}
}
break;
}
}
}
/* Add endpoint */
retStatus = (cy_en_usb_dev_status_t)
Cy_USBFS_Dev_Drv_AddEndpoint(context->drvBase,
&epConfig, context->drvContext);
/* Check operation result */
if (CY_USB_DEV_SUCCESS != retStatus)
{
retStatus = CY_USB_DEV_DRV_HW_ERROR;
break;
}
}
mask >>= 1U;
++endpoint;
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: SetConfigurationRequest
****************************************************************************//**
*
* Handles SET_CONFIGURATION standard request.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t SetConfigurationRequest(cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
uint32_t config = (uint32_t) transfer->setup.wValue;
if (0U == config)
{
/* Disable all data endpoints */
Cy_USBFS_Dev_Drv_UnConfigureDevice(context->drvBase, context->drvContext);
/* Store configuration and move to configured state */
context->configChanged = true;
context->configuration = (uint8_t) config;
context->state = CY_USB_DEV_ADDRESSED;
retStatus = CY_USB_DEV_SUCCESS;
}
else
{
/* Check whether configuration is valid */
if (config <= context->devDescriptors->numConfigurations)
{
uint32_t configIdx = VAL2IDX(config);
/* Clear context fields that keep configuration related data */
(void) memset((void *) context->alternate, 0, CY_USB_DEV_NUM_INTERFACES_MAX);
/* Set device endpoints into the default state before configure them */
Cy_USBFS_Dev_Drv_UnConfigureDevice(context->drvBase, context->drvContext);
/* Configure endpoints */
retStatus = ConfigureDataEndpoints(configIdx, context);
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Notify Bus Reset event for device instance */
if (NULL != context->eventsCallback)
{
/* Input parameters are zeros. Ignore return for Bus Reset event. */
retStatus = context->eventsCallback(CY_USB_DEV_EVENT_SET_CONFIG, config, 0UL, context);
}
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Get linked list of classes */
cy_stc_usb_dev_class_ll_item_t *curItem = context->classRoot;
/* Call Set Configuration callback for all class instances */
while (NULL != curItem)
{
if (NULL != curItem->classObj->setConfiguration)
{
/* Execute callback */
retStatus = curItem->classObj->setConfiguration(config, curItem->classData, context);
if (CY_USB_DEV_SUCCESS != retStatus)
{
/* Operation failed break the loop */
break;
}
}
/* Move to next element */
curItem = curItem->next;
}
}
}
if (CY_USB_DEV_SUCCESS == retStatus)
{
const uint32_t attribute = context->devDescriptors->configurations[configIdx]->configDescriptor[CONFIG_DESCR_ATTRIB_POS];
/* Complete device configuration (endpoints were configured) */
Cy_USBFS_Dev_Drv_ConfigDevice(context->drvBase, context->drvContext);
/* Set power status (remote wakeup status was cleared on bus reset) */
if (0U != (attribute & CONFIG_ATTR_SELF_POWERED_MASK))
{
context->status |= (uint8_t) CY_USB_DEV_STATUS_SELF_POWERED_MASK;
}
else
{
context->status &= (uint8_t) ~CY_USB_DEV_STATUS_SELF_POWERED_MASK;
}
/* Store configuration and move to configured state */
context->configChanged = true;
context->configuration = (uint8_t) config;
context->state = CY_USB_DEV_CONFIGURED;
}
else
{
/* Set configuration failed, remain in current state */
Cy_USBFS_Dev_Drv_UnConfigureDevice(context->drvBase, context->drvContext);
retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
}
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: InterfaceRemoveDataEndpoints
****************************************************************************//**
*
* Disables data endpoints that belong to a certain interface.
*
* \param numEndpoints
* The number of data endpoints.
*
* \param epsPool
* The pointer to pointer to the array of the endpoints.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of executed operation \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
static cy_en_usb_dev_status_t InterfaceRemoveDataEndpoints(uint32_t numEndpoints,
cy_stc_usb_dev_endpoint_t const * const *epsPool,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
uint32_t endpoint;
if (0U == numEndpoints)
{
/* Interface with zero endpoints return success */
return CY_USB_DEV_SUCCESS;
}
/* Remove endpoints that are available in pool */
for (endpoint = 0UL; endpoint < numEndpoints; ++endpoint)
{
/* Get endpoint parsed endpoint descriptor */
cy_stc_usbdev_endpoint_descr_t const *epDescr = (cy_stc_usbdev_endpoint_descr_t const *)((const void *) (epsPool[endpoint]->endpointDescriptor));
/* Remove endpoint */
retStatus = (cy_en_usb_dev_status_t)
Cy_USBFS_Dev_Drv_RemoveEndpoint(context->drvBase, (uint32_t) epDescr->bEndpointAddress,
context->drvContext);
if (CY_USB_DEV_SUCCESS != retStatus)
{
/* Remove operation failed */
retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
break;
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: InterfaceAddDataEndpoints
****************************************************************************//**
*
* Configures data endpoints that belong to a certain interface to operate.
*
* \param numEndpoints
* The number of data endpoints.
*
* \param epsPool
* The pointer to pointer to the array of the endpoints.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
static cy_en_usb_dev_status_t InterfaceAddDataEndpoints(uint32_t numEndpoints,
cy_stc_usb_dev_endpoint_t const *const *epsPool,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
uint32_t endpoint;
if (0U == numEndpoints)
{
/* Interface with zero endpoints return success */
return CY_USB_DEV_SUCCESS;
}
/* Add endpoints that are available the pool */
for (endpoint = 0UL; endpoint < numEndpoints; ++endpoint)
{
cy_stc_usbdev_endpoint_descr_t const *epDescr = (cy_stc_usbdev_endpoint_descr_t const *)((const void *) (epsPool[endpoint]->endpointDescriptor));
cy_stc_usb_dev_ep_config_t epConfig;
/* Setup configuration structure */
epConfig.allocBuffer = false;
epConfig.enableEndpoint = true;
epConfig.endpointAddr = epDescr->bEndpointAddress;
epConfig.attributes = epDescr->bmAttributes;
epConfig.maxPacketSize = GET_CFG_WORD(&epDescr->wMaxPacketSize);
/* Add endpoint */
retStatus = (cy_en_usb_dev_status_t)
Cy_USBFS_Dev_Drv_AddEndpoint(context->drvBase, &epConfig,
context->drvContext);
if (CY_USB_DEV_SUCCESS != retStatus)
{
/* Add operation failed */
retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
break;
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: CallSetInterfaceCallbacks
****************************************************************************//**
*
* Calls Class callbacks for SET_INTERFACE standard request.
*
* \param interface
* The interface number.
*
* \param alternate
* The alternate setting for the interface.
*
* \param curItem
* The pointer to class linked list element.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t CallSetInterfaceCallbacks(uint32_t interface,
uint32_t alternate,
cy_stc_usb_dev_class_ll_item_t *curItem,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_SUCCESS;
/* Call registered Set Interface callbacks for all class instances */
while (NULL != curItem)
{
/* Execute callback */
if (NULL != curItem->classObj->setInterface)
{
retStatus = curItem->classObj->setInterface(interface, alternate,
curItem->classData, context);
if (CY_USB_DEV_SUCCESS != retStatus)
{
break;
}
}
/* Move to next element */
curItem = curItem->next;
}
return retStatus;
}
/*******************************************************************************
* Function Name: SetInterfaceRequest
****************************************************************************//**
*
* Handles SET_INTERFACE standard request.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t SetInterfaceRequest(cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
uint32_t cfg = (uint32_t) VAL2IDX(context->configuration);
uint32_t intf = (uint32_t) transfer->setup.wIndex;
uint32_t alt = (uint32_t) transfer->setup.wValue;
/* Not supported when configuration is set */
if (CY_USB_DEV_CONFIGURED != context->state)
{
return CY_USB_DEV_REQUEST_NOT_HANDLED;
}
/* Check whether interface exists */
if (intf < context->devDescriptors->configurations[cfg]->numInterfaces)
{
/* Check whether alternate for this interface exists */
if (alt < context->devDescriptors->configurations[cfg]->interfaces[intf]->numAlternates)
{
/* Get pointer to interface descriptor structure */
const cy_stc_usb_dev_interface_t *descr = context->devDescriptors->configurations[cfg]->interfaces[intf];
/* If alternate settings are not changed, do nothing with current alternate */
if (alt != context->alternate[intf])
{
/* Remove endpoints used by current alternate (old) */
uint32_t altOld = context->alternate[intf];
retStatus = InterfaceRemoveDataEndpoints((uint32_t) descr->alternates[altOld]->numEndpoints,
(cy_stc_usb_dev_endpoint_t const * const *) descr->alternates[altOld]->endpoints,
context);
/* Add endpoints used by new alternate (received) */
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Add endpoints used by new alternate (received) */
retStatus = InterfaceAddDataEndpoints((uint32_t) descr->alternates[alt]->numEndpoints,
(cy_stc_usb_dev_endpoint_t const * const *) descr->alternates[alt]->endpoints,
context);
}
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Notify Bus Reset event for device instance */
if (NULL != context->eventsCallback)
{
/* Input parameters are zeros. Ignore return for Bus Reset event. */
retStatus = context->eventsCallback(CY_USB_DEV_EVENT_SET_INTERFACE, alt, intf, context);
}
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Call Set Interface callbacks for a Class */
retStatus = CallSetInterfaceCallbacks(intf, alt, context->classRoot, context);
}
}
/* Check request complete status */
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Store current alternate settings */
context->configChanged = true;
context->alternate[intf] = (uint8_t) alt;
}
}
else
{
/* Do nothing: current alternate is already set */
retStatus = CY_USB_DEV_SUCCESS;
}
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: SetFeatureRequest
****************************************************************************//**
*
* Handles SET_FEATURE standard request.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t SetFeatureRequest(cy_stc_usb_dev_control_transfer_t const *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
/* Endpoint or interface must be zero if device is not configured */
if (CY_USB_DEV_CONFIGURED != context->state)
{
if (0U != transfer->setup.wIndex)
{
return CY_USB_DEV_REQUEST_NOT_HANDLED;
}
}
/* Find request recipient */
switch (transfer->setup.bmRequestType.recipient)
{
case CY_USB_DEV_RECIPIENT_DEVICE:
{
/* Check feature selector: TEST_MODE is not supported */
if (CY_USB_DEV_DEVICE_REMOTE_WAKEUP == transfer->setup.wValue)
{
context->status |= CY_USB_DEV_STATUS_REMOTE_WAKEUP_MASK;
retStatus = CY_USB_DEV_SUCCESS;
}
}
break;
case CY_USB_DEV_RECIPIENT_INTERFACE:
/* There is no feature selector this recipient */
break;
case CY_USB_DEV_RECIPIENT_ENDPOINT:
{
/* Check that feature selector is ENDPOINT_HALT */
if (CY_USB_DEV_ENDPOINT_HALT == transfer->setup.wValue)
{
uint32_t endpoint = CY_USB_DEV_EPADDR2EP(transfer->setup.wIndex);
/* Only enabled and data endpoints can be STALLED */
retStatus = (cy_en_usb_dev_status_t)
Cy_USBFS_Dev_Drv_StallEndpoint(context->drvBase,
endpoint, context->drvContext);
if (CY_USB_DEV_SUCCESS != retStatus)
{
retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
}
}
}
break;
default:
/* Unknown recipient */
break;
}
return retStatus;
}
/*******************************************************************************
* Function Name: HandleStandardRequests
****************************************************************************//**
*
* Handles supported standard requests.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t HandleStandardRequests(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
/* Direction is Device to Host: DATA stage direction is IN */
if (CY_USB_DEV_DIR_DEVICE_TO_HOST == transfer->direction)
{
switch (transfer->setup.bRequest)
{
case CY_USB_DEV_RQST_GET_DESCRIPTOR:
retStatus = GetDescriptorRequest(transfer, context);
break;
case CY_USB_DEV_RQST_GET_CONFIGURATION:
retStatus = GetConfigurationRequest(transfer, context);
break;
case CY_USB_DEV_RQST_GET_INTERFACE:
retStatus = GetInterfaceRequest(transfer, context);
break;
case CY_USB_DEV_RQST_GET_STATUS:
retStatus = GetStatusRequest(transfer, context);
break;
default:
/* Do nothing. */
break;
}
}
/* Direction is Host to Device: DATA stage direction is OUT or no DATA stage */
else
{
switch (transfer->setup.bRequest)
{
case CY_USB_DEV_RQST_SET_ADDRESS:
retStatus = SetAddressRequest(transfer, context);
break;
case CY_USB_DEV_RQST_SET_DESCRIPTOR:
/* This request is optional and not supported */
break;
case CY_USB_DEV_RQST_SET_CONFIGURATION:
retStatus = SetConfigurationRequest(transfer, context);
break;
case CY_USB_DEV_RQST_SET_INTERFACE:
retStatus = SetInterfaceRequest(transfer, context);
break;
case CY_USB_DEV_RQST_CLEAR_FEATURE:
retStatus = ClearFeatureRequest(transfer, context);
break;
case CY_USB_DEV_RQST_SET_FEATURE:
retStatus = SetFeatureRequest(transfer, context);
break;
default:
/* Do nothing. */
break;
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: HandleClassRequests
****************************************************************************//**
*
* Handles supported class requests.
*
* \param curItem
* The pointer to class linked list element.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t HandleClassRequests(cy_stc_usb_dev_class_ll_item_t *curItem,
cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
/* Call Request Received callback for all class instances */
while (NULL != curItem)
{
/* Execute callback */
if (NULL != curItem->classObj->requestReceived)
{
retStatus = curItem->classObj->requestReceived(transfer, curItem->classData, context);
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Request is handled, exit loop */
break;
}
}
/* Move to next element */
curItem = curItem->next;
}
return retStatus;
}
/*******************************************************************************
* Function Name: HandleClassRequestsComplete
****************************************************************************//**
*
* Handles supported class requests completion stage (data was
* received from the USB Host).
*
* \param curItem
* The pointer to class linked list element.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t HandleClassRequestsCompleted(cy_stc_usb_dev_class_ll_item_t *curItem,
cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
/* Call Request Complete callback for all class instances */
while (NULL != curItem)
{
/* Execute callback */
if (NULL != curItem->classObj->requestCompleted)
{
retStatus = curItem->classObj->requestCompleted(transfer, curItem->classData, context);
if (CY_USB_DEV_SUCCESS == retStatus)
{
/* Request is handled, exit loop */
break;
}
}
/* Move to next element */
curItem = curItem->next;
}
return retStatus;
}
/*******************************************************************************
* Function Name: GetExtOsStringDescriptors
****************************************************************************//**
*
* Handles supported vendor-specific requests.
*
* \param msOsString
* The pointer to the MS OS String structure \ref cy_stc_usb_dev_ms_os_string_t.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t GetExtOsStringDescriptors(cy_stc_usb_dev_ms_os_string_t const *msOsString,
cy_stc_usb_dev_control_transfer_t *transfer)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
/* Direction is Device to Host: DATA stage direction is IN */
if (CY_USB_DEV_DIR_DEVICE_TO_HOST == transfer->direction)
{
if (transfer->setup.bRequest == msOsString->msVendorCode)
{
switch(transfer->setup.wIndex)
{
case CY_USB_DEV_MS_OS_STRING_EXT_COMPAT_ID:
case CY_USB_DEV_MS_OS_STRING_EXT_PROPERTEIS:
{
/* Get Extended Compat ID / Properties OS Descriptor (ignores wValue) */
uint8_t *strDescr = (uint8_t *) ((CY_USB_DEV_MS_OS_STRING_EXT_COMPAT_ID == transfer->setup.wIndex) ?
msOsString->extCompatIdDescriptor : msOsString->extPropertiesDescriptor);
if (NULL != strDescr)
{
transfer->ptr = strDescr;
transfer->remaining = GET_UINT16(strDescr[EXT_OS_DESC_LENGTH_BYTE0_POS],
strDescr[EXT_OS_DESC_LENGTH_BYTE1_POS]);
retStatus = CY_USB_DEV_SUCCESS;
}
}
break;
default:
/* Do nothing. */
break;
}
}
}
return retStatus;
}
/*******************************************************************************
* Function Name: HandleVendorRequests
****************************************************************************//**
*
* Handles supported vendor-specific requests.
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t HandleVendorRequests(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
/* Get pointer to MS OS String descriptor structure */
const cy_stc_usb_dev_ms_os_string_t *msOsString = context->devDescriptors->strings->osStringDescriptors;
if (NULL != msOsString)
{
retStatus = GetExtOsStringDescriptors(msOsString, transfer);
}
if ((CY_USB_DEV_SUCCESS != retStatus) && (NULL != context->vndRequestReceived))
{
/* There is no classContext for vendor-specific callbacks */
retStatus = context->vndRequestReceived(transfer, NULL, context);
}
return retStatus;
}
/*******************************************************************************
* Function Name: HandleVendorRequestsComplete
****************************************************************************//**
*
* Handles supported vendor-specific requests completion stage (data was
* received from the USB Host).
*
* \param transfer
* Pointer to structure that holds SETUP packet and information for
* request processing.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status of request processing: \ref CY_USB_DEV_SUCCESS or
* \ref CY_USB_DEV_REQUEST_NOT_HANDLED.
*
*******************************************************************************/
static cy_en_usb_dev_status_t HandleVendorRequestsCompleted(cy_stc_usb_dev_control_transfer_t *transfer,
cy_stc_usb_dev_context_t *context)
{
cy_en_usb_dev_status_t retStatus = CY_USB_DEV_REQUEST_NOT_HANDLED;
if (NULL != context->vndRequestCompleted)
{
/* There is no classContext for vendor-specific callbacks */
retStatus = context->vndRequestCompleted(transfer, NULL, context);
}
return retStatus;
}
/*******************************************************************************
* Function Name: Cy_USB_Dev_RegisterClass
****************************************************************************//**
*
* Registers device class that will be supported by USB Device.
* The USB Device provides a hooks to implement required class support.
*
* \param classItem
* The pointer to class linked list element.
*
* \param classObj
* The pointer to the class structure.
*
* \param classContext
* The pointer to the context class structure allocated by the user.
* The structure is used during the custom class operation for internal
* configuration and data retention.
*
* \param context
* The pointer to the context structure \ref cy_stc_usb_dev_context_t allocated
* by the user. The structure is used during the USB Device operation for internal
* configuration and data retention. The user must not modify anything in this
* structure.
*
* \return
* Status code of the function execution \ref cy_en_usb_dev_status_t.
*
*******************************************************************************/
cy_en_usb_dev_status_t Cy_USB_Dev_RegisterClass(cy_stc_usb_dev_class_ll_item_t *classItem,
cy_stc_usb_dev_class_t *classObj,
void *classContext,
cy_stc_usb_dev_context_t *context)
{
if ((NULL == classItem) || (NULL == classObj))
{
return CY_USB_DEV_BAD_PARAM;
}
/* Get linked list of classes */
cy_stc_usb_dev_class_ll_item_t *curItem = context->classRoot;
/* Store data members */
classItem->classObj = classObj;
classItem->classData = classContext;
classItem->next = NULL;
if (NULL == curItem)
{
/* Add 1st element */
context->classRoot = classItem;
}
else
{
/* Find last element */
while (NULL != curItem->next)
{
curItem = curItem->next;
}
/* Add current item to the end of the list */
curItem->next = classItem;
}
return CY_USB_DEV_SUCCESS;
}
#endif /* (defined(CY_IP_MXUSBFS) || defined(CY_IP_M0S8USBDSS)) */
/* [] END OF FILE */