Example #1
0
static void passEthMessages( void )
{
IPStackEvent_t xRxEvent;

	xRxEvent.eEventType = eNetworkRxEvent;
	xRxEvent.pvData = ( void * ) ethMsg;

	if( xSendEventStructToIPTask( &xRxEvent, ( portTickType ) 1000 ) != pdPASS )
	{
		/* The buffer could not be sent to the stack so	must be released again.
		This is a deferred handler taskr, not a real interrupt, so it is ok to
		use the task level function here. */
		do
		{
			NetworkBufferDescriptor_t *xNext = ethMsg->pxNextBuffer;
			vReleaseNetworkBufferAndDescriptor( ethMsg );
			ethMsg = xNext;
		} while( ethMsg != NULL );

		iptraceETHERNET_RX_EVENT_LOST();
		FreeRTOS_printf( ( "passEthMessages: Can not queue return packet!\n" ) );
	}

	ethMsg = ethLast = NULL;
}
Example #2
0
void emacps_check_tx( xemacpsif_s *xemacpsif )
{
int tail = xemacpsif->txTail;
int head = xemacpsif->txHead;

	if( head != tail )
	{
		for( ; ; )
		{
			if( ( xemacpsif->txSegments[ tail ].flags & XEMACPS_TXBUF_USED_MASK ) == 0 )
			{
				/* The driver is still waiting for the EMAC to sent this message.
				When done, "TXBUF_USED" will be set. */
				break;
			}
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
#warning ipconfigZERO_COPY_TX_DRIVER is defined
			{
			void *pvBuffer = pxDMA_tx_buffers[ tail ];
			NetworkBufferDescriptor_t *pxBuffer;

				if( pvBuffer != NULL )
				{
					pxDMA_tx_buffers[ tail ] = NULL;
					pxBuffer = pxPacketBuffer_to_NetworkBuffer( pvBuffer );
					if( pxBuffer != NULL )
					{
						vReleaseNetworkBufferAndDescriptor( pxBuffer );
					}
					else
					{
						FreeRTOS_printf( ( "emacps_check_tx: Can not find network bufffer" ) );
					}
				}
			}
#endif
			/* Clear all but the "used" and "wrap" bits. */
			if( tail < ipconfigNIC_N_TX_DESC - 1 )
			{
				xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK;
			}
			else
			{
				xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK;
			}

			if( ++tail == ipconfigNIC_N_TX_DESC )
			{
				tail = 0;
			}
			if( tail == head )
			{
				break;
			}
		}
		xemacpsif->txTail = tail;
	}
	return;
}
Example #3
0
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend )
{
/* Do not wait too long for a free TX DMA buffer. */
const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u );

	do {
		if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 )
		{
			/* Do not attempt to send packets as long as the Link Status is low. */
			break;
		}
		if( xTXDescriptorSemaphore == NULL )
		{
			/* Semaphore has not been created yet? */
			break;
		}
		if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
		{
			/* Time-out waiting for a free TX descriptor. */
			tx_release_count[ 3 ]++;
			break;
		}
		#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
		{
			/* Confirm that the pxDescriptor may be kept by the driver. */
			configASSERT( bReleaseAfterSend != pdFALSE );
		}
		#endif /* ipconfigZERO_COPY_TX_DRIVER */

		gmac_dev_write( &gs_gmac_dev, (void *)pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, prvTxCallback );

		#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
		{
			/* Confirm that the pxDescriptor may be kept by the driver. */
			bReleaseAfterSend = pdFALSE;
		}
		#endif /* ipconfigZERO_COPY_TX_DRIVER */
		/* Not interested in a call-back after TX. */
		iptraceNETWORK_INTERFACE_TRANSMIT();
	} while( 0 );

	if( bReleaseAfterSend != pdFALSE )
	{
		vReleaseNetworkBufferAndDescriptor( pxDescriptor );
	}
	return pdTRUE;
}
Example #4
0
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t bReleaseAfterSend )
{
size_t xSpace;

	iptraceNETWORK_INTERFACE_TRANSMIT();
	configASSERT( xIsCallingFromIPTask() == pdTRUE );

	/* Both the length of the data being sent and the actual data being sent
	are placed in the thread safe buffer used to pass data between the FreeRTOS
	tasks and the Win32 thread that sends data via the WinPCAP library.  Drop
	the packet if there is insufficient space in the buffer to hold both. */
	xSpace = uxStreamBufferGetSpace( xSendBuffer );

	if( ( pxNetworkBuffer->xDataLength <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) &&
		( xSpace >= ( pxNetworkBuffer->xDataLength + sizeof( pxNetworkBuffer->xDataLength ) ) ) )
	{
		/* First write in the length of the data, then write in the data
		itself. */
		uxStreamBufferAdd( xSendBuffer, 0, ( const uint8_t * ) &( pxNetworkBuffer->xDataLength ), sizeof( pxNetworkBuffer->xDataLength ) );
		uxStreamBufferAdd( xSendBuffer, 0, ( const uint8_t * ) pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength );
	}
	else
	{
		FreeRTOS_debug_printf( ( "xNetworkInterfaceOutput: send buffers full to store %lu\n", pxNetworkBuffer->xDataLength ) );
	}

	/* Kick the Tx task in either case in case it doesn't know the buffer is
	full. */
	SetEvent( pvSendEvent );

	/* The buffer has been sent so can be released. */
	if( bReleaseAfterSend != pdFALSE )
	{
		vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
	}

	return pdPASS;
}
Example #5
0
xNetworkBufferDescriptor_t *pxGetNetworkBufferWithDescriptor( size_t xRequestedSizeBytes, TickType_t xBlockTimeTicks )
{
xNetworkBufferDescriptor_t *pxReturn = NULL;
size_t uxCount;

	if( ( xRequestedSizeBytes != 0 ) && ( xRequestedSizeBytes < MINIMAL_BUFFER_SIZE ) )
	{
		/* ARP packets can replace application packets, so the storage must be
		at least large enough to hold an ARP. */
		xRequestedSizeBytes = MINIMAL_BUFFER_SIZE;
	}

	/* If there is a semaphore available, there is a network buffer available. */
	if( xSemaphoreTake( xNetworkBufferSemaphore, xBlockTimeTicks ) == pdPASS )
	{
		/* Protect the structure as it is accessed from tasks and interrupts. */
		taskENTER_CRITICAL();
		{
			pxReturn = ( xNetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList );
			uxListRemove( &( pxReturn->xBufferListItem ) );
		}
		taskEXIT_CRITICAL();

		/* Reading UBaseType_t, no critical section needed. */
		uxCount = listCURRENT_LIST_LENGTH( &xFreeBuffersList );

		if( uxMinimumFreeNetworkBuffers > uxCount )
		{
			uxMinimumFreeNetworkBuffers = uxCount;
		}		

		/* Allocate storage of exactly the requested size to the buffer. */
		configASSERT( pxReturn->pucEthernetBuffer == NULL );
		if( xRequestedSizeBytes > 0 )
		{
			/* Extra space is obtained so a pointer to the network buffer can
			be stored at the beginning of the buffer. */
			pxReturn->pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xRequestedSizeBytes + ipBUFFER_PADDING );

			if( pxReturn->pucEthernetBuffer == NULL )
			{
				/* The attempt to allocate storage for the buffer payload failed,
				so the network buffer structure cannot be used and must be
				released. */
				vReleaseNetworkBufferAndDescriptor( pxReturn );
				pxReturn = NULL;
			}
			else
			{
				/* Store a pointer to the network buffer structure in the
				buffer storage area, then move the buffer pointer on past the
				stored pointer so the pointer value is not overwritten by the
				application when the buffer is used. */
				*( ( xNetworkBufferDescriptor_t ** ) ( pxReturn->pucEthernetBuffer ) ) = pxReturn;
				pxReturn->pucEthernetBuffer += ipBUFFER_PADDING;

				/* Store the actual size of the allocated buffer, which may be
				greater than the original requested size. */
				pxReturn->xDataLength = xRequestedSizeBytes;

				#if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
				{
					/* make sure the buffer is not linked */
					pxReturn->pxNextBuffer = NULL;
				}
				#endif /* ipconfigUSE_LINKED_RX_MESSAGES */
			}
		}
		else
		{
			/* A descriptor is being returned without an associated buffer being
			allocated. */
		}
	}

	if( pxReturn == NULL )
	{
		iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER();
	}
	else
	{
		iptraceNETWORK_BUFFER_OBTAINED( pxReturn );
	}

	return pxReturn;
}
Example #6
0
static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, TickType_t xIdentifier )
{
DNSMessage_t *pxDNSMessageHeader;
uint32_t ulIPAddress = 0UL;
#if( ipconfigUSE_LLMNR == 1 )
	char *pcRequestedName = NULL;
#endif
uint8_t *pucByte;
uint16_t x, usDataLength, usQuestions;
#if( ipconfigUSE_LLMNR == 1 )
	uint16_t usType = 0, usClass = 0;
#endif
#if( ipconfigUSE_DNS_CACHE == 1 )
	char pcName[128] = ""; /*_RB_ What is the significance of 128?  Probably too big to go on the stack for a small MCU but don't know how else it could be made re-entrant.  Might be necessary. */
#endif

	pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;

	if( pxDNSMessageHeader->usIdentifier == ( uint16_t ) xIdentifier )
	{
		/* Start at the first byte after the header. */
		pucByte = pucUDPPayloadBuffer + sizeof( DNSMessage_t );

		/* Skip any question records. */
		usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
		for( x = 0; x < usQuestions; x++ )
		{
			#if( ipconfigUSE_LLMNR == 1 )
			{
				if( x == 0 )
				{
					pcRequestedName = ( char * ) pucByte;
				}
			}
			#endif

#if( ipconfigUSE_DNS_CACHE == 1 )
			if( x == 0 )
			{
				pucByte = prvReadNameField( pucByte, pcName, sizeof( pcName ) );
			}
			else
#endif /* ipconfigUSE_DNS_CACHE */
			{
				/* Skip the variable length pcName field. */
				pucByte = prvSkipNameField( pucByte );
			}

			#if( ipconfigUSE_LLMNR == 1 )
			{
				/* usChar2u16 returns value in host endianness. */
				usType = usChar2u16( pucByte );
				usClass = usChar2u16( pucByte + 2 );
			}
			#endif /* ipconfigUSE_LLMNR */

			/* Skip the type and class fields. */
			pucByte += sizeof( uint32_t );
		}

		/* Search through the answers records. */
		pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );

		if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
		{
			for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
			{
				pucByte = prvSkipNameField( pucByte );

				/* Is the type field that of an A record? */
				if( usChar2u16( pucByte ) == dnsTYPE_A_HOST )
				{
					/* This is the required record.  Skip the type, class, and
					time to live fields, plus the first byte of the data
					length. */
					pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );

					/* Sanity check the data length. */
					if( ( size_t ) *pucByte == sizeof( uint32_t ) )
					{
						/* Skip the second byte of the length. */
						pucByte++;

						/* Copy the IP address out of the record. */
						memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );

						#if( ipconfigUSE_DNS_CACHE == 1 )
						{
							prvProcessDNSCache( pcName, &ulIPAddress, pdFALSE );
						}
						#endif /* ipconfigUSE_DNS_CACHE */
						#if( ipconfigDNS_USE_CALLBACKS != 0 )
						{
							/* See if any asynchronous call was made to FreeRTOS_gethostbyname_a() */
							vDNSDoCallback( ( TickType_t ) pxDNSMessageHeader->usIdentifier, pcName, ulIPAddress );
						}
						#endif	/* ipconfigDNS_USE_CALLBACKS != 0 */
					}

					break;
				}
				else
				{
					/* Skip the type, class and time to live fields. */
					pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );

					/* Determine the length of the data in the field. */
					memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) );
					usDataLength = FreeRTOS_ntohs( usDataLength );

					/* Jump over the data length bytes, and the data itself. */
					pucByte += usDataLength + sizeof( uint16_t );
				}
			}
		}
