/****************************************************************************** * * Copyright (C) 2010 - 2014 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 THE * XILINX CONSORTIUM 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 * * Endpoint specific function implementations. * * @note None. * *
 * 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.
 * 
******************************************************************************/ /***************************** Include Files **********************************/ #include /* for bzero() */ #include #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 { 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); /* Tell the caller if we do not have any descriptors available. */ if (XUsbPs_dTDIsActive(Ep->dTDHead)) { return XST_USB_NO_DESC_AVAILABLE; } 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; }