X-Git-Url: https://git.sur5r.net/?p=freertos;a=blobdiff_plain;f=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2Fportable%2FNetworkInterface%2FATSAM4E%2FNetworkInterface.c;fp=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2Fportable%2FNetworkInterface%2FATSAM4E%2FNetworkInterface.c;h=83c96a07c078c448f34e6d632b0537eeaa342b70;hp=3528269bdb908c29673e1a7e78ce9df880a03342;hb=4fd3a0b144b6aa91eabd79a1eaf9bc0c8397f433;hpb=b15dfacb6026af3b0ba697e5753844923b468d2b diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ATSAM4E/NetworkInterface.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ATSAM4E/NetworkInterface.c index 3528269bd..83c96a07c 100644 --- a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ATSAM4E/NetworkInterface.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ATSAM4E/NetworkInterface.c @@ -1,637 +1,637 @@ -/* -FreeRTOS+TCP V2.0.11 -Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - http://aws.amazon.com/freertos - http://www.FreeRTOS.org -*/ - -/* Standard includes. */ -#include -#include -#include - -/* FreeRTOS includes. */ -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" -#include "semphr.h" - -/* FreeRTOS+TCP includes. */ -#include "FreeRTOS_IP.h" -#include "FreeRTOS_Sockets.h" -#include "FreeRTOS_IP_Private.h" -#include "NetworkBufferManagement.h" -#include "NetworkInterface.h" - -/* Some files from the Atmel Software Framework */ -/*_RB_ The SAM4E portable layer has three different header files called gmac.h! */ -#include "instance/gmac.h" -#include -#include - -#ifndef BMSR_LINK_STATUS - #define BMSR_LINK_STATUS 0x0004 //!< Link status -#endif - -#ifndef PHY_LS_HIGH_CHECK_TIME_MS - /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not - receiving packets. */ - #define PHY_LS_HIGH_CHECK_TIME_MS 15000 -#endif - -#ifndef PHY_LS_LOW_CHECK_TIME_MS - /* Check if the LinkSStatus in the PHY is still low every second. */ - #define PHY_LS_LOW_CHECK_TIME_MS 1000 -#endif - -/* Interrupt events to process. Currently only the Rx event is processed -although code for other events is included to allow for possible future -expansion. */ -#define EMAC_IF_RX_EVENT 1UL -#define EMAC_IF_TX_EVENT 2UL -#define EMAC_IF_ERR_EVENT 4UL -#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) - -#define ETHERNET_CONF_PHY_ADDR BOARD_GMAC_PHY_ADDR - -#define HZ_PER_MHZ ( 1000000UL ) - -#ifndef EMAC_MAX_BLOCK_TIME_MS - #define EMAC_MAX_BLOCK_TIME_MS 100ul -#endif - -#if !defined( GMAC_USES_TX_CALLBACK ) || ( GMAC_USES_TX_CALLBACK != 1 ) - #error Please define GMAC_USES_TX_CALLBACK as 1 -#endif - -/* Default the size of the stack used by the EMAC deferred handler task to 4x -the size of the stack used by the idle task - but allow this to be overridden in -FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ -#ifndef configEMAC_TASK_STACK_SIZE - #define configEMAC_TASK_STACK_SIZE ( 4 * configMINIMAL_STACK_SIZE ) -#endif - -/*-----------------------------------------------------------*/ - -/* - * Wait a fixed time for the link status to indicate the network is up. - */ -static BaseType_t xGMACWaitLS( TickType_t xMaxTime ); - -#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 ) - void vGMACGenerateChecksum( uint8_t *apBuffer ); -#endif - -/* - * Called from the ASF GMAC driver. - */ -static void prvRxCallback( uint32_t ulStatus ); -static void prvTxCallback( uint32_t ulStatus, uint8_t *puc_buffer ); - -/* - * A deferred interrupt handler task that processes GMAC interrupts. - */ -static void prvEMACHandlerTask( void *pvParameters ); - -/* - * Initialise the ASF GMAC driver. - */ -static BaseType_t prvGMACInit( void ); - -/* - * Try to obtain an Rx packet from the hardware. - */ -static uint32_t prvEMACRxPoll( void ); - -/*-----------------------------------------------------------*/ - -/* Bit map of outstanding ETH interrupt events for processing. Currently only -the Rx interrupt is handled, although code is included for other events to -enable future expansion. */ -static volatile uint32_t ulISREvents; - -/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ -static uint32_t ulPHYLinkStatus = 0; -static volatile BaseType_t xGMACSwitchRequired; - -/* ethernet_phy_addr: the address of the PHY in use. -Atmel was a bit ambiguous about it so the address will be stored -in this variable, see ethernet_phy.c */ -extern int ethernet_phy_addr; - -/* LLMNR multicast address. */ -static const uint8_t llmnr_mac_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; - -/* The GMAC object as defined by the ASF drivers. */ -static gmac_device_t gs_gmac_dev; - -/* MAC address to use. */ -extern const uint8_t ucMACAddress[ 6 ]; - -/* Holds the handle of the task used as a deferred interrupt processor. The -handle is used so direct notifications can be sent to the task for all EMAC/DMA -related interrupts. */ -TaskHandle_t xEMACTaskHandle = NULL; - -static QueueHandle_t xTxBufferQueue; -int tx_release_count[ 4 ]; - -/* xTXDescriptorSemaphore is a counting semaphore with -a maximum count of GMAC_TX_BUFFERS, which is the number of -DMA TX descriptors. */ -static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; - -/*-----------------------------------------------------------*/ - -/* - * GMAC interrupt handler. - */ -void GMAC_Handler(void) -{ - xGMACSwitchRequired = pdFALSE; - - /* gmac_handler() may call prvRxCallback() which may change - the value of xGMACSwitchRequired. */ - gmac_handler( &gs_gmac_dev ); - - if( xGMACSwitchRequired != pdFALSE ) - { - portEND_SWITCHING_ISR( xGMACSwitchRequired ); - } -} -/*-----------------------------------------------------------*/ - -static void prvRxCallback( uint32_t ulStatus ) -{ - if( ( ( ulStatus & GMAC_RSR_REC ) != 0 ) && ( xEMACTaskHandle != NULL ) ) - { - /* let the prvEMACHandlerTask know that there was an RX event. */ - ulISREvents |= EMAC_IF_RX_EVENT; - /* Only an RX interrupt can wakeup prvEMACHandlerTask. */ - vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); - } -} -/*-----------------------------------------------------------*/ - -static void prvTxCallback( uint32_t ulStatus, uint8_t *puc_buffer ) -{ - if( ( xTxBufferQueue != NULL ) && ( xEMACTaskHandle != NULL ) ) - { - /* let the prvEMACHandlerTask know that there was an RX event. */ - ulISREvents |= EMAC_IF_TX_EVENT; - - vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); - xQueueSendFromISR( xTxBufferQueue, &puc_buffer, ( BaseType_t * ) &xGMACSwitchRequired ); - tx_release_count[ 2 ]++; - } -} -/*-----------------------------------------------------------*/ - -BaseType_t xNetworkInterfaceInitialise( void ) -{ -const TickType_t x5_Seconds = 5000UL; - - if( xEMACTaskHandle == NULL ) - { - prvGMACInit(); - - /* Wait at most 5 seconds for a Link Status in the PHY. */ - xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) ); - - /* The handler task is created at the highest possible priority to - ensure the interrupt handler can return directly to it. */ - xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle ); - configASSERT( xEMACTaskHandle ); - } - - if( xTxBufferQueue == NULL ) - { - xTxBufferQueue = xQueueCreate( GMAC_TX_BUFFERS, sizeof( void * ) ); - configASSERT( xTxBufferQueue ); - } - - if( xTXDescriptorSemaphore == NULL ) - { - xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) GMAC_TX_BUFFERS, ( UBaseType_t ) GMAC_TX_BUFFERS ); - configASSERT( xTXDescriptorSemaphore ); - } - /* When returning non-zero, the stack will become active and - start DHCP (in configured) */ - return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0; -} -/*-----------------------------------------------------------*/ - -BaseType_t xGetPhyLinkStatus( void ) -{ -BaseType_t xResult; - - /* This function returns true if the Link Status in the PHY is high. */ - if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) - { - xResult = pdTRUE; - } - else - { - xResult = pdFALSE; - } - - return xResult; -} -/*-----------------------------------------------------------*/ - -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; -} -/*-----------------------------------------------------------*/ - -static BaseType_t prvGMACInit( void ) -{ -uint32_t ncfgr; - - gmac_options_t gmac_option; - - memset( &gmac_option, '\0', sizeof( gmac_option ) ); - gmac_option.uc_copy_all_frame = 0; - gmac_option.uc_no_boardcast = 0; - memcpy( gmac_option.uc_mac_addr, ucMACAddress, sizeof( gmac_option.uc_mac_addr ) ); - - gs_gmac_dev.p_hw = GMAC; - gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option ); - - NVIC_SetPriority( GMAC_IRQn, configMAC_INTERRUPT_PRIORITY ); - NVIC_EnableIRQ( GMAC_IRQn ); - - /* Contact the Ethernet PHY and store it's address in 'ethernet_phy_addr' */ - ethernet_phy_init( GMAC, ETHERNET_CONF_PHY_ADDR, sysclk_get_cpu_hz() ); - - ethernet_phy_auto_negotiate( GMAC, ethernet_phy_addr ); - ethernet_phy_set_link( GMAC, ethernet_phy_addr, 1 ); - - /* The GMAC driver will call a hook prvRxCallback(), which - in turn will wake-up the task by calling vTaskNotifyGiveFromISR() */ - gmac_dev_set_rx_callback( &gs_gmac_dev, prvRxCallback ); - gmac_set_address( GMAC, 1, (uint8_t*)llmnr_mac_address ); - - ncfgr = GMAC_NCFGR_SPD | GMAC_NCFGR_FD; - - GMAC->GMAC_NCFGR = ( GMAC->GMAC_NCFGR & ~( GMAC_NCFGR_SPD | GMAC_NCFGR_FD ) ) | ncfgr; - - return 1; -} -/*-----------------------------------------------------------*/ - -static inline unsigned long ulReadMDIO( unsigned /*short*/ usAddress ) -{ -uint32_t ulValue, ulReturn; -int rc; - - gmac_enable_management( GMAC, 1 ); - rc = gmac_phy_read( GMAC, ethernet_phy_addr, usAddress, &ulValue ); - gmac_enable_management( GMAC, 0 ); - if( rc == GMAC_OK ) - { - ulReturn = ulValue; - } - else - { - ulReturn = 0UL; - } - - return ulReturn; -} -/*-----------------------------------------------------------*/ - -static BaseType_t xGMACWaitLS( TickType_t xMaxTime ) -{ -TickType_t xStartTime = xTaskGetTickCount(); -TickType_t xEndTime; -BaseType_t xReturn; -const TickType_t xShortTime = pdMS_TO_TICKS( 100UL ); - - for( ;; ) - { - xEndTime = xTaskGetTickCount(); - - if( ( xEndTime - xStartTime ) > xMaxTime ) - { - /* Wated more than xMaxTime, return. */ - xReturn = pdFALSE; - break; - } - - /* Check the link status again. */ - ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); - - if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) - { - /* Link is up - return. */ - xReturn = pdTRUE; - break; - } - - /* Link is down - wait in the Blocked state for a short while (to allow - other tasks to execute) before checking again. */ - vTaskDelay( xShortTime ); - } - - FreeRTOS_printf( ( "xGMACWaitLS: %ld (PHY %d) freq %lu Mz\n", - xReturn, - ethernet_phy_addr, - sysclk_get_cpu_hz() / HZ_PER_MHZ ) ); - - return xReturn; -} -/*-----------------------------------------------------------*/ - -//#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 ) - - void vGMACGenerateChecksum( uint8_t *apBuffer ) - { - ProtocolPacket_t *xProtPacket = (ProtocolPacket_t *)apBuffer; - - if ( xProtPacket->xTCPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) - { - IPHeader_t *pxIPHeader = &(xProtPacket->xTCPPacket.xIPHeader); - - /* Calculate the IP header checksum. */ - pxIPHeader->usHeaderChecksum = 0x00; - pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); - pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); - - /* Calculate the TCP checksum for an outgoing packet. */ - usGenerateProtocolChecksum( ( uint8_t * ) apBuffer, pdTRUE ); - } - } - -//#endif -/*-----------------------------------------------------------*/ - -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 prvEMACHandlerTask( void *pvParameters ) -{ -TimeOut_t xPhyTime; -TickType_t xPhyRemTime; -UBaseType_t uxLastMinBufferCount = 0, uxCount; -UBaseType_t uxCurrentCount; -#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) - UBaseType_t uxLastMinQueueSpace; -#endif -#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( ;; ) - { - uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); - if( uxLastMinBufferCount != uxCurrentCount ) - { - /* The logging produced below may be helpful - while tuning +TCP: see how many buffers are in use. */ - uxLastMinBufferCount = uxCurrentCount; - FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", - uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); - } - - #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) - { - uxCurrentCount = uxGetMinimumIPQueueSpace(); - if( uxLastMinQueueSpace != uxCurrentCount ) - { - /* The logging produced below may be helpful - while tuning +TCP: see how many buffers are in use. */ - uxLastMinQueueSpace = uxCurrentCount; - FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); - } - } - #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ - - 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 ); - } - } - } -} -/*-----------------------------------------------------------*/ +/* +FreeRTOS+TCP V2.0.11 +Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + http://aws.amazon.com/freertos + http://www.FreeRTOS.org +*/ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +/* Some files from the Atmel Software Framework */ +/*_RB_ The SAM4E portable layer has three different header files called gmac.h! */ +#include "instance/gmac.h" +#include +#include + +#ifndef BMSR_LINK_STATUS + #define BMSR_LINK_STATUS 0x0004 //!< Link status +#endif + +#ifndef PHY_LS_HIGH_CHECK_TIME_MS + /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not + receiving packets. */ + #define PHY_LS_HIGH_CHECK_TIME_MS 15000 +#endif + +#ifndef PHY_LS_LOW_CHECK_TIME_MS + /* Check if the LinkSStatus in the PHY is still low every second. */ + #define PHY_LS_LOW_CHECK_TIME_MS 1000 +#endif + +/* Interrupt events to process. Currently only the Rx event is processed +although code for other events is included to allow for possible future +expansion. */ +#define EMAC_IF_RX_EVENT 1UL +#define EMAC_IF_TX_EVENT 2UL +#define EMAC_IF_ERR_EVENT 4UL +#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) + +#define ETHERNET_CONF_PHY_ADDR BOARD_GMAC_PHY_ADDR + +#define HZ_PER_MHZ ( 1000000UL ) + +#ifndef EMAC_MAX_BLOCK_TIME_MS + #define EMAC_MAX_BLOCK_TIME_MS 100ul +#endif + +#if !defined( GMAC_USES_TX_CALLBACK ) || ( GMAC_USES_TX_CALLBACK != 1 ) + #error Please define GMAC_USES_TX_CALLBACK as 1 +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to 4x +the size of the stack used by the idle task - but allow this to be overridden in +FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 4 * configMINIMAL_STACK_SIZE ) +#endif + +/*-----------------------------------------------------------*/ + +/* + * Wait a fixed time for the link status to indicate the network is up. + */ +static BaseType_t xGMACWaitLS( TickType_t xMaxTime ); + +#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 ) + void vGMACGenerateChecksum( uint8_t *apBuffer ); +#endif + +/* + * Called from the ASF GMAC driver. + */ +static void prvRxCallback( uint32_t ulStatus ); +static void prvTxCallback( uint32_t ulStatus, uint8_t *puc_buffer ); + +/* + * A deferred interrupt handler task that processes GMAC interrupts. + */ +static void prvEMACHandlerTask( void *pvParameters ); + +/* + * Initialise the ASF GMAC driver. + */ +static BaseType_t prvGMACInit( void ); + +/* + * Try to obtain an Rx packet from the hardware. + */ +static uint32_t prvEMACRxPoll( void ); + +/*-----------------------------------------------------------*/ + +/* Bit map of outstanding ETH interrupt events for processing. Currently only +the Rx interrupt is handled, although code is included for other events to +enable future expansion. */ +static volatile uint32_t ulISREvents; + +/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ +static uint32_t ulPHYLinkStatus = 0; +static volatile BaseType_t xGMACSwitchRequired; + +/* ethernet_phy_addr: the address of the PHY in use. +Atmel was a bit ambiguous about it so the address will be stored +in this variable, see ethernet_phy.c */ +extern int ethernet_phy_addr; + +/* LLMNR multicast address. */ +static const uint8_t llmnr_mac_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; + +/* The GMAC object as defined by the ASF drivers. */ +static gmac_device_t gs_gmac_dev; + +/* MAC address to use. */ +extern const uint8_t ucMACAddress[ 6 ]; + +/* Holds the handle of the task used as a deferred interrupt processor. The +handle is used so direct notifications can be sent to the task for all EMAC/DMA +related interrupts. */ +TaskHandle_t xEMACTaskHandle = NULL; + +static QueueHandle_t xTxBufferQueue; +int tx_release_count[ 4 ]; + +/* xTXDescriptorSemaphore is a counting semaphore with +a maximum count of GMAC_TX_BUFFERS, which is the number of +DMA TX descriptors. */ +static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; + +/*-----------------------------------------------------------*/ + +/* + * GMAC interrupt handler. + */ +void GMAC_Handler(void) +{ + xGMACSwitchRequired = pdFALSE; + + /* gmac_handler() may call prvRxCallback() which may change + the value of xGMACSwitchRequired. */ + gmac_handler( &gs_gmac_dev ); + + if( xGMACSwitchRequired != pdFALSE ) + { + portEND_SWITCHING_ISR( xGMACSwitchRequired ); + } +} +/*-----------------------------------------------------------*/ + +static void prvRxCallback( uint32_t ulStatus ) +{ + if( ( ( ulStatus & GMAC_RSR_REC ) != 0 ) && ( xEMACTaskHandle != NULL ) ) + { + /* let the prvEMACHandlerTask know that there was an RX event. */ + ulISREvents |= EMAC_IF_RX_EVENT; + /* Only an RX interrupt can wakeup prvEMACHandlerTask. */ + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); + } +} +/*-----------------------------------------------------------*/ + +static void prvTxCallback( uint32_t ulStatus, uint8_t *puc_buffer ) +{ + if( ( xTxBufferQueue != NULL ) && ( xEMACTaskHandle != NULL ) ) + { + /* let the prvEMACHandlerTask know that there was an RX event. */ + ulISREvents |= EMAC_IF_TX_EVENT; + + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); + xQueueSendFromISR( xTxBufferQueue, &puc_buffer, ( BaseType_t * ) &xGMACSwitchRequired ); + tx_release_count[ 2 ]++; + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ +const TickType_t x5_Seconds = 5000UL; + + if( xEMACTaskHandle == NULL ) + { + prvGMACInit(); + + /* Wait at most 5 seconds for a Link Status in the PHY. */ + xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) ); + + /* The handler task is created at the highest possible priority to + ensure the interrupt handler can return directly to it. */ + xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle ); + configASSERT( xEMACTaskHandle ); + } + + if( xTxBufferQueue == NULL ) + { + xTxBufferQueue = xQueueCreate( GMAC_TX_BUFFERS, sizeof( void * ) ); + configASSERT( xTxBufferQueue ); + } + + if( xTXDescriptorSemaphore == NULL ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) GMAC_TX_BUFFERS, ( UBaseType_t ) GMAC_TX_BUFFERS ); + configASSERT( xTXDescriptorSemaphore ); + } + /* When returning non-zero, the stack will become active and + start DHCP (in configured) */ + return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0; +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ +BaseType_t xResult; + + /* This function returns true if the Link Status in the PHY is high. */ + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + xResult = pdTRUE; + } + else + { + xResult = pdFALSE; + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +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; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvGMACInit( void ) +{ +uint32_t ncfgr; + + gmac_options_t gmac_option; + + memset( &gmac_option, '\0', sizeof( gmac_option ) ); + gmac_option.uc_copy_all_frame = 0; + gmac_option.uc_no_boardcast = 0; + memcpy( gmac_option.uc_mac_addr, ucMACAddress, sizeof( gmac_option.uc_mac_addr ) ); + + gs_gmac_dev.p_hw = GMAC; + gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option ); + + NVIC_SetPriority( GMAC_IRQn, configMAC_INTERRUPT_PRIORITY ); + NVIC_EnableIRQ( GMAC_IRQn ); + + /* Contact the Ethernet PHY and store it's address in 'ethernet_phy_addr' */ + ethernet_phy_init( GMAC, ETHERNET_CONF_PHY_ADDR, sysclk_get_cpu_hz() ); + + ethernet_phy_auto_negotiate( GMAC, ethernet_phy_addr ); + ethernet_phy_set_link( GMAC, ethernet_phy_addr, 1 ); + + /* The GMAC driver will call a hook prvRxCallback(), which + in turn will wake-up the task by calling vTaskNotifyGiveFromISR() */ + gmac_dev_set_rx_callback( &gs_gmac_dev, prvRxCallback ); + gmac_set_address( GMAC, 1, (uint8_t*)llmnr_mac_address ); + + ncfgr = GMAC_NCFGR_SPD | GMAC_NCFGR_FD; + + GMAC->GMAC_NCFGR = ( GMAC->GMAC_NCFGR & ~( GMAC_NCFGR_SPD | GMAC_NCFGR_FD ) ) | ncfgr; + + return 1; +} +/*-----------------------------------------------------------*/ + +static inline unsigned long ulReadMDIO( unsigned /*short*/ usAddress ) +{ +uint32_t ulValue, ulReturn; +int rc; + + gmac_enable_management( GMAC, 1 ); + rc = gmac_phy_read( GMAC, ethernet_phy_addr, usAddress, &ulValue ); + gmac_enable_management( GMAC, 0 ); + if( rc == GMAC_OK ) + { + ulReturn = ulValue; + } + else + { + ulReturn = 0UL; + } + + return ulReturn; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xGMACWaitLS( TickType_t xMaxTime ) +{ +TickType_t xStartTime = xTaskGetTickCount(); +TickType_t xEndTime; +BaseType_t xReturn; +const TickType_t xShortTime = pdMS_TO_TICKS( 100UL ); + + for( ;; ) + { + xEndTime = xTaskGetTickCount(); + + if( ( xEndTime - xStartTime ) > xMaxTime ) + { + /* Wated more than xMaxTime, return. */ + xReturn = pdFALSE; + break; + } + + /* Check the link status again. */ + ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + /* Link is up - return. */ + xReturn = pdTRUE; + break; + } + + /* Link is down - wait in the Blocked state for a short while (to allow + other tasks to execute) before checking again. */ + vTaskDelay( xShortTime ); + } + + FreeRTOS_printf( ( "xGMACWaitLS: %ld (PHY %d) freq %lu Mz\n", + xReturn, + ethernet_phy_addr, + sysclk_get_cpu_hz() / HZ_PER_MHZ ) ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +//#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 ) + + void vGMACGenerateChecksum( uint8_t *apBuffer ) + { + ProtocolPacket_t *xProtPacket = (ProtocolPacket_t *)apBuffer; + + if ( xProtPacket->xTCPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) + { + IPHeader_t *pxIPHeader = &(xProtPacket->xTCPPacket.xIPHeader); + + /* Calculate the IP header checksum. */ + pxIPHeader->usHeaderChecksum = 0x00; + pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); + pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); + + /* Calculate the TCP checksum for an outgoing packet. */ + usGenerateProtocolChecksum( ( uint8_t * ) apBuffer, pdTRUE ); + } + } + +//#endif +/*-----------------------------------------------------------*/ + +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 prvEMACHandlerTask( void *pvParameters ) +{ +TimeOut_t xPhyTime; +TickType_t xPhyRemTime; +UBaseType_t uxLastMinBufferCount = 0, uxCount; +UBaseType_t uxCurrentCount; +#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + UBaseType_t uxLastMinQueueSpace; +#endif +#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( ;; ) + { + uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); + if( uxLastMinBufferCount != uxCurrentCount ) + { + /* The logging produced below may be helpful + while tuning +TCP: see how many buffers are in use. */ + uxLastMinBufferCount = uxCurrentCount; + FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", + uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); + } + + #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + { + uxCurrentCount = uxGetMinimumIPQueueSpace(); + if( uxLastMinQueueSpace != uxCurrentCount ) + { + /* The logging produced below may be helpful + while tuning +TCP: see how many buffers are in use. */ + uxLastMinQueueSpace = uxCurrentCount; + FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); + } + } + #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ + + 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 ); + } + } + } +} +/*-----------------------------------------------------------*/