#if( ipconfigUSE_LLMNR == 1 )
		else if( ( usQuestions != ( uint16_t )0u ) && ( usType == ( uint16_t )dnsTYPE_A_HOST ) && ( usClass == ( uint16_t )dnsCLASS_IN ) )
		{
			/* If this is not a reply to our DNS request, it might an LLMNR
			request. */
			if( xApplicationDNSQueryHook ( ( pcRequestedName + 1 ) ) )
			{
			int16_t usLength;
			NetworkBufferDescriptor_t *pxNewBuffer = NULL;
			NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer );
			LLMNRAnswer_t *pxAnswer;

				if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) )
				{
				BaseType_t xDataLength = pxNetworkBuffer->xDataLength + sizeof( UDPHeader_t ) +
					sizeof( EthernetHeader_t ) + sizeof( IPHeader_t );

					/* The field xDataLength was set to the length of the UDP payload.
					The answer (reply) will be longer than the request, so the packet
					must be duplicaed into a bigger buffer */
					pxNetworkBuffer->xDataLength = xDataLength;
					pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, xDataLength + 16 );
					if( pxNewBuffer != NULL )
					{
					BaseType_t xOffset1, xOffset2;

						xOffset1 = ( BaseType_t ) ( pucByte - pucUDPPayloadBuffer );
						xOffset2 = ( BaseType_t ) ( ( ( uint8_t * ) pcRequestedName ) - pucUDPPayloadBuffer );

						pxNetworkBuffer = pxNewBuffer;
						pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + ipUDP_PAYLOAD_OFFSET_IPv4;

						pucByte = pucUDPPayloadBuffer + xOffset1;
						pcRequestedName = ( char * ) ( pucUDPPayloadBuffer + xOffset2 );
						pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;

					}
					else
					{
						/* Just to indicate that the message may not be answered. */
						pxNetworkBuffer = NULL;
					}
				}
				if( pxNetworkBuffer != NULL )
				{
					pxAnswer = (LLMNRAnswer_t *)pucByte;

					/* Leave 'usIdentifier' and 'usQuestions' untouched. */
					vSetField16( pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_REPONSE );	/* Set the response flag */
					vSetField16( pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 );	/* Provide a single answer */
					vSetField16( pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 );	/* No authority */
					vSetField16( pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 );	/* No additional info */

					pxAnswer->ucNameCode = dnsNAME_IS_OFFSET;
					pxAnswer->ucNameOffset = ( uint8_t )( pcRequestedName - ( char * ) pucUDPPayloadBuffer );

					vSetField16( pxAnswer, LLMNRAnswer_t, usType, dnsTYPE_A_HOST );	/* Type A: host */
					vSetField16( pxAnswer, LLMNRAnswer_t, usClass, dnsCLASS_IN );	/* 1: Class IN */
					vSetField32( pxAnswer, LLMNRAnswer_t, ulTTL, dnsLLMNR_TTL_VALUE );
					vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, 4 );
					vSetField32( pxAnswer, LLMNRAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) );

					usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( pucByte - pucUDPPayloadBuffer ) );

					prvReplyDNSMessage( pxNetworkBuffer, usLength );

					if( pxNewBuffer != NULL )
					{
						vReleaseNetworkBufferAndDescriptor( pxNewBuffer );
					}
				}
			}
		}
