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; }
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; }
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; }
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; }
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; }
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; }
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 ); } }
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 ); } } } }
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; }
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 ); } } }
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; }