/** * This function handles TX buffer interrupts. It is called by the interrupt * when a transmit complete interrupt occurs. It returns buffers of completed * descriptors to the caller. * * @param InstancePtr is a pointer to the XUsbPs instance of the * controller. * @param EpCompl is the Bit mask of endpoints that caused a transmit * complete interrupt. * * @return None * * @note None. * ******************************************************************************/ static void XUsbPs_IntrHandleTX(XUsbPs *InstancePtr, u32 EpCompl) { int Index; u32 Mask; int NumEp; /* Check all endpoints for TX complete bits. */ Mask = 0x00010000; NumEp = InstancePtr->DeviceConfig.NumEndpoints; /* Check for every endpoint if its TX complete bit is * set. */ for (Index = 0; Index < NumEp; Index++, Mask <<= 1) { XUsbPs_EpIn *Ep; if (!(EpCompl & Mask)) { continue; } /* The TX complete bit for this endpoint is * set. Walk the list of descriptors to see * which ones are completed. */ Ep = &InstancePtr->DeviceConfig.Ep[Index].In; while (Ep->dTDTail != Ep->dTDHead) { XUsbPs_dTDInvalidateCache(Ep->dTDTail); /* If the descriptor is not active then the buffer has * not been sent yet. */ if (XUsbPs_dTDIsActive(Ep->dTDTail)) { break; } if (Ep->HandlerFunc) { void *BufPtr; BufPtr = (void *) XUsbPs_ReaddTD(Ep->dTDTail, XUSBPS_dTDUSERDATA); Ep->HandlerFunc(Ep->HandlerRef, Index, XUSBPS_EP_EVENT_DATA_TX, BufPtr); } Ep->dTDTail = XUsbPs_dTDGetNLP(Ep->dTDTail); } } }
/** * This function handles RX buffer interrupts. It is called by the interrupt * when a receive complete interrupt occurs. It notifies the callback functions * that have been registered with the individual endpoints that data has been * received. * * @param InstancePtr * Pointer to the XUsbPs instance of the controller. * * @param EpCompl * Bit mask of endpoints that caused a receive complete interrupt. * @return * none * ******************************************************************************/ static void XUsbPs_IntrHandleRX(XUsbPs *InstancePtr, u32 EpCompl) { XUsbPs_EpOut *Ep; int Index; u32 Mask; int NumEp; /* Check all endpoints for RX complete bits. */ Mask = 0x00000001; NumEp = InstancePtr->DeviceConfig.NumEndpoints; /* Check for every endpoint if its RX complete bit is set.*/ for (Index = 0; Index < NumEp; Index++, Mask <<= 1) { int numP = 0; if (!(EpCompl & Mask)) { continue; } Ep = &InstancePtr->DeviceConfig.Ep[Index].Out; XUsbPs_dTDInvalidateCache(Ep->dTDCurr); /* Handle all finished dTDs */ while (!XUsbPs_dTDIsActive(Ep->dTDCurr)) { numP += 1; if (Ep->HandlerFunc) { Ep->HandlerFunc(Ep->HandlerRef, Index, XUSBPS_EP_EVENT_DATA_RX, NULL); } Ep->dTDCurr = XUsbPs_dTDGetNLP(Ep->dTDCurr); XUsbPs_dTDInvalidateCache(Ep->dTDCurr); } /* Re-Prime the endpoint.*/ XUsbPs_EpPrime(InstancePtr, Index, XUSBPS_EP_DIRECTION_OUT); } }
/** * 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 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 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 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) { 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; Xil_AssertNonvoid(InstancePtr != 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].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) XUsbPs_dTDSetIOC(Ep->dTDHead); 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; } } while(BufferLen); 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 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; }