#endif /* ipconfigUSE_LLMNR == 1 */
	}

	return ulIPAddress;
}
/* The deferred interrupt handler is a standard RTOS task.  FreeRTOS's centralised
 deferred interrupt handling capabilities can also be used. */
static void prvEMACDeferredInterruptHandlerTask(void *pvParameters) {
	xNetworkBufferDescriptor_t *pxBufferDescriptor;
	size_t xBytesReceived;
	/* Used to indicate that xSendEventStructToIPTask() is being called because
	 of an Ethernet receive event. */
	xIPStackEvent_t xRxEvent;

	uint8_t *buffer;
	__IO ETH_DMADescTypeDef *dmarxdesc;
	uint32_t payloadoffset = 0;
	uint32_t byteslefttocopy = 0;
	uint32_t i = 0;

	xEMACRxEventSemaphore = xSemaphoreCreateCounting(10, 0);

	for (;;) {
		/* Wait for the Ethernet MAC interrupt to indicate that another packet
		 has been received.  It is assumed xEMACRxEventSemaphore is a counting
		 semaphore (to count the Rx events) and that the semaphore has already
		 been created (remember this is an example of a simple rather than
		 optimised port layer). */
		if (xSemaphoreTake( xEMACRxEventSemaphore, portMAX_DELAY ) == pdTRUE) {

			/* get received frame */
			if (HAL_ETH_GetReceivedFrame_IT(&heth_global) != HAL_OK)
				return;

			/* Obtain the size of the packet */
			xBytesReceived = (uint16_t) heth_global.RxFrameInfos.length;
			buffer = (uint8_t *) heth_global.RxFrameInfos.buffer;

			if (xBytesReceived > 0) {
				/* Allocate a network buffer descriptor that points to a buffer
				 large enough to hold the received frame.  As this is the simple
				 rather than efficient example the received data will just be copied
				 into this buffer. */
				pxBufferDescriptor = pxGetNetworkBufferWithDescriptor(xBytesReceived, 0);

				if (pxBufferDescriptor != NULL) {
					/* pxBufferDescriptor->pucEthernetBuffer now points to an Ethernet
					 buffer large enough to hold the received data.  Copy the
					 received data into pcNetworkBuffer->pucEthernetBuffer.  Here it
					 is assumed ReceiveData() is a peripheral driver function that
					 copies the received data into a buffer passed in as the function's
					 parameter.  Remember! While is is a simple robust technique -
					 it is not efficient.  An example that uses a zero copy technique
					 is provided further down this page. */

					dmarxdesc = heth_global.RxFrameInfos.FSRxDesc;

					pxBufferDescriptor->xDataLength = xBytesReceived;

					byteslefttocopy = xBytesReceived;
					payloadoffset = 0;

					/* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
					while (byteslefttocopy > ETH_RX_BUF_SIZE) {
						/* Copy data to pbuf */
						memcpy(
								(uint8_t*) ((uint8_t*) pxBufferDescriptor->pucEthernetBuffer
										+ payloadoffset), (uint8_t*) ((uint8_t*) buffer),
								ETH_RX_BUF_SIZE);

						/* Point to next descriptor */
						dmarxdesc = (ETH_DMADescTypeDef *) (dmarxdesc->Buffer2NextDescAddr);
						buffer = (uint8_t *) (dmarxdesc->Buffer1Addr);

						byteslefttocopy -= ETH_RX_BUF_SIZE;
						payloadoffset += ETH_RX_BUF_SIZE;
					}
					/* Copy remaining data in pbuf */
					memcpy(
							(uint8_t*) ((uint8_t*) pxBufferDescriptor->pucEthernetBuffer
									+ payloadoffset), (uint8_t*) ((uint8_t*) buffer),
							byteslefttocopy);
				}

				/* See if the data contained in the received Ethernet frame needs
				 to be processed.  NOTE! It is preferable to do this in
				 the interrupt service routine itself, which would remove the need
				 to unblock this task for packets that don't need processing. */
				if (eConsiderFrameForProcessing(pxBufferDescriptor->pucEthernetBuffer)
						== eProcessBuffer) {
					/* The event about to be sent to the TCP/IP is an Rx event. */
					xRxEvent.eEventType = eNetworkRxEvent;

					/* pvData is used to point to the network buffer descriptor that
					 now references the received data. */
					xRxEvent.pvData = (void *) pxBufferDescriptor;

					/* Send the data to the TCP/IP stack. */
					if (xSendEventStructToIPTask(&xRxEvent, 0) == pdFALSE) {
						/* The buffer could not be sent to the IP task so the buffer
						 must be released. */
						vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor);

						/* Make a call to the standard trace macro to log the
						 occurrence. */
						iptraceETHERNET_RX_EVENT_LOST();
					}
					else {
						/* The message was successfully sent to the TCP/IP stack.
						 Call the standard trace macro to log the occurrence. */
						iptraceNETWORK_INTERFACE_RECEIVE();
						gdb.monit.rx_eth_frames++;
					}
				}
				else {
					/* The Ethernet frame can be dropped, but the Ethernet buffer
					 must be released. */
					vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor);
				}

				/* Release descriptors to DMA */
				/* Point to first descriptor */
				dmarxdesc = heth_global.RxFrameInfos.FSRxDesc;
				/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
				for (i = 0; i < heth_global.RxFrameInfos.SegCount; i++) {
					dmarxdesc->Status |= ETH_DMARXDESC_OWN;
					dmarxdesc = (ETH_DMADescTypeDef *) (dmarxdesc->Buffer2NextDescAddr);
				}

				/* Clear Segment_Count */
				heth_global.RxFrameInfos.SegCount = 0;

			}
			else {
				/* The event was lost because a network buffer was not available.
				 Call the standard trace macro to log the occurrence. */
				iptraceETHERNET_RX_EVENT_LOST();
			}
		}

		/* When Rx Buffer unavailable flag is set: clear it and resume reception */
		if ((heth_global.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t) RESET) {
			/* Clear RBUS ETHERNET DMA flag */
			heth_global.Instance->DMASR = ETH_DMASR_RBUS;
			/* Resume DMA reception */
			heth_global.Instance->DMARPDR = 0;
		}
	}

}
BaseType_t xNetworkInterfaceOutput(
xNetworkBufferDescriptor_t * const pxDescriptor, BaseType_t xReleaseAfterSend) {

	uint8_t *buffer = (uint8_t *) (heth_global.TxDesc->Buffer1Addr);
	__IO ETH_DMADescTypeDef *DmaTxDesc;
	uint32_t framelength = 0;
	uint32_t byteslefttocopy = 0;
	uint32_t payloadoffset = 0;
	DmaTxDesc = heth_global.TxDesc;

	uint8_t errval;

	/* copy frame from pbufs to driver buffers */

	/* Is this buffer available? If not, goto error */
	if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t) RESET) {
		errval = pdFAIL;
		goto error;
	}

