/******************************************************************************
* Copyright (C) 2010 - 2021 Xilinx, Inc.  All rights reserved.
* Copyright (C) 2023 - 2026 Advanced Micro Devices, Inc. All Rights Reserved.
* SPDX-License-Identifier: MIT
******************************************************************************/

/******************************************************************************/
/**
 * @file xusbps_endpoint.c
* @addtogroup usbps Overview
* @{
 *
 * 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...)
 * 2.5   pm  02/20/20 Added ISO endpoint support.
 * 2.11  ka  01/29/26 Fix codespell errors.
 * </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 occurred.
 *
 * @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 occurred.
*		- 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 occurred.
*		- 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 occurred.
*		- 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 		EpType;
	u32 		Multo = 0;
	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;
	EpType = InstancePtr->DeviceConfig.EpCfg[EpNum].In.Type;

	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;

	Ep->RequestedBytes = BufferLen;
	Ep->BytesTxed = 0;
	Ep->BufferPtr = (u8 *)BufferPtr;

	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;
		Ep->BytesTxed += Length;

		if (EpType == XUSBPS_EP_TYPE_ISOCHRONOUS) {
			Multo = BufferLen /
				InstancePtr->
				DeviceConfig.EpCfg[EpNum].Out.MaxPacketSize;
			if (BufferLen == 0 ||
			    (BufferLen %
			     InstancePtr->
			     DeviceConfig.EpCfg[EpNum].
			     Out.MaxPacketSize)) {
				Multo++;
			}

			XUsbPs_dTDSetMultO(Ep->dTDHead, (Multo << 10));
		}

		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 and pass it to the application.
 *
 * @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	BufferLen is holding the buffer length.
 *
 * @return	None.
 *
 * @note	None.
 *
 ******************************************************************************/
void XUsbPs_EpGetData(XUsbPs *InstancePtr, u8 EpNum, u32 BufferLen)
{
	XUsbPs_EpOut	*Ep;
	XUsbPs_EpSetup	*EpSetup;
	u8 *DataBuff;
	u32 Handle;
	u32 length = 0;
	u32 InavalidateLen = 0;

	/* The buffer is not active which means that it has been processed by
	 * the DMA engine and contains valid data.
	 */
	Ep = &InstancePtr->DeviceConfig.Ep[EpNum].Out;
	EpSetup = &InstancePtr->DeviceConfig.EpCfg[EpNum].Out;

	/* Use the buffer pointer stored in the "user data" field of the
	 * Transfer Descriptor.
	*/

	do {
		XUsbPs_dTDInvalidateCache(Ep->dTDCurr);

		DataBuff = (u8 *) XUsbPs_ReaddTD(Ep->dTDCurr,
						 XUSBPS_dTDUSERDATA);

		length = EpSetup->BufSize -
			 XUsbPs_dTDGetTransferLen(Ep->dTDCurr);

		if (length > 0) {
			BufferLen = length;
		} else {
			BufferLen = 0;
		}

		/* Invalidate the Buffer Pointer */
		InavalidateLen =  BufferLen;
		if (BufferLen % 32) {
			InavalidateLen = (BufferLen / 32) * 32 + 32;
		}

		Xil_DCacheInvalidateRange((unsigned int)DataBuff,
					  InavalidateLen);

		memcpy(Ep->BufferPtr, DataBuff,  BufferLen);

		Ep->BytesTxed += BufferLen;
		Ep->BufferPtr += BufferLen;

		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, DataBuff);
		XUsbPs_dTDSetTransferLen(Ep->dTDCurr, EpSetup->BufSize);

		XUsbPs_dTDFlushCache(Ep->dTDCurr);

		XUsbPs_EpBufferRelease(Handle);

		Ep->dTDCurr = XUsbPs_dTDGetNLP(Ep->dTDCurr);
		XUsbPs_dTDInvalidateCache(Ep->dTDCurr);

		/* Either Host sent exact requested length or more than that
		 * or lesser data.
		 */
		if ((Ep->RequestedBytes <= Ep->BytesTxed) ||
		    (Ep->BytesTxed % EpSetup->MaxPacketSize)) {
			Ep->MemAlloted  = 0;
			if (Ep->HandlerIsoFunc) {
				Ep->HandlerIsoFunc(Ep->HandlerRef,
						   Ep->RequestedBytes,
						   Ep->BytesTxed);
			}

			break;
		}
	} while (!XUsbPs_dTDIsActive(Ep->dTDCurr));

}

/*****************************************************************************/
/**
 * 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 occurred.
 *		- 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 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	BufferLen (OUT param) is a pointer to the integer that will
 *		hold the buffer length.
 *
 * @return
 *		- XST_SUCCESS: The operation completed successfully.
 *		- XST_FAILURE: An error occurred.
 *		- XST_USB_NO_BUF: No buffer available.
 *
 * @note
 *
 ******************************************************************************/
s32 XUsbPs_EpDataBufferReceive(XUsbPs *InstancePtr, u8 EpNum,
			       u8 *BufferPtr, u32 BufferLen)
{
	XUsbPs_EpOut	*Ep;

	Xil_AssertNonvoid(InstancePtr  != NULL);
	Xil_AssertNonvoid(BufferPtr    != NULL);
	Xil_AssertNonvoid(EpNum < InstancePtr->DeviceConfig.NumEndpoints);

	Ep = &InstancePtr->DeviceConfig.Ep[EpNum].Out;

	Ep->RequestedBytes = BufferLen;
	Ep->BytesTxed = 0;
	Ep->BufferPtr = BufferPtr;
	Ep->MemAlloted = 1;

	XUsbPs_dTDInvalidateCache(Ep->dTDCurr);

	/* Locate the next available buffer in the ring. A buffer is available
	 * if its descriptor is not active.
	 */
	if (XUsbPs_dTDIsActive(Ep->dTDCurr)) {
		return XST_USB_NO_BUF;
	}
	XUsbPs_EpGetData(InstancePtr, EpNum, BufferLen);

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
 * 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 occurred.
 *		- 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 sets the handler for ISO 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.
 *
 * @return
 *		- XST_SUCCESS: The operation completed successfully.
 *		- XST_FAILURE: An error occurred.
 *		- XST_INVALID_PARAM: Invalid parameter passed.
 *
 * @note
 * 		The user can disable a handler by setting the callback function
 * 		pointer to NULL.
 *
 ******************************************************************************/
s32 XUsbPs_EpSetIsoHandler(XUsbPs *InstancePtr, u8 EpNum, u8 Direction,
			   XUsbPs_EpIsoHandlerFunc CallBackFunc)
{
	XUsbPs_Endpoint	*Ep;
	void *CallBackRef = (void *) InstancePtr->AppData;

	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.HandlerIsoFunc	= CallBackFunc;
		Ep->Out.HandlerRef	= CallBackRef;
	}

	if (Direction & XUSBPS_EP_DIRECTION_IN) {
		Ep->In.HandlerIsoFunc	= 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 occurred.
*		- 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 occurred.
*
* @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 endianness 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 Descriptors 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;
		Ep[EpNum].Out.HandlerIsoFunc = NULL;
		Ep[EpNum].In.HandlerIsoFunc  = 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 occurred.
 *
 ******************************************************************************/
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 occurred.
 *		- XST_USB_BUF_TOO_BIG: The provided buffer is bigger than the
 *		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 occurred.
 *
 ******************************************************************************/
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;
}

/** @} */
