/** * * Add a descriptor, with an attached empty buffer, into the receive descriptor * list. The buffer attached to the descriptor must be 32-bit aligned if using * the OPB Ethernet core and 64-bit aligned if using the PLB Ethernet core. * This function is used by the upper layer software during initialization when * first setting up the receive descriptors, and also during reception of frames * to replace filled buffers with empty buffers. This function can be called * when the device is started or stopped. Note that it does start the scatter- * gather DMA engine. Although this is not necessary during initialization, it * is not a problem during initialization because the MAC receiver is not yet * started. * * The buffer attached to the descriptor must be aligned on both the front end * and the back end. * * Notification of received frames are done asynchronously through the receive * callback function. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param BdPtr is a pointer to the buffer descriptor that will be added to the * descriptor list. * * @return * * - XST_SUCCESS if a descriptor was successfully returned to the driver * - XST_NOT_SGDMA if the device is not in scatter-gather DMA mode * - XST_DMA_SG_LIST_FULL if the receive descriptor list is full * - XST_DMA_SG_BD_LOCKED if the DMA channel cannot insert the descriptor into * the list because a locked descriptor exists at the insert point. * - XST_DMA_SG_NOTHING_TO_COMMIT if even after inserting a descriptor into the * list, the DMA channel believes there are no new descriptors to commit. * * @internal * * A status that should never be returned from this function, although * the code is set up to handle it, is XST_DMA_SG_NO_LIST. Starting the device * requires a list to be created, and this function requires the device to be * started. * ******************************************************************************/ XStatus XEmac_SgRecv(XEmac * InstancePtr, XBufDescriptor * BdPtr) { XStatus Result; u32 BdControl; XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(BdPtr != NULL); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); /* * Be sure the device is configured for scatter-gather DMA */ if (!XEmac_mIsSgDma(InstancePtr)) { return XST_NOT_SGDMA; } /* * Set some descriptor control word defaults (destination address increment * and local source address) and the source address (the FIFO). These are * the same for every receive descriptor. */ BdControl = XBufDescriptor_GetControl(BdPtr); XBufDescriptor_SetControl(BdPtr, BdControl | XEM_DFT_RECV_BD_MASK); XBufDescriptor_SetSrcAddress(BdPtr, InstancePtr->PhysAddress + XEM_PFIFO_RXDATA_OFFSET); /* * Put the descriptor into the channel's descriptor list and commit. * Although this function is likely called within interrupt context, there * is the possibility that the upper layer software queues it to a task. * In this case, a critical section is needed here to protect shared data * in the DMA component. */ XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); Result = XDmaChannel_PutDescriptor(&InstancePtr->RecvChannel, BdPtr); if (Result != XST_SUCCESS) { XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return Result; } Result = XDmaChannel_CommitPuts(&InstancePtr->RecvChannel); if (Result != XST_SUCCESS) { XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return Result; } /* * Start the DMA channel. Ignore the return status since we know the list * exists and has at least one entry and we don't care if the channel is * already started. The DMA component accesses data here that can be * modified at interrupt or task levels, so a critical section is required. */ (void)XDmaChannel_SgStart(&InstancePtr->RecvChannel); XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return XST_SUCCESS; }
/****************************************************************************** * * FUNCTION: * * XDmaChannel_CommitPuts * * DESCRIPTION: * * This function commits the buffer descriptors which have been put into the * scatter list for the DMA channel since the last commit operation was * performed. This enables the calling functions to put several buffer * descriptors into the list (e.g.,a packet's worth) before allowing the scatter * gather operations to start. This prevents the DMA channel hardware from * starting to use the buffer descriptors in the list before they are ready * to be used (multiple buffer descriptors for a single packet). * * ARGUMENTS: * * InstancePtr contains a pointer to the DMA channel to operate on. The DMA * channel should be configured to use scatter gather in order for this function * to be called. * * RETURN VALUE: * * A status indicating XST_SUCCESS if the buffer descriptors of the list were * successfully committed. * * A value of XST_DMA_SG_NOTHING_TO_COMMIT indicates that the buffer descriptors * were not committed because there was nothing to commit in the list. All the * buffer descriptors which are in the list are commited. * * NOTES: * * None. * ******************************************************************************/ XStatus XDmaChannel_CommitPuts(XDmaChannel * InstancePtr) { /* assert to verify input arguments */ XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); /* if the buffer descriptor to be committed is already committed or * the list is empty (none have been put in), then indicate an error */ if ((InstancePtr->CommitPtr == NULL) || XDmaChannel_IsSgListEmpty(InstancePtr)) { return XST_DMA_SG_NOTHING_TO_COMMIT; } /* last descriptor in the list must have scatter gather disabled so the end * of the list is hit by h/w, if descriptor to commit is not last in list, * commit descriptors by enabling scatter gather in the descriptor */ if (InstancePtr->CommitPtr != InstancePtr->LastPtr) { u32 Control; Control = XBufDescriptor_GetControl(InstancePtr->CommitPtr); XBufDescriptor_SetControl(InstancePtr->CommitPtr, Control & ~XDC_DMACR_SG_DISABLE_MASK); } /* Update the commit pointer to indicate that there is nothing to be * committed, this state is used by start processing to know that the * buffer descriptor to start is not waiting to be committed */ InstancePtr->CommitPtr = NULL; return XST_SUCCESS; }
/****************************************************************************** * * FUNCTION: * * XDmaChannel_GetDescriptor * * DESCRIPTION: * * This function gets a buffer descriptor from the scatter gather list of the * DMA channel. The buffer descriptor is retrieved from the scatter gather list * and the scatter gather list is updated to not include the retrieved buffer * descriptor. This is typically done after a scatter gather operation * completes indicating that a data buffer has been successfully sent or data * has been received into the data buffer. The purpose of this function is to * allow the device using the scatter gather operation to get the results of the * operation. * * ARGUMENTS: * * InstancePtr contains a pointer to the DMA channel to operate on. The DMA * channel should be configured to use scatter gather in order for this function * to be called. * * BufDescriptorPtr is a pointer to a pointer to the buffer descriptor which * was retrieved from the list. The buffer descriptor is not really removed * from the list, but it is changed to a state such that the hardware will not * use it again until it is put into the scatter gather list of the DMA channel. * * RETURN VALUE: * * A status indicating XST_SUCCESS if a buffer descriptor was retrieved from * the scatter gather list of the DMA channel. * * A value of XST_DMA_SG_NO_LIST indicates the scatter gather list has not * been created. * * A value of XST_DMA_SG_LIST_EMPTY indicates no buffer descriptor was * retrieved from the list because there are no buffer descriptors to be * processed in the list. * * BufDescriptorPtr is updated to point to the buffer descriptor which was * retrieved from the list if the status indicates success. * * NOTES: * * None. * ******************************************************************************/ XStatus XDmaChannel_GetDescriptor(XDmaChannel * InstancePtr, XBufDescriptor ** BufDescriptorPtr) { u32 Control; /* assert to verify input arguments */ XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(BufDescriptorPtr != NULL); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); /* if a scatter gather list has not been created yet, return a status */ if (InstancePtr->TotalDescriptorCount == 0) { return XST_DMA_SG_NO_LIST; } /* if the buffer descriptor list is empty, then indicate an error */ if (XDmaChannel_IsSgListEmpty(InstancePtr)) { return XST_DMA_SG_LIST_EMPTY; } /* retrieve the next buffer descriptor which is ready to be processed from * the buffer descriptor list for the DMA channel, set the control word * such that hardware will stop after the descriptor has been processed */ Control = XBufDescriptor_GetControl(InstancePtr->GetPtr); XBufDescriptor_SetControl(InstancePtr->GetPtr, Control | XDC_DMACR_SG_DISABLE_MASK); /* set the input argument, which is also an output, to point to the * buffer descriptor which is to be retrieved from the list */ *BufDescriptorPtr = InstancePtr->GetPtr; /* update the pointer of the DMA channel to reflect the buffer descriptor * was retrieved from the list by setting it to the next buffer descriptor * in the list and indicate one less descriptor in the list now */ InstancePtr->GetPtr = XBufDescriptor_GetNextPtr(InstancePtr->GetPtr); InstancePtr->ActiveDescriptorCount--; return XST_SUCCESS; }
/** * * Send an Ethernet frame using scatter-gather DMA. The caller attaches the * frame to one or more buffer descriptors, then calls this function once for * each descriptor. The caller is responsible for allocating and setting up the * descriptor. An entire Ethernet frame may or may not be contained within one * descriptor. This function simply inserts the descriptor into the scatter- * gather engine's transmit list. The caller is responsible for providing mutual * exclusion to guarantee that a frame is contiguous in the transmit list. The * buffer attached to the descriptor must be 32-bit aligned if using the OPB * Ethernet core and 64-bit aligned if using the PLB Ethernet core. * * The driver updates the descriptor with the device control register before * being inserted into the transmit list. If this is the last descriptor in * the frame, the inserts are committed, which means the descriptors for this * frame are now available for transmission. * * It is assumed that the upper layer software supplies a correctly formatted * Ethernet frame, including the destination and source addresses, the * type/length field, and the data field. It is also assumed that upper layer * software does not append FCS at the end of the frame. * * This call is non-blocking. Notification of error or successful transmission * is done asynchronously through the send or error callback function. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param BdPtr is the address of a descriptor to be inserted into the transmit * ring. * @param Delay indicates whether to start the scatter-gather DMA channel * immediately, or whether to wait. This allows the user to build up a * list of more than one descriptor before starting the transmission of * the packets, which allows the application to keep up with DMA and have * a constant stream of frames being transmitted. Use XEM_SGDMA_NODELAY or * XEM_SGDMA_DELAY, defined in xemac.h, as the value of this argument. If * the user chooses to delay and build a list, the user must call this * function with the XEM_SGDMA_NODELAY option or call XEmac_Start() to * kick off the tranmissions. * * @return * * - XST_SUCCESS if the buffer was successfull sent * - XST_DEVICE_IS_STOPPED if the Ethernet MAC has not been started yet * - XST_NOT_SGDMA if the device is not in scatter-gather DMA mode * - XST_DMA_SG_LIST_FULL if the descriptor list for the DMA channel is full * - XST_DMA_SG_BD_LOCKED if the DMA channel cannot insert the descriptor into * the list because a locked descriptor exists at the insert point * - XST_DMA_SG_NOTHING_TO_COMMIT if even after inserting a descriptor into the * list, the DMA channel believes there are no new descriptors to commit. If * this is ever encountered, there is likely a thread mutual exclusion problem * on transmit. * * @note * * This function is not thread-safe. The user must provide mutually exclusive * access to this function if there are to be multiple threads that can call it. * * @internal * * A status that should never be returned from this function, although * the code is set up to handle it, is XST_DMA_SG_NO_LIST. Starting the device * requires a list to be created, and this function requires the device to be * started. * ******************************************************************************/ XStatus XEmac_SgSend(XEmac * InstancePtr, XBufDescriptor * BdPtr, int Delay) { XStatus Result; u32 BdControl; XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(BdPtr != NULL); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); /* * Be sure the device is configured for scatter-gather DMA, then be sure * it is started. */ if (!XEmac_mIsSgDma(InstancePtr)) { return XST_NOT_SGDMA; } /* * Set some descriptor control word defaults (source address increment * and local destination address) and the destination address * (the FIFO). These are the same for every transmit descriptor. */ BdControl = XBufDescriptor_GetControl(BdPtr); XBufDescriptor_SetControl(BdPtr, BdControl | XEM_DFT_SEND_BD_MASK); XBufDescriptor_SetDestAddress(BdPtr, InstancePtr->PhysAddress + XEM_PFIFO_TXDATA_OFFSET); /* * Put the descriptor in the send list. The DMA component accesses data * here that can also be modified in interrupt context, so a critical * section is required. */ XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); Result = XDmaChannel_PutDescriptor(&InstancePtr->SendChannel, BdPtr); if (Result != XST_SUCCESS) { XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return Result; } /* * If this is the last buffer in the frame, commit the inserts and start * the DMA engine if necessary */ if (XBufDescriptor_IsLastControl(BdPtr)) { Result = XDmaChannel_CommitPuts(&InstancePtr->SendChannel); if (Result != XST_SUCCESS) { XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return Result; } if (Delay == XEM_SGDMA_NODELAY) { /* * Start the DMA channel. Ignore the return status since we know the * list exists and has at least one entry and we don't care if the * channel is already started. The DMA component accesses data here * that can be modified at interrupt or task levels, so a critical * section is required. */ (void)XDmaChannel_SgStart(&InstancePtr->SendChannel); } } XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return XST_SUCCESS; }
/****************************************************************************** * * FUNCTION: * * XDmaChannel_PutDescriptor * * DESCRIPTION: * * This function puts a buffer descriptor into the DMA channel scatter * gather list. A DMA channel maintains a list of buffer descriptors which are * to be processed. This function puts the specified buffer descriptor * at the next location in the list. Note that since the list is already intact, * the information in the parameter is copied into the list (rather than modify * list pointers on the fly). * * After buffer descriptors are put into the list, they must also be committed * by calling another function. This allows multiple buffer descriptors which * span a single packet to be put into the list while preventing the hardware * from starting the first buffer descriptor of the packet. * * ARGUMENTS: * * InstancePtr contains a pointer to the DMA channel to operate on. The DMA * channel should be configured to use scatter gather in order for this function * to be called. * * BufferDescriptorPtr is a pointer to the buffer descriptor to be put into * the next available location of the scatter gather list. * * RETURN VALUE: * * A status which indicates XST_SUCCESS if the buffer descriptor was * successfully put into the scatter gather list. * * A value of XST_DMA_SG_NO_LIST indicates the scatter gather list has not * been created. * * A value of XST_DMA_SG_LIST_FULL indicates the buffer descriptor was not * put into the list because the list was full. * * A value of XST_DMA_SG_BD_LOCKED indicates the buffer descriptor was not * put into the list because the buffer descriptor in the list which is to * be overwritten was locked. A locked buffer descriptor indicates the higher * layered software is still using the buffer descriptor. * * NOTES: * * It is necessary to create a scatter gather list for a DMA channel before * putting buffer descriptors into it. * ******************************************************************************/ XStatus XDmaChannel_PutDescriptor(XDmaChannel * InstancePtr, XBufDescriptor * BufferDescriptorPtr) { u32 Control; /* assert to verify valid input arguments and alignment for those * arguments that have alignment restrictions */ XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(BufferDescriptorPtr != NULL); XASSERT_NONVOID(((u32) BufferDescriptorPtr & 3) == 0); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); /* if a scatter gather list has not been created yet, return a status */ if (InstancePtr->TotalDescriptorCount == 0) { return XST_DMA_SG_NO_LIST; } /* if the list is full because all descriptors are pointing to valid * buffers, then indicate an error, this code assumes no list or an * empty list is detected above */ if (InstancePtr->ActiveDescriptorCount == InstancePtr->TotalDescriptorCount) { return XST_DMA_SG_LIST_FULL; } /* if the buffer descriptor in the list which is to be overwritten is * locked, then don't overwrite it and return a status */ if (XBufDescriptor_IsLocked(InstancePtr->PutPtr)) { return XST_DMA_SG_BD_LOCKED; } /* set the scatter gather stop bit in the control word of the descriptor * to cause the h/w to stop after it processes this descriptor since it * will be the last in the list */ Control = XBufDescriptor_GetControl(BufferDescriptorPtr); XBufDescriptor_SetControl(BufferDescriptorPtr, Control | XDC_DMACR_SG_DISABLE_MASK); /* set both statuses in the descriptor so we tell if they are updated with * the status of the transfer, the hardware should change the busy in the * DMA status to be false when it completes */ XBufDescriptor_SetStatus(BufferDescriptorPtr, XDC_DMASR_BUSY_MASK); XBufDescriptor_SetDeviceStatus(BufferDescriptorPtr, 0); /* copy the descriptor into the next position in the list so it's ready to * be used by the h/w, this assumes the descriptor in the list prior to this * one still has the stop bit in the control word set such that the h/w * use this one yet */ CopyBufferDescriptor(BufferDescriptorPtr, InstancePtr->PutPtr); /* only the last in the list and the one to be committed have scatter gather * disabled in the control word, a commit requires only one descriptor * to be changed, when # of descriptors to commit > 2 all others except the * 1st and last have scatter gather enabled */ if ((InstancePtr->CommitPtr != InstancePtr->LastPtr) && (InstancePtr->CommitPtr != NULL)) { Control = XBufDescriptor_GetControl(InstancePtr->LastPtr); XBufDescriptor_SetControl(InstancePtr->LastPtr, Control & ~XDC_DMACR_SG_DISABLE_MASK); } /* update the list data based upon putting a descriptor into the list, * these operations must be last */ InstancePtr->ActiveDescriptorCount++; /* only update the commit pointer if it is not already active, this allows * it to be deactivated after every commit such that a single descriptor * which is committed does not appear to be waiting to be committed */ if (InstancePtr->CommitPtr == NULL) { InstancePtr->CommitPtr = InstancePtr->LastPtr; } /* these updates MUST BE LAST after the commit pointer update in order for * the commit pointer to track the correct descriptor to be committed */ InstancePtr->LastPtr = InstancePtr->PutPtr; InstancePtr->PutPtr = XBufDescriptor_GetNextPtr(InstancePtr->PutPtr); return XST_SUCCESS; }