#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
	{
		ProtocolPacket_t *pxPacket;

		/* If the peripheral must calculate the checksum, it wants
		 the protocol checksum to have a value of zero. */
		pxPacket = (ProtocolPacket_t *) (pxDescriptor->pucEthernetBuffer);

		if (pxPacket->xICMPPacket.xIPHeader.ucProtocol == ipPROTOCOL_ICMP) {
			pxPacket->xICMPPacket.xICMPHeader.usChecksum = (uint16_t) 0u;
		}
	}
#endif

	/* Get bytes in current lwIP buffer */
	byteslefttocopy = pxDescriptor->xDataLength;
	framelength = byteslefttocopy; // copy of frame length
	payloadoffset = 0;

	/* Check if the length of data to copy is bigger than Tx buffer size*/
	while ((byteslefttocopy) > ETH_TX_BUF_SIZE) {
		/* Copy data to Tx buffer*/
		memcpy((uint8_t*) ((uint8_t*) buffer),
				(uint8_t*) ((uint8_t*) pxDescriptor->pucEthernetBuffer + payloadoffset),
				(ETH_TX_BUF_SIZE));

		/* Point to next descriptor */
		DmaTxDesc = (ETH_DMADescTypeDef *) (DmaTxDesc->Buffer2NextDescAddr);

		/* Check if the buffer is available */
		if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t) RESET) {
			errval = pdFAIL;
			goto error;
		}

		// memcpy( (uint8_t*)((uint8_t*)buffer), (uint8_t*)((uint8_t*)pxDescriptor->pucEthernetBuffer + payloadoffset), byteslefttocopy );
		buffer = (uint8_t *) (DmaTxDesc->Buffer1Addr);

		byteslefttocopy -= (ETH_TX_BUF_SIZE);
		payloadoffset += (ETH_TX_BUF_SIZE);

	}

	/* Copy the remaining bytes */
	memcpy((uint8_t*) ((uint8_t*) buffer),
			(uint8_t*) ((uint8_t*) pxDescriptor->pucEthernetBuffer + payloadoffset),
			byteslefttocopy);

	/* Prepare transmit descriptors to give to DMA */
	HAL_ETH_TransmitFrame(&heth_global, framelength);

	gdb.monit.tx_eth_frames++;

	/* Call the standard trace macro to log the send event. */
	iptraceNETWORK_INTERFACE_TRANSMIT();

	errval = pdPASS;

	error:

	if (xReleaseAfterSend != pdFALSE) {
		/* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet
		 buffer.  The Ethernet buffer is therefore no longer needed, and must be
		 freed for re-use. */
		vReleaseNetworkBufferAndDescriptor(pxDescriptor);
	}

	/* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
	if ((heth_global.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t) RESET) {
		/* Clear TUS ETHERNET DMA flag */
		heth_global.Instance->DMASR = ETH_DMASR_TUS;

		/* Resume DMA transmission*/
		heth_global.Instance->DMATPDR = 0;
	}

	return errval;

}
Example #9
0
void vProcessGeneratedUDPPacket( xNetworkBufferDescriptor_t * const pxNetworkBuffer )
{
xUDPPacket_t *pxUDPPacket;
xIPHeader_t *pxIPHeader;
eARPLookupResult_t eReturned;
uint32_t ulIPAddress = pxNetworkBuffer->ulIPAddress;

	/* Map the UDP packet onto the start of the frame. */
	pxUDPPacket = ( xUDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;

	/* Determine the ARP cache status for the requested IP address. */
	eReturned = eARPGetCacheEntry( &( ulIPAddress ), &( pxUDPPacket->xEthernetHeader.xDestinationAddress ) );
	if( eReturned != eCantSendPacket )
	{
		if( eReturned == eARPCacheHit )
		{
			#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
				uint8_t xSocketOptions;
			#endif
			iptraceSENDING_UDP_PACKET( pxNetworkBuffer->ulIPAddress );

			/* Create short cuts to the data within the packet. */
			pxIPHeader = &( pxUDPPacket->xIPHeader );

		#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 )
			/* Is it possible that the packet is not actually a UDP packet
			after all, but an ICMP packet. */
			if( pxNetworkBuffer->usPort != ipPACKET_CONTAINS_ICMP_DATA )
		#endif /* ipconfigSUPPORT_OUTGOING_PINGS */
			{
			xUDPHeader_t *pxUDPHeader;

				pxUDPHeader = &( pxUDPPacket->xUDPHeader );

				pxUDPHeader->usDestinationPort = pxNetworkBuffer->usPort;
				pxUDPHeader->usSourcePort = pxNetworkBuffer->usBoundPort;
				pxUDPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( xUDPHeader_t ) );
				pxUDPHeader->usLength = FreeRTOS_htons( pxUDPHeader->usLength );
				pxUDPHeader->usChecksum = 0;
			}

			/* memcpy() the constant parts of the header information into
			the	correct location within the packet.  This fills in:
				xEthernetHeader.xSourceAddress
				xEthernetHeader.usFrameType
				xIPHeader.ucVersionHeaderLength
				xIPHeader.ucDifferentiatedServicesCode
				xIPHeader.usLength
				xIPHeader.usIdentification
				xIPHeader.usFragmentOffset
				xIPHeader.ucTimeToLive
				xIPHeader.ucProtocol
			and
				xIPHeader.usHeaderChecksum
			*/
			/* Save options now, as they will be overwritten by memcpy */
			#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
				xSocketOptions = pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ];
			#endif
			memcpy( ( void *) &( pxUDPPacket->xEthernetHeader.xSourceAddress ), ( void * ) xDefaultPartUDPPacketHeader.ucBytes, sizeof( xDefaultPartUDPPacketHeader ) );

		#if ipconfigSUPPORT_OUTGOING_PINGS == 1
			if( pxNetworkBuffer->usPort == ipPACKET_CONTAINS_ICMP_DATA )
			{
				pxIPHeader->ucProtocol = ipPROTOCOL_ICMP;
				pxIPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( xIPHeader_t ) );
			}
			else
		#endif /* ipconfigSUPPORT_OUTGOING_PINGS */
			{
				pxIPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( xIPHeader_t ) + sizeof( xUDPHeader_t ) );
			}

			/* The total transmit size adds on the Ethernet header. */
			pxNetworkBuffer->xDataLength = pxIPHeader->usLength + sizeof( xEthernetHeader_t );
			pxIPHeader->usLength = FreeRTOS_htons( pxIPHeader->usLength );
			/* HT:endian: changed back to network endian */
			pxIPHeader->ulDestinationIPAddress = pxNetworkBuffer->ulIPAddress;

			#if( ipconfigUSE_LLMNR == 1 )
			{
				/* LLMNR messages are typically used on a LAN and they're
				 * not supposed to cross routers */
				if( pxNetworkBuffer->ulIPAddress == ipLLMNR_IP_ADDR )
				{
					pxIPHeader->ucTimeToLive = 0x01;
				}
			}
			#endif

			#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
			{
				pxIPHeader->usHeaderChecksum = 0;
				pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0UL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IP_HEADER );
				pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );

				if( ( xSocketOptions & FREERTOS_SO_UDPCKSUM_OUT ) != 0 )
				{
					usGenerateProtocolChecksum( (uint8_t*)pxUDPPacket, pdTRUE );
				}
				else
				{
					pxUDPPacket->xUDPHeader.usChecksum = 0;
				}
			}
			#endif
		}
		else if( eReturned == eARPCacheMiss )
		{
			/* Add an entry to the ARP table with a null hardware address.
			This allows the ARP timer to know that an ARP reply is
			outstanding, and perform retransmissions if necessary. */
			vARPRefreshCacheEntry( NULL, ulIPAddress );

			/* Generate an ARP for the required IP address. */
			iptracePACKET_DROPPED_TO_GENERATE_ARP( pxNetworkBuffer->ulIPAddress );
			pxNetworkBuffer->ulIPAddress = ulIPAddress;
			vARPGenerateRequestPacket( pxNetworkBuffer );
		}
		else
		{
			/* The lookup indicated that an ARP request has already been
			sent out for the queried IP address. */
			eReturned = eCantSendPacket;
		}
	}

	if( eReturned != eCantSendPacket )
	{
		/* The network driver is responsible for freeing the network buffer
		after the packet has been sent. */
		xNetworkInterfaceOutput( pxNetworkBuffer, pdTRUE );
	}
	else
	{
		/* The packet can't be sent (DHCP not completed?).  Just drop the
		packet. */
		vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
	}
}
Example #10
0
static void prvEMACHandlerTask( void *pvParameters )
{
TimeOut_t xPhyTime;
TickType_t xPhyRemTime;
UBaseType_t uxCount;
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
	NetworkBufferDescriptor_t *pxBuffer;
#endif
uint8_t *pucBuffer;
BaseType_t xResult = 0;
uint32_t xStatus;
const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS );

	/* Remove compiler warnings about unused parameters. */
	( void ) pvParameters;

	configASSERT( xEMACTaskHandle );

	vTaskSetTimeOutState( &xPhyTime );
	xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );

	for( ;; )
	{
		vCheckBuffersAndQueue();

		if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 )
		{
			/* No events to process now, wait for the next. */
			ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );
		}

		if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
		{
			ulISREvents &= ~EMAC_IF_RX_EVENT;

			/* Wait for the EMAC interrupt to indicate that another packet has been
			received. */
			xResult = prvEMACRxPoll();
		}

		if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
		{
			/* Future extension: code to release TX buffers if zero-copy is used. */
			ulISREvents &= ~EMAC_IF_TX_EVENT;
			while( xQueueReceive( xTxBufferQueue, &pucBuffer, 0 ) != pdFALSE )
			{
				#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
				{
					pxBuffer = pxPacketBuffer_to_NetworkBuffer( pucBuffer );
					if( pxBuffer != NULL )
					{
						vReleaseNetworkBufferAndDescriptor( pxBuffer );
						tx_release_count[ 0 ]++;
					}
					else
					{
						tx_release_count[ 1 ]++;
					}
				}
				#else
				{
					tx_release_count[ 0 ]++;
				}
				#endif
				uxCount = uxQueueMessagesWaiting( ( QueueHandle_t ) xTXDescriptorSemaphore );
				if( uxCount < GMAC_TX_BUFFERS )
				{
					/* Tell the counting semaphore that one more TX descriptor is available. */
					xSemaphoreGive( xTXDescriptorSemaphore );
				}
			}
		}

		if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 )
		{
			/* Future extension: logging about errors that occurred. */
			ulISREvents &= ~EMAC_IF_ERR_EVENT;
		}

		if( xResult > 0 )
		{
			/* A packet was received. No need to check for the PHY status now,
			but set a timer to check it later on. */
			vTaskSetTimeOutState( &xPhyTime );
			xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
			xResult = 0;
		}
		else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
		{
			/* Check the link status again. */
			xStatus = ulReadMDIO( PHY_REG_01_BMSR );

			if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) )
			{
				ulPHYLinkStatus = xStatus;
				FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) );
			}

			vTaskSetTimeOutState( &xPhyTime );
			if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
			{
				xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
			}
			else
			{
				xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
			}
		}
	}
}
Example #11
0
static uint32_t prvEMACRxPoll( void )
{
unsigned char *pucUseBuffer;
uint32_t ulReceiveCount, ulResult, ulReturnValue = 0;
static NetworkBufferDescriptor_t *pxNextNetworkBufferDescriptor = NULL;
const UBaseType_t xMinDescriptorsToLeave = 2UL;
const TickType_t xBlockTime = pdMS_TO_TICKS( 100UL );
static IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };

	for( ;; )
	{
		/* If pxNextNetworkBufferDescriptor was not left pointing at a valid
		descriptor then allocate one now. */
		if( ( pxNextNetworkBufferDescriptor == NULL ) && ( uxGetNumberOfFreeNetworkBuffers() > xMinDescriptorsToLeave ) )
		{
			pxNextNetworkBufferDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xBlockTime );
		}

		if( pxNextNetworkBufferDescriptor != NULL )
		{
			/* Point pucUseBuffer to the buffer pointed to by the descriptor. */
			pucUseBuffer = ( unsigned char* ) ( pxNextNetworkBufferDescriptor->pucEthernetBuffer - ipconfigPACKET_FILLER_SIZE );
		}
		else
		{
			/* As long as pxNextNetworkBufferDescriptor is NULL, the incoming
			messages will be flushed and ignored. */
			pucUseBuffer = NULL;
		}

		/* Read the next packet from the hardware into pucUseBuffer. */
		ulResult = gmac_dev_read( &gs_gmac_dev, pucUseBuffer, ipTOTAL_ETHERNET_FRAME_SIZE, &ulReceiveCount );

		if( ( ulResult != GMAC_OK ) || ( ulReceiveCount == 0 ) )
		{
			/* No data from the hardware. */
			break;
		}

		if( pxNextNetworkBufferDescriptor == NULL )
		{
			/* Data was read from the hardware, but no descriptor was available
			for it, so it will be dropped. */
			iptraceETHERNET_RX_EVENT_LOST();
			continue;
		}

		iptraceNETWORK_INTERFACE_RECEIVE();
		pxNextNetworkBufferDescriptor->xDataLength = ( size_t ) ulReceiveCount;
		xRxEvent.pvData = ( void * ) pxNextNetworkBufferDescriptor;

		/* Send the descriptor to the IP task for processing. */
		if( xSendEventStructToIPTask( &xRxEvent, xBlockTime ) != pdTRUE )
		{
			/* The buffer could not be sent to the stack so must be released
			again. */
			vReleaseNetworkBufferAndDescriptor( pxNextNetworkBufferDescriptor );
			iptraceETHERNET_RX_EVENT_LOST();
			FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) );
		}

		/* Now the buffer has either been passed to the IP-task,
		or it has been released in the code above. */
		pxNextNetworkBufferDescriptor = NULL;
		ulReturnValue++;
	}

	return ulReturnValue;
}
Example #12
0
static void prvInterruptSimulatorTask( void *pvParameters )
{
struct pcap_pkthdr xHeader;
static struct pcap_pkthdr *pxHeader;
const uint8_t *pucPacketData;
uint8_t ucRecvBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ];
NetworkBufferDescriptor_t *pxNetworkBuffer;
IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
eFrameProcessingResult_t eResult;

	/* Remove compiler warnings about unused parameters. */
	( void ) pvParameters;

	for( ;; )
	{
		/* Does the circular buffer used to pass data from the Win32 thread that
		handles WinPCAP Rx into the FreeRTOS simulator contain another packet? */
		if( uxStreamBufferGetSize( xRecvBuffer ) > sizeof( xHeader ) )
		{
			/* Get the next packet. */
			uxStreamBufferGet( xRecvBuffer, 0, (uint8_t*)&xHeader, sizeof( xHeader ), pdFALSE );
			uxStreamBufferGet( xRecvBuffer, 0, (uint8_t*)ucRecvBuffer, ( size_t ) xHeader.len, pdFALSE );
			pucPacketData = ucRecvBuffer;
			pxHeader = &xHeader;

			iptraceNETWORK_INTERFACE_RECEIVE();

			eResult = ipCONSIDER_FRAME_FOR_PROCESSING( pucPacketData );
			if( eResult == eProcessBuffer )
			{
				/* Will the data fit into the frame buffer? */
				if( pxHeader->len <= ipTOTAL_ETHERNET_FRAME_SIZE )
				{
					/* Obtain a buffer into which the data can be placed.  This
					is only	an interrupt simulator, not a real interrupt, so it
					is ok to call the task level function here, but note that
					some buffer implementations cannot be called from a real
					interrupt. */
					pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( pxHeader->len, 0 );

					if( pxNetworkBuffer != NULL )
					{
						memcpy( pxNetworkBuffer->pucEthernetBuffer, pucPacketData, pxHeader->len );
						pxNetworkBuffer->xDataLength = ( size_t ) pxHeader->len;

						#if( niDISRUPT_PACKETS == 1 )
						{
							pxNetworkBuffer = vRxFaultInjection( pxNetworkBuffer, pucPacketData );
						}
						#endif /* niDISRUPT_PACKETS */

						if( pxNetworkBuffer != NULL )
						{
							xRxEvent.pvData = ( void * ) pxNetworkBuffer;

							/* Data was received and stored.  Send a message to
							the IP task to let it know. */
							if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL )
							{
								/* The buffer could not be sent to the stack so
								must be released again.  This is only an
								interrupt simulator, not a real interrupt, so it
								is ok to use the task level function here, but
								note no all buffer implementations will allow
								this function to be executed from a real
								interrupt. */
								vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
								iptraceETHERNET_RX_EVENT_LOST();
							}
						}
						else
						{
							/* The packet was already released or stored inside
							vRxFaultInjection().  Don't release it here. */
						}
					}
					else
					{
						iptraceETHERNET_RX_EVENT_LOST();
					}
				}
				else
				{
					/* Log that a packet was dropped because it would have
					overflowed the buffer, but there may be more buffers to
					process. */
				}
			}
		}
		else
		{
			/* There is no real way of simulating an interrupt.  Make sure
			other tasks can run. */
			vTaskDelay( configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY );
		}
	}
}
Example #13
0
XStatus emacps_send_message(xemacpsif_s *xemacpsif, NetworkBufferDescriptor_t *pxFirstBuffer, int iReleaseAfterSend )
{
NetworkBufferDescriptor_t *pxBuffer;
int head = xemacpsif->txHead;
int tail = xemacpsif->txTail;
int iHasSent = 0;
uint32_t ulBaseAddress = xemacpsif->emacps.Config.BaseAddress;

#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
	if( iReleaseAfterSend == pdFALSE )
	{
	NetworkBufferDescriptor_t *pxNewBuffer;

		pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxFirstBuffer, pxFirstBuffer->xDataLength );
		if( pxNewBuffer )
		{
			pxFirstBuffer = pxNewBuffer;
			iReleaseAfterSend = pdTRUE;
		}
	}
