blob: f93c4b8005f1eeca2e5974bf723aea4096db7f61 [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2010 - 2015 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/******************************************************************************/
/**
* @file xusbps_endpoint.c
* @addtogroup usbps_v2_2
* @{
*
* Endpoint specific function implementations.
*
* @note None.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- -------- --------------------------------------------------------
* 1.00a jz 10/10/10 First release
* 1.03a nm 09/21/12 Fixed CR#678977. Added proper sequence for setup packet
* handling.
* 1.04a nm 11/02/12 Fixed CR#683931. Mult bits are set properly in dQH.
* 2.00a kpc 04/03/14 Fixed CR#777763. Updated the macro names
* 2.1 kpc 04/28/14 Added XUsbPs_EpBufferSendWithZLT api and merged common
* code to XUsbPs_EpQueueRequest.
* 2.3 bss 01/19/16 Modified XUsbPs_EpQueueRequest function to fix CR#873972
* (moving of dTD Head/Tail Pointers)and CR#873974(invalidate
* Caches After Buffer Receive in Endpoint Buffer Handler...)
* </pre>
******************************************************************************/
/***************************** Include Files **********************************/
#include <string.h> /* for bzero() */
#include <stdio.h>
#include "xusbps.h"
#include "xusbps_endpoint.h"
/************************** Constant Definitions ******************************/
/**************************** Type Definitions ********************************/
/************************** Variable Definitions ******************************/
/************************** Function Prototypes ******************************/
static void XUsbPs_EpListInit(XUsbPs_DeviceConfig *DevCfgPtr);
static void XUsbPs_dQHInit(XUsbPs_DeviceConfig *DevCfgPtr);
static int XUsbPs_dTDInit(XUsbPs_DeviceConfig *DevCfgPtr);
static int XUsbPs_dTDAttachBuffer(XUsbPs_dTD *dTDPtr,
const u8 *BufferPtr, u32 BufferLen);
static void XUsbPs_dQHSetMaxPacketLenISO(XUsbPs_dQH *dQHPtr, u32 Len);
/* Functions to reconfigure endpoint upon host's set alternate interface
* request.
*/
static void XUsbPs_dQHReinitEp(XUsbPs_DeviceConfig *DevCfgPtr,
int EpNum, unsigned short NewDirection);
static int XUsbPs_dTDReinitEp(XUsbPs_DeviceConfig *DevCfgPtr,
int EpNum, unsigned short NewDirection);
static int XUsbPs_EpQueueRequest(XUsbPs *InstancePtr, u8 EpNum,
const u8 *BufferPtr, u32 BufferLen, u8 ReqZero);
/******************************* Functions ************************************/
/*****************************************************************************/
/**
*
* This function configures the DEVICE side of the controller. The caller needs
* to pass in the desired configuration (e.g. number of endpoints) and a
* DMAable buffer that will hold the Queue Head List and the Transfer
* Descriptors. The required size for this buffer can be obtained by the caller
* using the: XUsbPs_DeviceMemRequired() macro.
*
* @param InstancePtr is a pointer to the XUsbPs instance of the
* controller.
* @param CfgPtr is a pointer to the configuration structure that contains
* the desired DEVICE side configuration.
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
*
* @note
* The caller may configure the controller for both, DEVICE and
* HOST side.
*
******************************************************************************/
int XUsbPs_ConfigureDevice(XUsbPs *InstancePtr,
const XUsbPs_DeviceConfig *CfgPtr)
{
int Status;
u32 ModeValue = 0x0;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(CfgPtr != NULL);
/* Copy the configuration data over into the local instance structure */
InstancePtr->DeviceConfig = *CfgPtr;
/* Align the buffer to a 2048 byte (XUSBPS_dQH_BASE_ALIGN) boundary.*/
InstancePtr->DeviceConfig.PhysAligned =
(InstancePtr->DeviceConfig.DMAMemPhys +
XUSBPS_dQH_BASE_ALIGN) &
~(XUSBPS_dQH_BASE_ALIGN -1);
/* Initialize the endpoint pointer list data structure. */
XUsbPs_EpListInit(&InstancePtr->DeviceConfig);
/* Initialize the Queue Head structures in DMA memory. */
XUsbPs_dQHInit(&InstancePtr->DeviceConfig);
/* Initialize the Transfer Descriptors in DMA memory.*/
Status = XUsbPs_dTDInit(&InstancePtr->DeviceConfig);
if (XST_SUCCESS != Status) {
return XST_FAILURE;
}
/* Changing the DEVICE mode requires a controller RESET. */
if (XST_SUCCESS != XUsbPs_Reset(InstancePtr)) {
return XST_FAILURE;
}
/* Set the Queue Head List address. */
XUsbPs_WriteReg(InstancePtr->Config.BaseAddress,
XUSBPS_EPLISTADDR_OFFSET,
InstancePtr->DeviceConfig.PhysAligned);
/* Set the USB mode register to configure DEVICE mode.
*
* XUSBPS_MODE_SLOM_MASK note:
* Disable Setup Lockout. Setup Lockout is not required as we
* will be using the tripwire mechanism when handling setup
* packets.
*/
ModeValue = XUSBPS_MODE_CM_DEVICE_MASK | XUSBPS_MODE_SLOM_MASK;
XUsbPs_WriteReg(InstancePtr->Config.BaseAddress,
XUSBPS_MODE_OFFSET, ModeValue);
XUsbPs_SetBits(InstancePtr, XUSBPS_OTGCSR_OFFSET,
XUSBPS_OTGSC_OT_MASK);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function sends a given data buffer.
*
* @param InstancePtr is a pointer to XUsbPs instance of the controller.
* @param EpNum is the number of the endpoint to receive data from.
* @param BufferPtr is a pointer to the buffer to send.
* @param BufferLen is the Buffer length.
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
* - XST_USB_BUF_TOO_BIG: Provided buffer is too big (>16kB).
* - XST_USB_NO_DESC_AVAILABLE: No TX descriptor is available.
*
******************************************************************************/
int XUsbPs_EpBufferSend(XUsbPs *InstancePtr, u8 EpNum,
const u8 *BufferPtr, u32 BufferLen)
{
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(EpNum < InstancePtr->DeviceConfig.NumEndpoints);
return XUsbPs_EpQueueRequest(InstancePtr, EpNum, BufferPtr,
BufferLen, FALSE);
}
/*****************************************************************************/
/**
* This function sends a given data buffer and also zero length packet if the
* Bufferlen is in multiples of endpoint max packet size.
*
* @param InstancePtr is a pointer to XUsbPs instance of the controller.
* @param EpNum is the number of the endpoint to receive data from.
* @param BufferPtr is a pointer to the buffer to send.
* @param BufferLen is the Buffer length.
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
* - XST_USB_BUF_TOO_BIG: Provided buffer is too big (>16kB).
* - XST_USB_NO_DESC_AVAILABLE: No TX descriptor is available.
*
******************************************************************************/
int XUsbPs_EpBufferSendWithZLT(XUsbPs *InstancePtr, u8 EpNum,
const u8 *BufferPtr, u32 BufferLen)
{
u8 ReqZero = FALSE;
XUsbPs_EpSetup *Ep;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(EpNum < InstancePtr->DeviceConfig.NumEndpoints);
Ep = &InstancePtr->DeviceConfig.EpCfg[EpNum].In;
if ((BufferLen >= Ep->MaxPacketSize) &&
(BufferLen % Ep->MaxPacketSize == 0)) {
ReqZero = TRUE;
}
return XUsbPs_EpQueueRequest(InstancePtr, EpNum, BufferPtr,
BufferLen, ReqZero);
}
/*****************************************************************************/
/**
* This function sends a given data buffer and also sends ZLT packet if it is
* requested.
*
* @param InstancePtr is a pointer to XUsbPs instance of the controller.
* @param EpNum is the number of the endpoint to receive data from.
* @param BufferPtr is a pointer to the buffer to send.
* @param BufferLen is the Buffer length.
* @param ReqZero is the
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
* - XST_USB_BUF_TOO_BIG: Provided buffer is too big (>16kB).
* - XST_USB_NO_DESC_AVAILABLE: No TX descriptor is available.
*
******************************************************************************/
static int XUsbPs_EpQueueRequest(XUsbPs *InstancePtr, u8 EpNum,
const u8 *BufferPtr, u32 BufferLen, u8 ReqZero)
{
int Status;
u32 Token;
XUsbPs_EpIn *Ep;
XUsbPs_dTD *DescPtr;
u32 Length;
u32 PipeEmpty = 1;
u32 Mask = 0x00010000;
u32 BitMask = Mask << EpNum;
u32 RegValue;
u32 Temp;
u32 exit = 1;
/* Locate the next available buffer in the ring. A buffer is available
* if its descriptor is not active.
*/
Ep = &InstancePtr->DeviceConfig.Ep[EpNum].In;
Xil_DCacheFlushRange((unsigned int)BufferPtr, BufferLen);
if(Ep->dTDTail != Ep->dTDHead) {
PipeEmpty = 0;
}
XUsbPs_dTDInvalidateCache(Ep->dTDHead);
/* Tell the caller if we do not have any descriptors available. */
if (XUsbPs_dTDIsActive(Ep->dTDHead)) {
return XST_USB_NO_DESC_AVAILABLE;
}
/* Remember the current head. */
DescPtr = Ep->dTDHead;
do {
/* Tell the caller if we do not have any descriptors available. */
if (XUsbPs_dTDIsActive(Ep->dTDHead)) {
return XST_USB_NO_DESC_AVAILABLE;
}
Length = (BufferLen > XUSBPS_dTD_BUF_MAX_SIZE) ? XUSBPS_dTD_BUF_MAX_SIZE : BufferLen;
/* Attach the provided buffer to the current descriptor.*/
Status = XUsbPs_dTDAttachBuffer(Ep->dTDHead, BufferPtr, Length);
if (XST_SUCCESS != Status) {
return XST_FAILURE;
}
BufferLen -= Length;
BufferPtr += Length;
XUsbPs_dTDSetActive(Ep->dTDHead);
if (BufferLen == 0 && (ReqZero == FALSE)) {
XUsbPs_dTDSetIOC(Ep->dTDHead);
exit = 0;
}
XUsbPs_dTDClrTerminate(Ep->dTDHead);
XUsbPs_dTDFlushCache(Ep->dTDHead);
/* Advance the head descriptor pointer to the next descriptor. */
Ep->dTDHead = XUsbPs_dTDGetNLP(Ep->dTDHead);
/* Terminate the next descriptor and flush the cache.*/
XUsbPs_dTDInvalidateCache(Ep->dTDHead);
if (ReqZero && BufferLen == 0) {
ReqZero = FALSE;
}
} while(BufferLen || exit);
XUsbPs_dTDSetTerminate(Ep->dTDHead);
XUsbPs_dTDFlushCache(Ep->dTDHead);
if(!PipeEmpty) {
/* Read the endpoint prime register. */
RegValue = XUsbPs_ReadReg(InstancePtr->Config.BaseAddress, XUSBPS_EPPRIME_OFFSET);
if(RegValue & BitMask) {
return XST_SUCCESS;
}
do {
RegValue = XUsbPs_ReadReg(InstancePtr->Config.BaseAddress, XUSBPS_CMD_OFFSET);
XUsbPs_WriteReg(InstancePtr->Config.BaseAddress, XUSBPS_CMD_OFFSET,
RegValue | XUSBPS_CMD_ATDTW_MASK);
Temp = XUsbPs_ReadReg(InstancePtr->Config.BaseAddress, XUSBPS_EPRDY_OFFSET)
& BitMask;
} while(!(XUsbPs_ReadReg(InstancePtr->Config.BaseAddress, XUSBPS_CMD_OFFSET) &
XUSBPS_CMD_ATDTW_MASK));
RegValue = XUsbPs_ReadReg(InstancePtr->Config.BaseAddress, XUSBPS_CMD_OFFSET);
XUsbPs_WriteReg(InstancePtr->Config.BaseAddress, XUSBPS_CMD_OFFSET,
RegValue & ~XUSBPS_CMD_ATDTW_MASK);
if(Temp) {
return XST_SUCCESS;
}
}
/* Check, if the DMA engine is still running. If it is running, we do
* not clear Queue Head fields.
*
* Same cache rule as for the Transfer Descriptor applies for the Queue
* Head.
*/
XUsbPs_dQHInvalidateCache(Ep->dQH);
/* Add the dTD to the dQH */
XUsbPs_WritedQH(Ep->dQH, XUSBPS_dQHdTDNLP, DescPtr);
Token = XUsbPs_ReaddQH(Ep->dQH, XUSBPS_dQHdTDTOKEN);
Token &= ~(XUSBPS_dTDTOKEN_ACTIVE_MASK | XUSBPS_dTDTOKEN_HALT_MASK);
XUsbPs_WritedQH(Ep->dQH, XUSBPS_dQHdTDTOKEN, Token);
XUsbPs_dQHFlushCache(Ep->dQH);
Status = XUsbPs_EpPrime(InstancePtr, EpNum, XUSBPS_EP_DIRECTION_IN);
return Status;
}
/*****************************************************************************/
/**
* This function receives a data buffer from the endpoint of the given endpoint
* number.
*
* @param InstancePtr is a pointer to the XUsbPs instance of the
* controller.
* @param EpNum is the number of the endpoint to receive data from.
* @param BufferPtr (OUT param) is a pointer to the buffer pointer to hold
* the reference of the data buffer.
* @param BufferLenPtr (OUT param) is a pointer to the integer that will
* hold the buffer length.
* @param Handle is the opaque handle to be used when the buffer is
* released.
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
* - XST_USB_NO_BUF: No buffer available.
*
* @note
* After handling the data in the buffer, the user MUST release
* the buffer using the Handle by calling the
* XUsbPs_EpBufferRelease() function.
*
******************************************************************************/
int XUsbPs_EpBufferReceive(XUsbPs *InstancePtr, u8 EpNum,
u8 **BufferPtr, u32 *BufferLenPtr, u32 *Handle)
{
XUsbPs_EpOut *Ep;
XUsbPs_EpSetup *EpSetup;
u32 length = 0;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(BufferPtr != NULL);
Xil_AssertNonvoid(BufferLenPtr != NULL);
Xil_AssertNonvoid(Handle != NULL);
Xil_AssertNonvoid(EpNum < InstancePtr->DeviceConfig.NumEndpoints);
/* Locate the next available buffer in the ring. A buffer is available
* if its descriptor is not active.
*/
Ep = &InstancePtr->DeviceConfig.Ep[EpNum].Out;
XUsbPs_dTDInvalidateCache(Ep->dTDCurr);
if (XUsbPs_dTDIsActive(Ep->dTDCurr)) {
return XST_USB_NO_BUF;
}
/* The buffer is not active which means that it has been processed by
* the DMA engine and contains valid data.
*/
EpSetup = &InstancePtr->DeviceConfig.EpCfg[EpNum].Out;
/* Use the buffer pointer stored in the "user data" field of the
* Transfer Descriptor.
*/
*BufferPtr = (u8 *) XUsbPs_ReaddTD(Ep->dTDCurr,
XUSBPS_dTDUSERDATA);
length = EpSetup->BufSize -
XUsbPs_dTDGetTransferLen(Ep->dTDCurr);
if(length > 0) {
*BufferLenPtr = length;
}else {
*BufferLenPtr = 0;
}
*Handle = (u32) Ep->dTDCurr;
/* Reset the descriptor's BufferPointer0 and Transfer Length fields to
* their original value. Note that we can not yet re-activate the
* descriptor as the caller will be using the attached buffer. Once the
* caller releases the buffer by calling XUsbPs_EpBufferRelease(), we
* can re-activate the descriptor.
*/
XUsbPs_WritedTD(Ep->dTDCurr, XUSBPS_dTDBPTR0, *BufferPtr);
XUsbPs_dTDSetTransferLen(Ep->dTDCurr, EpSetup->BufSize);
XUsbPs_dTDFlushCache(Ep->dTDCurr);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function returns a previously received data buffer to the driver.
*
* @param Handle is a pointer to the buffer that is returned.
*
* @return None.
*
******************************************************************************/
void XUsbPs_EpBufferRelease(u32 Handle)
{
XUsbPs_dTD *dTDPtr;
/* Perform sanity check on Handle.*/
Xil_AssertVoid((0 != Handle) && (0 == (Handle % XUSBPS_dTD_ALIGN)));
/* Activate the descriptor and clear the Terminate bit. Make sure to do
* the proper cache handling.
*/
dTDPtr = (XUsbPs_dTD *) Handle;
XUsbPs_dTDInvalidateCache(dTDPtr);
XUsbPs_dTDClrTerminate(dTDPtr);
XUsbPs_dTDSetActive(dTDPtr);
XUsbPs_dTDSetIOC(dTDPtr);
XUsbPs_dTDFlushCache(dTDPtr);
}
/*****************************************************************************/
/**
* This function sets the handler for endpoint events.
*
* @param InstancePtr is a pointer to the XUsbPs instance of the
* controller.
* @param EpNum is the number of the endpoint to receive data from.
* @param Direction is the direction of the endpoint (bitfield):
* - XUSBPS_EP_DIRECTION_OUT
* - XUSBPS_EP_DIRECTION_IN
* @param CallBackFunc is the Handler callback function.
* Can be NULL if the user wants to disable the handler entry.
* @param CallBackRef is the user definable data pointer that will be
* passed back if the handler is called. May be NULL.
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
* - XST_INVALID_PARAM: Invalid parameter passed.
*
* @note
* The user can disable a handler by setting the callback function
* pointer to NULL.
*
******************************************************************************/
int XUsbPs_EpSetHandler(XUsbPs *InstancePtr, u8 EpNum, u8 Direction,
XUsbPs_EpHandlerFunc CallBackFunc,
void *CallBackRef)
{
XUsbPs_Endpoint *Ep;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(CallBackFunc != NULL);
Xil_AssertNonvoid(EpNum < InstancePtr->DeviceConfig.NumEndpoints);
Ep = &InstancePtr->DeviceConfig.Ep[EpNum];
if(Direction & XUSBPS_EP_DIRECTION_OUT) {
Ep->Out.HandlerFunc = CallBackFunc;
Ep->Out.HandlerRef = CallBackRef;
}
if(Direction & XUSBPS_EP_DIRECTION_IN) {
Ep->In.HandlerFunc = CallBackFunc;
Ep->In.HandlerRef = CallBackRef;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function primes an endpoint.
*
* @param InstancePtr is pointer to the XUsbPs instance.
* @param EpNum is the number of the endpoint to receive data from.
* @param Direction is the direction of the endpoint (bitfield):
* - XUSBPS_EP_DIRECTION_OUT
* - XUSBPS_EP_DIRECTION_IN
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
* - XST_INVALID_PARAM: Invalid parameter passed.
*
* @note None.
*
******************************************************************************/
int XUsbPs_EpPrime(XUsbPs *InstancePtr, u8 EpNum, u8 Direction)
{
u32 Mask;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(EpNum < InstancePtr->DeviceConfig.NumEndpoints);
/* Get the right bit mask for the endpoint direction. */
switch (Direction) {
case XUSBPS_EP_DIRECTION_OUT:
Mask = 0x00000001;
break;
case XUSBPS_EP_DIRECTION_IN:
Mask = 0x00010000;
break;
default:
return XST_INVALID_PARAM;
}
/* Write the endpoint prime register. */
XUsbPs_WriteReg(InstancePtr->Config.BaseAddress,
XUSBPS_EPPRIME_OFFSET, Mask << EpNum);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function extracts the Setup Data from a given endpoint.
*
* @param InstancePtr is a pointer to the XUsbPs instance of the
* controller.
* @param EpNum is the number of the endpoint to receive data from.
* @param SetupDataPtr is a pointer to the setup data structure to be
* filled.
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
*
* @note None.
******************************************************************************/
int XUsbPs_EpGetSetupData(XUsbPs *InstancePtr, int EpNum,
XUsbPs_SetupData *SetupDataPtr)
{
XUsbPs_EpOut *Ep;
u32 Data[2];
u8 *p;
int Timeout;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(SetupDataPtr != NULL);
Xil_AssertNonvoid(EpNum < InstancePtr->DeviceConfig.NumEndpoints);
Ep = &InstancePtr->DeviceConfig.Ep[EpNum].Out;
/* Get the data from the Queue Heads Setup buffer into local variables
* so we can extract the setup data values.
*/
do {
/* Arm the tripwire. The tripwire will tell us if a new setup
* packet arrived (in which case the tripwire bit will be
* cleared) while we were reading the buffer. If a new setup
* packet arrived the buffer is corrupted and we continue
* reading.
*/
XUsbPs_SetSetupTripwire(InstancePtr);
XUsbPs_dQHInvalidateCache(Ep->dQH);
Data[0] = XUsbPs_ReaddQH(Ep->dQH, XUSBPS_dQHSUB0);
Data[1] = XUsbPs_ReaddQH(Ep->dQH, XUSBPS_dQHSUB1);
} while (FALSE == XUsbPs_SetupTripwireIsSet(InstancePtr));
/* Clear the pending endpoint setup stat bit.
*/
XUsbPs_WriteReg(InstancePtr->Config.BaseAddress,
XUSBPS_EPSTAT_OFFSET, 1 << EpNum);
/* Clear the Tripwire bit and continue.
*/
XUsbPs_ClrSetupTripwire(InstancePtr);
/* Data in the setup buffer is being converted by the core to big
* endian format. We have to take care of proper byte swapping when
* reading the setup data values.
*
* Need to check if there is a smarter way to do this and take the
* processor/memory-controller endianess into account?
*/
p = (u8 *) Data;
SetupDataPtr->bmRequestType = p[0];
SetupDataPtr->bRequest = p[1];
SetupDataPtr->wValue = (p[3] << 8) | p[2];
SetupDataPtr->wIndex = (p[5] << 8) | p[4];
SetupDataPtr->wLength = (p[7] << 8) | p[6];
/* Before we leave we need to make sure that the endpoint setup bit has
* cleared. It needs to be 0 before the endpoint can be re-primed.
*
* Note: According to the documentation this endpoint setup bit should
* clear within 1-2us after it has been written above. This means that
* we should never catch it being 1 here. However, we still need to
* poll it to make sure. Just in case, we use a counter 'Timeout' so we
* won't hang here if the bit is stuck for some reason.
*/
Timeout = XUSBPS_TIMEOUT_COUNTER;
while ((XUsbPs_ReadReg(InstancePtr->Config.BaseAddress,
XUSBPS_EPSTAT_OFFSET) &
(1 << EpNum)) && --Timeout) {
/* NOP */
}
if (0 == Timeout) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function initializes the endpoint pointer data structure.
*
* The function sets up the local data structure with the aligned addresses for
* the Queue Head and Transfer Descriptors.
*
* @param DevCfgPtr is pointer to the XUsbPs DEVICE configuration
* structure.
*
* @return none
*
* @note
* Endpoints of type XUSBPS_EP_TYPE_NONE are not used in the
* system. Therefore no memory is reserved for them.
*
******************************************************************************/
static void XUsbPs_EpListInit(XUsbPs_DeviceConfig *DevCfgPtr)
{
int EpNum;
u8 *p;
XUsbPs_Endpoint *Ep;
XUsbPs_EpConfig *EpCfg;
/* Set up the XUsbPs_Endpoint array. This array is used to define the
* location of the Queue Head list and the Transfer Descriptors in the
* block of DMA memory that has been passed into the driver.
*
* 'p' is used to set the pointers in the local data structure.
* Initially 'p' is pointed to the beginning of the DMAable memory
* block. As pointers are assigned, 'p' is incremented by the size of
* the respective object.
*/
Ep = DevCfgPtr->Ep;
EpCfg = DevCfgPtr->EpCfg;
/* Start off with 'p' pointing to the (aligned) beginning of the DMA
* buffer.
*/
p = (u8 *) DevCfgPtr->PhysAligned;
/* Initialize the Queue Head pointer list.
*
* Each endpoint has two Queue Heads. One for the OUT direction and one
* for the IN direction. An OUT Queue Head is always followed by an IN
* Queue Head.
*
* Queue Head alignment is XUSBPS_dQH_ALIGN.
*
* Note that we have to reserve space here for unused endpoints.
*/
for (EpNum = 0; EpNum < DevCfgPtr->NumEndpoints; ++EpNum) {
/* OUT Queue Head */
Ep[EpNum].Out.dQH = (XUsbPs_dQH *) p;
p += XUSBPS_dQH_ALIGN;
/* IN Queue Head */
Ep[EpNum].In.dQH = (XUsbPs_dQH *) p;
p += XUSBPS_dQH_ALIGN;
}
/* 'p' now points to the first address after the Queue Head list. The
* Transfer Descriptors start here.
*
* Each endpoint has a variable number of Transfer Descriptors
* depending on user configuration.
*
* Transfer Descriptor alignment is XUSBPS_dTD_ALIGN.
*/
for (EpNum = 0; EpNum < DevCfgPtr->NumEndpoints; ++EpNum) {
/* OUT Descriptors.
*/
if (XUSBPS_EP_TYPE_NONE != EpCfg[EpNum].Out.Type) {
Ep[EpNum].Out.dTDs = (XUsbPs_dTD *) p;
Ep[EpNum].Out.dTDCurr = (XUsbPs_dTD *) p;
p += XUSBPS_dTD_ALIGN * EpCfg[EpNum].Out.NumBufs;
}
/* IN Descriptors.
*/
if (XUSBPS_EP_TYPE_NONE != EpCfg[EpNum].In.Type) {
Ep[EpNum].In.dTDs = (XUsbPs_dTD *) p;
Ep[EpNum].In.dTDHead = (XUsbPs_dTD *) p;
Ep[EpNum].In.dTDTail = (XUsbPs_dTD *) p;
p += XUSBPS_dTD_ALIGN * EpCfg[EpNum].In.NumBufs;
}
}
/* 'p' now points to the first address after the Transfer Descriptors.
* The data buffers for the OUT Transfer Desciptors start here.
*
* Note that IN (TX) Transfer Descriptors are not assigned buffers at
* this point. Buffers will be assigned when the user calls the send()
* function.
*/
for (EpNum = 0; EpNum < DevCfgPtr->NumEndpoints; ++EpNum) {
if (XUSBPS_EP_TYPE_NONE != EpCfg[EpNum].Out.Type) {
/* If BufSize for this endpoint is set to 0 it means
* that we do not need to attach a buffer to this
* descriptor. We also initialize it's buffer pointer
* to NULL.
*/
if (0 == EpCfg[EpNum].Out.BufSize) {
Ep[EpNum].Out.dTDBufs = NULL;
continue;
}
Ep[EpNum].Out.dTDBufs = p;
p += EpCfg[EpNum].Out.BufSize * EpCfg[EpNum].Out.NumBufs;
}
}
/* Initialize the endpoint event handlers to NULL.
*/
for (EpNum = 0; EpNum < DevCfgPtr->NumEndpoints; ++EpNum) {
Ep[EpNum].Out.HandlerFunc = NULL;
Ep[EpNum].In.HandlerFunc = NULL;
}
}
/*****************************************************************************/
/**
*
* This function initializes the Queue Head List in memory.
*
* @param DevCfgPtr is a pointer to the XUsbPs DEVICE configuration
* structure.
*
* @return None
*
* @note None.
*
******************************************************************************/
static void XUsbPs_dQHInit(XUsbPs_DeviceConfig *DevCfgPtr)
{
int EpNum;
XUsbPs_Endpoint *Ep;
XUsbPs_EpConfig *EpCfg;
/* Setup pointers for simpler access. */
Ep = DevCfgPtr->Ep;
EpCfg = DevCfgPtr->EpCfg;
/* Go through the list of Queue Head entries and:
*
* - Set Transfer Descriptor addresses
* - Set Maximum Packet Size
* - Disable Zero Length Termination (ZLT) for non-isochronous transfers
* - Enable Interrupt On Setup (IOS)
*
*/
for (EpNum = 0; EpNum < DevCfgPtr->NumEndpoints; ++EpNum) {
/* OUT Queue Heads.*/
if (XUSBPS_EP_TYPE_NONE != EpCfg[EpNum].Out.Type) {
XUsbPs_WritedQH(Ep[EpNum].Out.dQH,
XUSBPS_dQHCPTR, Ep[EpNum].Out.dTDs);
/* For isochronous, ep max packet size translates to different
* values in queue head than other types.
* Also enable ZLT for isochronous.
*/
if(XUSBPS_EP_TYPE_ISOCHRONOUS == EpCfg[EpNum].Out.Type) {
XUsbPs_dQHSetMaxPacketLenISO(Ep[EpNum].Out.dQH,
EpCfg[EpNum].Out.MaxPacketSize);
XUsbPs_dQHEnableZLT(Ep[EpNum].Out.dQH);
}else {
XUsbPs_dQHSetMaxPacketLen(Ep[EpNum].Out.dQH,
EpCfg[EpNum].Out.MaxPacketSize);
XUsbPs_dQHDisableZLT(Ep[EpNum].Out.dQH);
}
/* Only control OUT needs this */
if(XUSBPS_EP_TYPE_CONTROL == EpCfg[EpNum].Out.Type) {
XUsbPs_dQHSetIOS(Ep[EpNum].Out.dQH);
}
/* Set up the overlay next dTD pointer. */
XUsbPs_WritedQH(Ep[EpNum].Out.dQH,
XUSBPS_dQHdTDNLP, Ep[EpNum].Out.dTDs);
XUsbPs_dQHFlushCache(Ep[EpNum].Out.dQH);
}
/* IN Queue Heads. */
if (XUSBPS_EP_TYPE_NONE != EpCfg[EpNum].In.Type) {
XUsbPs_WritedQH(Ep[EpNum].In.dQH,
XUSBPS_dQHCPTR, Ep[EpNum].In.dTDs);
/* Isochronous ep packet size can be larger than 1024.*/
if(XUSBPS_EP_TYPE_ISOCHRONOUS == EpCfg[EpNum].In.Type) {
XUsbPs_dQHSetMaxPacketLenISO(Ep[EpNum].In.dQH,
EpCfg[EpNum].In.MaxPacketSize);
XUsbPs_dQHEnableZLT(Ep[EpNum].In.dQH);
}else {
XUsbPs_dQHSetMaxPacketLen(Ep[EpNum].In.dQH,
EpCfg[EpNum].In.MaxPacketSize);
XUsbPs_dQHDisableZLT(Ep[EpNum].In.dQH);
}
XUsbPs_dQHFlushCache(Ep[EpNum].In.dQH);
}
}
}
/*****************************************************************************/
/**
*
* This function initializes the Transfer Descriptors lists in memory.
*
* @param DevCfgPtr is a pointer to the XUsbPs DEVICE configuration
* structure.
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
*
******************************************************************************/
static int XUsbPs_dTDInit(XUsbPs_DeviceConfig *DevCfgPtr)
{
int EpNum;
XUsbPs_Endpoint *Ep;
XUsbPs_EpConfig *EpCfg;
/* Setup pointers for simpler access. */
Ep = DevCfgPtr->Ep;
EpCfg = DevCfgPtr->EpCfg;
/* Walk through the list of endpoints and initialize their Transfer
* Descriptors.
*/
for (EpNum = 0; EpNum < DevCfgPtr->NumEndpoints; ++EpNum) {
int Td;
int NumdTD;
XUsbPs_EpOut *Out = &Ep[EpNum].Out;
XUsbPs_EpIn *In = &Ep[EpNum].In;
/* OUT Descriptors
* ===============
*
* + Set the next link pointer
* + Set the interrupt complete and the active bit
* + Attach the buffer to the dTD
*/
if (XUSBPS_EP_TYPE_NONE != EpCfg[EpNum].Out.Type) {
NumdTD = EpCfg[EpNum].Out.NumBufs;
}
else {
NumdTD = 0;
}
for (Td = 0; Td < NumdTD; ++Td) {
int Status;
int NextTd = (Td + 1) % NumdTD;
XUsbPs_dTDInvalidateCache(&Out->dTDs[Td]);
/* Set NEXT link pointer. */
XUsbPs_WritedTD(&Out->dTDs[Td], XUSBPS_dTDNLP,
&Out->dTDs[NextTd]);
/* Set the OUT descriptor ACTIVE and enable the
* interrupt on complete.
*/
XUsbPs_dTDSetActive(&Out->dTDs[Td]);
XUsbPs_dTDSetIOC(&Out->dTDs[Td]);
/* Set up the data buffer with the descriptor. If the
* buffer pointer is NULL it means that we do not need
* to attach a buffer to this descriptor.
*/
if (NULL == Out->dTDBufs) {
XUsbPs_dTDFlushCache(&Out->dTDs[Td]);
continue;
}
Status = XUsbPs_dTDAttachBuffer(
&Out->dTDs[Td],
Out->dTDBufs +
(Td * EpCfg[EpNum].Out.BufSize),
EpCfg[EpNum].Out.BufSize);
if (XST_SUCCESS != Status) {
return XST_FAILURE;
}
XUsbPs_dTDFlushCache(&Out->dTDs[Td]);
}
/* IN Descriptors
* ==============
*
* + Set the next link pointer
* + Set the Terminate bit to mark it available
*/
if (XUSBPS_EP_TYPE_NONE != EpCfg[EpNum].In.Type) {
NumdTD = EpCfg[EpNum].In.NumBufs;
}
else {
NumdTD = 0;
}
for (Td = 0; Td < NumdTD; ++Td) {
int NextTd = (Td + 1) % NumdTD;
XUsbPs_dTDInvalidateCache(&In->dTDs[Td]);
/* Set NEXT link pointer. */
XUsbPs_WritedTD(In->dTDs[Td], XUSBPS_dTDNLP,
In->dTDs[NextTd]);
/* Set the IN descriptor's TERMINATE bits. */
XUsbPs_dTDSetTerminate(In->dTDs[Td]);
XUsbPs_dTDFlushCache(&In->dTDs[Td]);
}
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function associates a buffer with a Transfer Descriptor. The function
* will take care of splitting the buffer into multiple 4kB aligned segments if
* the buffer happens to span one or more 4kB pages.
*
* @param dTDIndex is a pointer to the Transfer Descriptor
* @param BufferPtr is pointer to the buffer to link to the descriptor.
* @param BufferLen is the length of the buffer.
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
* - XST_USB_BUF_TOO_BIG: The provided buffer is bigger than tha
* maximum allowed buffer size (16k).
*
* @note
* Cache invalidation and flushing needs to be handler by the
* caller of this function.
*
******************************************************************************/
static int XUsbPs_dTDAttachBuffer(XUsbPs_dTD *dTDPtr,
const u8 *BufferPtr, u32 BufferLen)
{
u32 BufAddr;
u32 BufEnd;
u32 PtrNum;
Xil_AssertNonvoid(dTDPtr != NULL);
/* Check if the buffer is smaller than 16kB. */
if (BufferLen > XUSBPS_dTD_BUF_MAX_SIZE) {
return XST_USB_BUF_TOO_BIG;
}
/* Get a u32 of the buffer pointer to avoid casting in the following
* logic operations.
*/
BufAddr = (u32) BufferPtr;
/* Set the buffer pointer 0. Buffer pointer 0 can point to any location
* in memory. It does not need to be 4kB aligned. However, if the
* provided buffer spans one or more 4kB boundaries, we need to set up
* the subsequent buffer pointers which must be 4kB aligned.
*/
XUsbPs_WritedTD(dTDPtr, XUSBPS_dTDBPTR(0), BufAddr);
/* Check if the buffer spans a 4kB boundary.
*
* Only do this check, if we are not sending a 0-length buffer.
*/
if (BufferLen > 0) {
BufEnd = BufAddr + BufferLen -1;
PtrNum = 1;
while ((BufAddr & 0xFFFFF000) != (BufEnd & 0xFFFFF000)) {
/* The buffer spans at least one boundary, let's set
* the next buffer pointer and repeat the procedure
* until the end of the buffer and the pointer written
* are in the same 4kB page.
*/
BufAddr = (BufAddr + 0x1000) & 0xFFFFF000;
XUsbPs_WritedTD(dTDPtr, XUSBPS_dTDBPTR(PtrNum),
BufAddr);
PtrNum++;
}
}
/* Set the length of the buffer. */
XUsbPs_dTDSetTransferLen(dTDPtr, BufferLen);
/* We remember the buffer pointer in the user data field (reserved
* field in the dTD). This makes it easier to reset the buffer pointer
* after a buffer has been received on the endpoint. The buffer pointer
* needs to be reset because the DMA engine modifies the buffer pointer
* while receiving.
*/
XUsbPs_WritedTD(dTDPtr, XUSBPS_dTDUSERDATA, BufferPtr);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function set the Max PacketLen for the queue head for isochronous EP.
*
* If the max packet length is greater than XUSBPS_MAX_PACKET_SIZE, then
* Mult bits are set to reflect that.
*
* @param dQHPtr is a pointer to the dQH element.
* @param Len is the Length to be set.
*
******************************************************************************/
static void XUsbPs_dQHSetMaxPacketLenISO(XUsbPs_dQH *dQHPtr, u32 Len)
{
u32 Mult = (Len & ENDPOINT_MAXP_MULT_MASK) >> ENDPOINT_MAXP_MULT_SHIFT;
u32 MaxPktSize = (Mult > 1) ? ENDPOINT_MAXP_LENGTH : Len;
if (MaxPktSize > XUSBPS_MAX_PACKET_SIZE) {
return;
}
if (Mult > 3) {
return;
}
/* Set Max packet size */
XUsbPs_WritedQH(dQHPtr, XUSBPS_dQHCFG,
(XUsbPs_ReaddQH(dQHPtr, XUSBPS_dQHCFG) &
~XUSBPS_dQHCFG_MPL_MASK) |
(MaxPktSize << XUSBPS_dQHCFG_MPL_SHIFT));
/* Set Mult to tell hardware how many transactions in each microframe */
XUsbPs_WritedQH(dQHPtr, XUSBPS_dQHCFG,
(XUsbPs_ReaddQH(dQHPtr, XUSBPS_dQHCFG) &
~XUSBPS_dQHCFG_MULT_MASK) |
(Mult << XUSBPS_dQHCFG_MULT_SHIFT));
}
/*****************************************************************************/
/**
* This function reconfigures one Ep corresponding to host's request of setting
* alternate interface. The endpoint has been disabled before this call.
*
* Both QH and dTDs are updated for the new configuration.
*
* @param InstancePtr is a pointer to the XUsbPs instance of the
* controller.
* @param CfgPtr
* Pointer to the updated XUsbPs DEVICE configuration structure.
*
* @param EpNum
* The endpoint to be reconfigured.
*
* @param NewDirection
* The new transfer direction the endpoint.
*
* @param DirectionChanged
* A boolean value indicate whether the transfer direction has changed.
*
* @return
* XST_SUCCESS upon success, XST_FAILURE otherwise.
*
******************************************************************************/
int XUsbPs_ReconfigureEp(XUsbPs *InstancePtr, XUsbPs_DeviceConfig *CfgPtr,
int EpNum, unsigned short NewDirection,
int DirectionChanged) {
int Status = XST_SUCCESS;
XUsbPs_Endpoint *Ep;
XUsbPs_EpConfig *EpCfg;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(CfgPtr != NULL);
Ep = CfgPtr->Ep;
EpCfg = CfgPtr->EpCfg;
/* If transfer direction changes, dTDs has to be reset
* Number of buffers are preset and should not to be changed.
*/
if(DirectionChanged) {
if(NewDirection == XUSBPS_EP_DIRECTION_OUT) {
u8 *p;
/* Swap the pointer to the dTDs.
*/
Ep[EpNum].Out.dTDs = Ep[EpNum].In.dTDs;
p = (u8 *)(Ep[EpNum].Out.dTDs + XUSBPS_dTD_ALIGN * EpCfg[EpNum].Out.NumBufs);
/* Set the OUT buffer if buffer size is not zero
*/
if(EpCfg[EpNum].Out.BufSize > 0) {
Ep[EpNum].Out.dTDBufs = p;
}
} else if(NewDirection == XUSBPS_EP_DIRECTION_IN) {
Ep[EpNum].In.dTDs = Ep[EpNum].Out.dTDs;
}
}
/* Reset dTD progress tracking pointers
*/
if(NewDirection == XUSBPS_EP_DIRECTION_IN) {
Ep[EpNum].In.dTDHead = Ep[EpNum].In.dTDTail = Ep[EpNum].In.dTDs;
} else if(NewDirection == XUSBPS_EP_DIRECTION_OUT) {
Ep[EpNum].Out.dTDCurr = Ep[EpNum].Out.dTDs;
}
/* Reinitialize information in QH
*/
XUsbPs_dQHReinitEp(CfgPtr, EpNum, NewDirection);
/* Reinitialize the dTD linked list, and flush the cache
*/
Status = XUsbPs_dTDReinitEp(CfgPtr, EpNum, NewDirection);
if(Status != XST_SUCCESS) {
return Status;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function re-initializes the Queue Head List in memory.
* The endpoint 1 has been disabled before this call.
*
* @param DevCfgPtr
* Pointer to the updated XUsbPs DEVICE configuration structure.
*
* @param EpNum
* The endpoint to be reconfigured.
*
* @param NewDirection
* The new transfer direction of endpoint 1
*
* @return none
*
******************************************************************************/
static void XUsbPs_dQHReinitEp(XUsbPs_DeviceConfig *DevCfgPtr,
int EpNum, unsigned short NewDirection)
{
XUsbPs_Endpoint *Ep;
XUsbPs_EpConfig *EpCfg;
/* Setup pointers for simpler access.
*/
Ep = DevCfgPtr->Ep;
EpCfg = DevCfgPtr->EpCfg;
/* Go through the list of Queue Head entries and:
*
* - Set Transfer Descriptor addresses
* - Set Maximum Packet Size
* - Disable Zero Length Termination (ZLT) for non-isochronous transfers
* - Enable Interrupt On Setup (IOS)
*
*/
if(NewDirection == XUSBPS_EP_DIRECTION_OUT) {
/* OUT Queue Heads.
*/
XUsbPs_WritedQH(Ep[EpNum].Out.dQH,
XUSBPS_dQHCPTR, Ep[EpNum].Out.dTDs);
/* For isochronous, ep max packet size translates to different
* values in queue head than other types.
* Also enable ZLT for isochronous.
*/
if(XUSBPS_EP_TYPE_ISOCHRONOUS == EpCfg[EpNum].Out.Type) {
XUsbPs_dQHSetMaxPacketLenISO(Ep[EpNum].Out.dQH,
EpCfg[EpNum].Out.MaxPacketSize);
XUsbPs_dQHEnableZLT(Ep[EpNum].Out.dQH);
}else {
XUsbPs_dQHSetMaxPacketLen(Ep[EpNum].Out.dQH,
EpCfg[EpNum].Out.MaxPacketSize);
XUsbPs_dQHDisableZLT(Ep[EpNum].Out.dQH);
}
XUsbPs_dQHSetIOS(Ep[EpNum].Out.dQH);
/* Set up the overlay next dTD pointer.
*/
XUsbPs_WritedQH(Ep[EpNum].Out.dQH,
XUSBPS_dQHdTDNLP, Ep[EpNum].Out.dTDs);
XUsbPs_dQHFlushCache(Ep[EpNum].Out.dQH);
} else if(NewDirection == XUSBPS_EP_DIRECTION_IN) {
/* IN Queue Heads.
*/
XUsbPs_WritedQH(Ep[EpNum].In.dQH,
XUSBPS_dQHCPTR, Ep[EpNum].In.dTDs);
/* Isochronous ep packet size can be larger than 1024. */
if(XUSBPS_EP_TYPE_ISOCHRONOUS == EpCfg[EpNum].In.Type) {
XUsbPs_dQHSetMaxPacketLenISO(Ep[EpNum].In.dQH,
EpCfg[EpNum].In.MaxPacketSize);
XUsbPs_dQHEnableZLT(Ep[EpNum].In.dQH);
}else {
XUsbPs_dQHSetMaxPacketLen(Ep[EpNum].In.dQH,
EpCfg[EpNum].In.MaxPacketSize);
XUsbPs_dQHDisableZLT(Ep[EpNum].In.dQH);
}
XUsbPs_dQHSetIOS(Ep[EpNum].In.dQH);
XUsbPs_dQHFlushCache(Ep[EpNum].In.dQH);
}
}
/*****************************************************************************/
/**
*
* This function re-initializes the Transfer Descriptors lists in memory.
* The endpoint has been disabled before the call. The transfer descriptors
* list pointer has been initialized too.
*
* @param DevCfgPtr
* Pointer to the XUsbPs DEVICE configuration structure.
*
* @param EpNum
* The endpoint to be reconfigured.
*
* @param NewDirection
* The new transfer direction of endpoint 1
*
* @return
* - XST_SUCCESS: The operation completed successfully.
* - XST_FAILURE: An error occured.
*
******************************************************************************/
static int XUsbPs_dTDReinitEp(XUsbPs_DeviceConfig *DevCfgPtr,
int EpNum, unsigned short NewDirection)
{
XUsbPs_Endpoint *Ep;
XUsbPs_EpConfig *EpCfg;
int Td;
int NumdTD;
/* Setup pointers for simpler access.
*/
Ep = DevCfgPtr->Ep;
EpCfg = DevCfgPtr->EpCfg;
if(NewDirection == XUSBPS_EP_DIRECTION_OUT) {
XUsbPs_EpOut *Out = &Ep[EpNum].Out;
/* OUT Descriptors
* ===============
*
* + Set the next link pointer
* + Set the interrupt complete and the active bit
* + Attach the buffer to the dTD
*/
NumdTD = EpCfg[EpNum].Out.NumBufs;
for (Td = 0; Td < NumdTD; ++Td) {
int Status;
int NextTd = (Td + 1) % NumdTD;
XUsbPs_dTDInvalidateCache(&Out->dTDs[Td]);
/* Set NEXT link pointer.
*/
XUsbPs_WritedTD(&Out->dTDs[Td], XUSBPS_dTDNLP,
&Out->dTDs[NextTd]);
/* Set the OUT descriptor ACTIVE and enable the
* interrupt on complete.
*/
XUsbPs_dTDSetActive(&Out->dTDs[Td]);
XUsbPs_dTDSetIOC(&Out->dTDs[Td]);
/* Set up the data buffer with the descriptor. If the
* buffer pointer is NULL it means that we do not need
* to attach a buffer to this descriptor.
*/
if (Out->dTDBufs != NULL) {
Status = XUsbPs_dTDAttachBuffer(
&Out->dTDs[Td],
Out->dTDBufs +
(Td * EpCfg[EpNum].Out.BufSize),
EpCfg[EpNum].Out.BufSize);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
}
XUsbPs_dTDFlushCache(&Out->dTDs[Td]);
}
} else if(NewDirection == XUSBPS_EP_DIRECTION_IN) {
XUsbPs_EpIn *In = &Ep[EpNum].In;
/* IN Descriptors
* ==============
*
* + Set the next link pointer
* + Set the Terminate bit to mark it available
*/
NumdTD = EpCfg[EpNum].In.NumBufs;
for (Td = 0; Td < NumdTD; ++Td) {
int NextTd = (Td + 1) % NumdTD;
XUsbPs_dTDInvalidateCache(&In->dTDs[Td]);
/* Set NEXT link pointer.
*/
XUsbPs_WritedTD(&In->dTDs[Td], XUSBPS_dTDNLP,
&In->dTDs[NextTd]);
/* Set the IN descriptor's TERMINATE bits.
*/
XUsbPs_dTDSetTerminate(&In->dTDs[Td]);
XUsbPs_dTDFlushCache(&In->dTDs[Td]);
}
}
return XST_SUCCESS;
}
/** @} */