/** * 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 is the first-level interrupt handler for the USB core. All USB * interrupts will be handled here. Depending on the type of the interrupt, * second level interrupt handler may be called. Second level interrupt * handlers will be registered by the user using the: * XUsbPs_IntrSetHandler() * and/or * XUsbPs_EpSetHandler() * functions. * * * @param HandlerRef is a Reference passed to the interrupt register * function. In our case this will be a pointer to the XUsbPs * instance. * * @return None * * @note None * ******************************************************************************/ void XUsbPs_IntrHandler(void *HandlerRef) { XUsbPs *InstancePtr; u32 IrqSts; Xil_AssertVoid(HandlerRef != NULL); InstancePtr = (XUsbPs *) HandlerRef; /* Handle controller (non-endpoint) related interrupts. */ IrqSts = XUsbPs_ReadReg(InstancePtr->Config.BaseAddress, XUSBPS_ISR_OFFSET); /* Clear the interrupt status register. */ XUsbPs_WriteReg(InstancePtr->Config.BaseAddress, XUSBPS_ISR_OFFSET, IrqSts); /* Nak interrupt, used to respond to host's IN request */ if(IrqSts & XUSBPS_IXR_NAK_MASK) { /* Ack the hardware */ XUsbPs_WriteReg(InstancePtr->Config.BaseAddress, XUSBPS_EPNAKISR_OFFSET, XUsbPs_ReadReg(InstancePtr->Config.BaseAddress, XUSBPS_EPNAKISR_OFFSET)); } /*************************************************************** * * Handle general interrupts. Endpoint interrupts will be handler * later. * */ /* RESET interrupt.*/ if (IrqSts & XUSBPS_IXR_UR_MASK) { XUsbPs_IntrHandleReset(InstancePtr, IrqSts); return; } /* Check if we have a user handler that needs to be called. Note that * this is the handler for general interrupts. Endpoint interrupts will * be handled below. */ if ((IrqSts & InstancePtr->HandlerMask) && InstancePtr->HandlerFunc) { (InstancePtr->HandlerFunc)(InstancePtr->HandlerRef, IrqSts); } /*************************************************************** * * Handle Endpoint interrupts. * */ if (IrqSts & XUSBPS_IXR_UI_MASK) { u32 EpStat; u32 EpCompl; /* ENDPOINT 0 SETUP PACKET HANDLING * * Check if we got a setup packet on endpoint 0. Currently we * only check for setup packets on endpoint 0 as we would not * expect setup packets on any other endpoint (even though it * is possible to send setup packets on other endpoints). */ EpStat = XUsbPs_ReadReg(InstancePtr->Config.BaseAddress, XUSBPS_EPSTAT_OFFSET); if (EpStat & 0x0001) { /* Handle the setup packet */ XUsbPs_IntrHandleEp0Setup(InstancePtr); /* Re-Prime the endpoint. * Endpoint is de-primed if a setup packet comes in. */ XUsbPs_EpPrime(InstancePtr, 0, XUSBPS_EP_DIRECTION_OUT); } /* Check for RX and TX complete interrupts. */ EpCompl = XUsbPs_ReadReg(InstancePtr->Config.BaseAddress, XUSBPS_EPCOMPL_OFFSET); /* ACK the complete interrupts. */ XUsbPs_WriteReg(InstancePtr->Config.BaseAddress, XUSBPS_EPCOMPL_OFFSET, EpCompl); /* Check OUT (RX) endpoints. */ if (EpCompl & XUSBPS_EP_OUT_MASK) { XUsbPs_IntrHandleRX(InstancePtr, EpCompl); } /* Check IN (TX) endpoints. */ if (EpCompl & XUSBPS_EP_IN_MASK) { XUsbPs_IntrHandleTX(InstancePtr, EpCompl); } } }
/** * 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; }