#endif

	/* Although it looks like more that 1 message can be sent,
	this possibility is NOT being used and not yet tested. */
	for( pxBuffer = pxFirstBuffer; pxBuffer != NULL;  )
	{
	NetworkBufferDescriptor_t *pxNextBuffer;

		if( ( head == tail ) && ( xemacpsif->txSegments[ head ].flags & XEMACPS_TXBUF_USED_MASK ) == 0 )
		{
//			FreeRTOS_printf( ( "emacps_send_message: All TX buffers full\n" ) );
			break;
		}
		if( xValidLength( pxBuffer->xDataLength ) )
		{
			/* Just a sanity check, the TX buffer will be assigned permanently. */
#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
			if( pxDMA_tx_buffers[ head ] != NULL )
#endif
			{
				uint32_t flags = 0;
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
				/* Move the message to unbuffered space in RAM. */
				pxDMA_tx_buffers[ head ] = ( uint32_t )pxBuffer->pucEthernetBuffer;
				if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
				{
					Xil_DCacheFlushRange( ( unsigned )pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength );
				}
#else
				/* Copy the message to unbuffered space in RAM. */
				memcpy( pxDMA_tx_buffers[ head ], pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength );
#endif
				/* Packets will be sent one-by-one, so for each packet
				the TXBUF_LAST bit will be set. */
				flags |= XEMACPS_TXBUF_LAST_MASK;
				flags |= ( pxBuffer->xDataLength & XEMACPS_TXBUF_LEN_MASK );
				if( head == ( ipconfigNIC_N_TX_DESC - 1 ) )
				{
					flags |= XEMACPS_TXBUF_WRAP_MASK;
				}

				/* Copy the address of the buffer and set the flags. */
				xemacpsif->txSegments[ head ].address = ( uint32_t )pxDMA_tx_buffers[ head ];
				xemacpsif->txSegments[ head ].flags = flags;

				iHasSent = pdTRUE;
			}
			if( ++head == ipconfigNIC_N_TX_DESC )
			{
				head = 0;
			}
		}
		/* Make a copy of the next field, before pxBuffer gets deleted */
		pxNextBuffer = pxBuffer->pxNextBuffer;
#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
		if( iReleaseAfterSend != pdFALSE )
		{
			vReleaseNetworkBufferAndDescriptor( pxBuffer );
		}
#endif
		pxBuffer = pxNextBuffer;
	}
	/* Update the TX-head index. These variable are declared volatile so they will be
	accessed as little as possible.	*/
	xemacpsif->txHead = head;

	/* Data Synchronization Barrier */
	dsb();

	if( iHasSent != pdFALSE )
	{
		/* Make STARTTX high */
		uint32_t ulValue = XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET);
		/* Start transmit */
		xemacpsif->txBusy = pdTRUE;
		XEmacPs_WriteReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET, ( ulValue | XEMACPS_NWCTRL_STARTTX_MASK ) );
	}
	dsb();

	return 0;
}