summaryrefslogtreecommitdiff
path: root/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/NetworkInterface.c
diff options
context:
space:
mode:
Diffstat (limited to 'FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/NetworkInterface.c')
-rw-r--r--FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/NetworkInterface.c928
1 files changed, 928 insertions, 0 deletions
diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/NetworkInterface.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/NetworkInterface.c
new file mode 100644
index 000000000..543cc1d62
--- /dev/null
+++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/NetworkInterface.c
@@ -0,0 +1,928 @@
+/*
+FreeRTOS+TCP V2.2.1
+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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* 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 "FreeRTOS_ARP.h"
+#include "NetworkBufferManagement.h"
+#include "NetworkInterface.h"
+
+/* Some files from the Atmel Software Framework */
+/* gmac_SAM.[ch] is a combination of the gmac.[ch] for both SAM4E and SAME70. */
+#include "gmac_SAM.h"
+#include <sysclk.h>
+#include "phyhandling.h"
+
+/* This file is included to see if 'CONF_BOARD_ENABLE_CACHE' is defined. */
+#include "conf_board.h"
+
+
+/* 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 )
+
+/* 1536 bytes is more than needed, 1524 would be enough.
+But 1536 is a multiple of 32, which gives a great alignment for
+cached memories. */
+
+#define NETWORK_BUFFER_SIZE 1536
+
+#ifndef EMAC_MAX_BLOCK_TIME_MS
+ /* The task 'prvEMACHandlerTask()' will wake-up every 100 ms, to see
+ if something has to be done, mostly checking if the PHY has a
+ change in Link Status. */
+ #define EMAC_MAX_BLOCK_TIME_MS 100ul
+#endif
+
+#if( ipconfigZERO_COPY_RX_DRIVER == 0 )
+ #error This driver works optimal if ipconfigZERO_COPY_RX_DRIVER is defined as 1
+#endif
+
+#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
+ #error This driver works optimal if ipconfigZERO_COPY_TX_DRIVER is defined 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
+
+#ifndef niEMAC_HANDLER_TASK_PRIORITY
+ #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1
+#endif
+
+#if( __DCACHE_PRESENT != 0 ) && defined( CONF_BOARD_ENABLE_CACHE )
+ #include "core_cm7.h"
+ #warning This driver assumes the presence of DCACHE
+ #define NETWORK_BUFFERS_CACHED 1
+ #define CACHE_LINE_SIZE 32
+ #define NETWORK_BUFFER_HEADER_SIZE ( ipconfigPACKET_FILLER_SIZE + 8 )
+
+ static void cache_clean_invalidate()
+ {
+ /* If you application crashes here, make sure that SCB_EnableDCache() has been called. */
+ SCB_CleanInvalidateDCache();
+ }
+ /*-----------------------------------------------------------*/
+
+ static void cache_clean_invalidate_by_addr(uint32_t addr, uint32_t size)
+ {
+ /* SAME70 does not have clean/invalidate per area. */
+ /* SCB_CleanInvalidateDCache_by_Addr( ( uint32_t * )addr, size); */
+ SCB_CleanInvalidateDCache();
+ }
+ /*-----------------------------------------------------------*/
+
+ static void cache_invalidate_by_addr(addr, size) \
+ {
+ /* SAME70 does not have clean/invalidate per area. */
+ /* SCB_InvalidateDCache_by_Addr( ( uint32_t * )addr, size); */
+ SCB_InvalidateDCache();
+ }
+ /*-----------------------------------------------------------*/
+
+#else
+ #warning Sure there is no caching?
+ #define cache_clean_invalidate() do {} while( 0 )
+ #define cache_clean_invalidate_by_addr(addr, size) do {} while( 0 )
+ #define cache_invalidate_by_addr(addr, size) do {} while( 0 )
+#endif
+
+/*-----------------------------------------------------------*/
+
+/*
+ * Update settings in GMAC for speed and duplex.
+ */
+static void prvEthernetUpdateConfig( BaseType_t xForce );
+
+/*
+ * Access functions to the PHY's: read() and write() to be used by
+ * phyHandling.c.
+ */
+static BaseType_t xPHY_Read( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue );
+static BaseType_t xPHY_Write( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue );
+
+#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 )
+ void vGMACGenerateChecksum( uint8_t *apBuffer, size_t uxLength );
+#endif
+
+/*
+ * Called from the ASF GMAC driver.
+ */
+void xRxCallback( uint32_t ulStatus );
+void xTxCallback( 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 );
+
+/*
+ * Handle transmission errors.
+ */
+static void hand_tx_errors( 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 volatile BaseType_t xGMACSwitchRequired;
+
+/* 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;
+
+/* For local use only: describe the PHY's properties: */
+const PhyProperties_t xPHYProperties =
+{
+ #if( ipconfigETHERNET_AN_ENABLE != 0 )
+ .ucSpeed = PHY_SPEED_AUTO,
+ .ucDuplex = PHY_DUPLEX_AUTO,
+ #else
+ #if( ipconfigETHERNET_USE_100MB != 0 )
+ .ucSpeed = PHY_SPEED_100,
+ #else
+ .ucSpeed = PHY_SPEED_10,
+ #endif
+
+ #if( ipconfigETHERNET_USE_FULL_DUPLEX != 0 )
+ .ucDuplex = PHY_DUPLEX_FULL,
+ #else
+ .ucDuplex = PHY_DUPLEX_HALF,
+ #endif
+ #endif
+
+ #if( ipconfigETHERNET_AN_ENABLE != 0 ) && ( ipconfigETHERNET_AUTO_CROSS_ENABLE != 0 )
+ .ucMDI_X = PHY_MDIX_AUTO,
+ #elif( ipconfigETHERNET_CROSSED_LINK != 0 )
+ .ucMDI_X = PHY_MDIX_CROSSED,
+ #else
+ .ucMDI_X = PHY_MDIX_DIRECT,
+ #endif
+};
+
+/* All PHY handling code has now been separated from the NetworkInterface.c,
+see "../Common/phyHandling.c". */
+static EthernetPhy_t xPhyObject;
+
+/*-----------------------------------------------------------*/
+
+/*
+ * GMAC interrupt handler.
+ */
+void GMAC_Handler(void)
+{
+ xGMACSwitchRequired = pdFALSE;
+
+ /* gmac_handler() may call xRxCallback() which may change
+ the value of xGMACSwitchRequired. */
+ gmac_handler( &gs_gmac_dev );
+
+ if( xGMACSwitchRequired != pdFALSE )
+ {
+ portEND_SWITCHING_ISR( xGMACSwitchRequired );
+ }
+}
+/*-----------------------------------------------------------*/
+
+void xRxCallback( 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 );
+ }
+}
+/*-----------------------------------------------------------*/
+
+void returnTxBuffer(uint8_t *puc_buffer)
+{
+ /* Called from a non-ISR context. */
+ if( xTxBufferQueue != NULL )
+ {
+ xQueueSend( xTxBufferQueue, &puc_buffer, 0 );
+ xTaskNotifyGive( xEMACTaskHandle );
+ ulISREvents |= EMAC_IF_TX_EVENT;
+ }
+}
+
+void xTxCallback( uint32_t ulStatus, uint8_t *puc_buffer )
+{
+ if( ( xTxBufferQueue != NULL ) && ( xEMACTaskHandle != NULL ) )
+ {
+ /* let the prvEMACHandlerTask know that there was an TX event. */
+ ulISREvents |= EMAC_IF_TX_EVENT;
+ /* Wakeup prvEMACHandlerTask. */
+ vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired );
+ xQueueSendFromISR( xTxBufferQueue, &puc_buffer, ( BaseType_t * ) &xGMACSwitchRequired );
+ tx_release_count[ 2 ]++;
+ }
+}
+/*-----------------------------------------------------------*/
+
+
+/*
+ The two standard defines 'GMAC_MAN_RW_TYPE' and 'GMAC_MAN_READ_ONLY'
+ are incorrect.
+ Therefore, use the following:
+*/
+
+#define GMAC_MAINTENANCE_READ_ACCESS (2)
+#define GMAC_MAINTENANCE_WRITE_ACCESS (1)
+
+static BaseType_t xPHY_Read( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue )
+{
+BaseType_t xReturn;
+UBaseType_t uxWasEnabled;
+
+ /* Wait until bus idle */
+ while ((GMAC->GMAC_NSR & GMAC_NSR_IDLE) == 0);
+ /* Write maintain register */
+ /*
+ * OP: Operation: 10 is read. 01 is write.
+ */
+ uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u;
+ if( uxWasEnabled == 0u )
+ {
+ /* Enable further GMAC maintenance. */
+ GMAC->GMAC_NCR |= GMAC_NCR_MPE;
+ }
+ GMAC->GMAC_MAN = GMAC_MAN_WTN(GMAC_MAN_CODE_VALUE)
+ | GMAC_MAN_CLTTO
+ | GMAC_MAN_PHYA(xAddress)
+ | GMAC_MAN_REGA(xRegister)
+ | GMAC_MAN_OP(GMAC_MAINTENANCE_READ_ACCESS)
+ | GMAC_MAN_DATA( (uint16_t)0u );
+
+ if (gmac_wait_phy(GMAC, MAC_PHY_RETRY_MAX) == GMAC_TIMEOUT)
+ {
+ *pulValue = (uint32_t)0xffffu;
+ xReturn = -1;
+ }
+ else
+ {
+ /* Wait until bus idle */
+ while ((GMAC->GMAC_NSR & GMAC_NSR_IDLE) == 0);
+
+ /* Return data */
+ *pulValue = (uint32_t)(GMAC->GMAC_MAN & GMAC_MAN_DATA_Msk);
+
+ xReturn = 0;
+ }
+ if( uxWasEnabled == 0u )
+ {
+ /* Disable further GMAC maintenance. */
+ GMAC->GMAC_NCR &= ~GMAC_NCR_MPE;
+ }
+
+ return xReturn;
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t xPHY_Write( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue )
+{
+BaseType_t xReturn;
+UBaseType_t uxWasEnabled;
+
+ /* Wait until bus idle */
+ while ((GMAC->GMAC_NSR & GMAC_NSR_IDLE) == 0);
+ /* Write maintain register */
+ uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u;
+ if( uxWasEnabled == 0u )
+ {
+ /* Enable further GMAC maintenance. */
+ GMAC->GMAC_NCR |= GMAC_NCR_MPE;
+ }
+ GMAC->GMAC_MAN = GMAC_MAN_WTN(GMAC_MAN_CODE_VALUE)
+ | GMAC_MAN_CLTTO
+ | GMAC_MAN_PHYA(xAddress)
+ | GMAC_MAN_REGA(xRegister)
+ | GMAC_MAN_OP(GMAC_MAINTENANCE_WRITE_ACCESS)
+ | GMAC_MAN_DATA( (uint16_t)ulValue );
+
+ if (gmac_wait_phy(GMAC, MAC_PHY_RETRY_MAX) == GMAC_TIMEOUT )
+ {
+ xReturn = -1;
+ }
+ else
+ {
+ xReturn = 0;
+ }
+ if( uxWasEnabled == 0u )
+ {
+ /* Disable further GMAC maintenance. */
+ GMAC->GMAC_NCR &= ~GMAC_NCR_MPE;
+ }
+
+ return xReturn;
+}
+/*-----------------------------------------------------------*/
+
+BaseType_t xNetworkInterfaceInitialise( void )
+{
+const TickType_t x5_Seconds = 5000UL;
+
+ if( xEMACTaskHandle == NULL )
+ {
+ prvGMACInit();
+
+ cache_clean_invalidate();
+
+ /* 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, niEMAC_HANDLER_TASK_PRIORITY, &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 xGetPhyLinkStatus();
+}
+/*-----------------------------------------------------------*/
+
+BaseType_t xGetPhyLinkStatus( void )
+{
+BaseType_t xReturn;
+
+ if( xPhyObject.ulLinkStatusMask != 0 )
+ {
+ xReturn = pdPASS;
+ }
+ else
+ {
+ xReturn = pdFAIL;
+ }
+
+ return xReturn;
+}
+/*-----------------------------------------------------------*/
+
+/** The GMAC TX errors to handle */
+#define GMAC_TX_ERRORS (GMAC_TSR_TFC | GMAC_TSR_HRESP)
+
+static void hand_tx_errors( void )
+{
+/* Handle GMAC underrun or AHB errors. */
+ if (gmac_get_tx_status(GMAC) & GMAC_TX_ERRORS) {
+
+ gmac_enable_transmit(GMAC, false);
+
+ /* Reinit TX descriptors. */
+// gmac_tx_init(ps_gmac_dev);
+ gmac_reset_tx_mem(&gs_gmac_dev);
+ /* Clear error status. */
+ gmac_clear_tx_status(GMAC, GMAC_TX_ERRORS);
+
+ gmac_enable_transmit(GMAC, true);
+ }
+}
+
+volatile IPPacket_t *pxSendPacket;
+
+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 );
+uint32_t ulTransmitSize;
+
+ ulTransmitSize = pxDescriptor->xDataLength;
+
+ pxSendPacket = (IPPacket_t *)pxDescriptor->pucEthernetBuffer;
+
+ if( ulTransmitSize > NETWORK_BUFFER_SIZE )
+ {
+ ulTransmitSize = NETWORK_BUFFER_SIZE;
+ }
+ /* A do{}while(0) loop is introduced to allow the use of multiple break
+ statement. */
+ do {
+ if( xPhyObject.ulLinkStatusMask == 0ul )
+ {
+ /* 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;
+ }
+ hand_tx_errors();
+ 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 */
+
+ #if( NETWORK_BUFFERS_CACHED != 0 )
+ {
+ uint32_t xlength = CACHE_LINE_SIZE * ( ( ulTransmitSize + NETWORK_BUFFER_HEADER_SIZE + CACHE_LINE_SIZE - 1 ) / CACHE_LINE_SIZE );
+ uint32_t xAddress = ( uint32_t )( pxDescriptor->pucEthernetBuffer - NETWORK_BUFFER_HEADER_SIZE );
+ cache_clean_invalidate_by_addr( xAddress, xlength );
+ }
+ #endif
+
+ gmac_dev_write( &gs_gmac_dev, (void *)pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
+
+ #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( ipFALSE_BOOL );
+
+ if( bReleaseAfterSend != pdFALSE )
+ {
+ vReleaseNetworkBufferAndDescriptor( pxDescriptor );
+ }
+ return pdTRUE;
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvGMACInit( void )
+{
+uint32_t ncfgr;
+
+ gmac_options_t gmac_option;
+
+ gmac_enable_management(GMAC, true);
+ /* Enable further GMAC maintenance. */
+ GMAC->GMAC_NCR |= GMAC_NCR_MPE;
+
+ 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 );
+
+ {
+ /* Set MDC clock divider. */
+ gmac_set_mdc_clock( GMAC, sysclk_get_cpu_hz() );
+
+ vPhyInitialise( &xPhyObject, xPHY_Read, xPHY_Write );
+ xPhyDiscover( &xPhyObject );
+ xPhyConfigure( &xPhyObject, &xPHYProperties );
+
+ /* For a reset / reconfigure of the EMAC. */
+ prvEthernetUpdateConfig( pdTRUE );
+
+ /* Select Media Independent Interface type */
+ #if( SAME70 != 0 )
+ {
+ /* Selecting RMII mode. */
+ GMAC->GMAC_UR &= ~GMAC_UR_RMII;
+ }
+ #else
+ {
+ gmac_select_mii_mode(GMAC, ETH_PHY_MODE);
+ }
+ #endif
+
+ gmac_enable_transmit(GMAC, true);
+ gmac_enable_receive(GMAC, true);
+
+ }
+
+ gmac_enable_management(GMAC, true);
+
+ gmac_set_address( GMAC, 1, (uint8_t*)llmnr_mac_address );
+
+ gmac_enable_management(GMAC, false);
+ /* Disable further GMAC maintenance. */
+ GMAC->GMAC_NCR &= ~GMAC_NCR_MPE;
+
+ return 1;
+}
+/*-----------------------------------------------------------*/
+
+static void prvEthernetUpdateConfig( BaseType_t xForce )
+{
+ FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS mask %02lX Force %d\n",
+ xPhyObject.ulLinkStatusMask,
+ ( int )xForce ) );
+
+ if( ( xForce != pdFALSE ) || ( xPhyObject.ulLinkStatusMask != 0 ) )
+ {
+ #if( ipconfigETHERNET_AN_ENABLE != 0 )
+ {
+ UBaseType_t uxWasEnabled;
+
+ /* Restart the auto-negotiation. */
+ uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u;
+ if( uxWasEnabled == 0u )
+ {
+ /* Enable further GMAC maintenance. */
+ GMAC->GMAC_NCR |= GMAC_NCR_MPE;
+ }
+ xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) );
+
+ /* Configure the MAC with the Duplex Mode fixed by the
+ auto-negotiation process. */
+ if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL )
+ {
+ gmac_enable_full_duplex(GMAC, pdTRUE);
+ }
+ else
+ {
+ gmac_enable_full_duplex(GMAC, pdFALSE);
+ }
+
+ /* Configure the MAC with the speed fixed by the
+ auto-negotiation process. */
+ if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 )
+ {
+ gmac_set_speed(GMAC, pdFALSE);
+ }
+ else
+ {
+ gmac_set_speed(GMAC, pdTRUE);
+ }
+ if( uxWasEnabled == 0u )
+ {
+ /* Enable further GMAC maintenance. */
+ GMAC->GMAC_NCR &= ~GMAC_NCR_MPE;
+ }
+ }
+ #else
+ {
+ if( xPHYProperties.ucDuplex == PHY_DUPLEX_FULL )
+ {
+ xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_FULL;
+ gmac_enable_full_duplex(GMAC, pdTRUE);
+ }
+ else
+ {
+ xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_HALF;
+ gmac_enable_full_duplex(GMAC, pdFALSE);
+ }
+
+ if( xPHYProperties.ucSpeed == PHY_SPEED_100 )
+ {
+ xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_100;
+ gmac_set_speed(GMAC, pdTRUE);
+ }
+ else
+ {
+ xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_10;
+ gmac_set_speed(GMAC, pdFALSE);
+ }
+
+ xPhyObject.xPhyPreferences.ucMDI_X = PHY_MDIX_AUTO;
+
+ /* Use predefined (fixed) configuration. */
+ xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) );
+ }
+ #endif
+
+ }
+}
+/*-----------------------------------------------------------*/
+
+void vGMACGenerateChecksum( uint8_t *pucBuffer, size_t uxLength )
+{
+ProtocolPacket_t *xProtPacket = ( ProtocolPacket_t * ) pucBuffer;
+
+ 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( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );
+ pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );
+
+ /* Calculate the TCP checksum for an outgoing packet. */
+ usGenerateProtocolChecksum( pucBuffer, uxLength, pdTRUE );
+ }
+}
+/*-----------------------------------------------------------*/
+
+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 };
+uint8_t *pucDMABuffer = 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, &pucDMABuffer );
+
+ 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();
+ #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
+ {
+ pxNextNetworkBufferDescriptor = pxPacketBuffer_to_NetworkBuffer( pucDMABuffer );
+ if( pxNextNetworkBufferDescriptor == NULL )
+ {
+ /* STrange: can not translate from a DMA buffer to a Network Buffer. */
+ break;
+ }
+ }
+ #endif /* ipconfigZERO_COPY_RX_DRIVER */
+
+ 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;
+}
+/*-----------------------------------------------------------*/
+
+volatile UBaseType_t uxLastMinBufferCount = 0;
+#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
+ volatile UBaseType_t uxLastMinQueueSpace;
+#endif
+volatile UBaseType_t uxCurrentSemCount;
+volatile UBaseType_t uxLowestSemCount;
+
+void vCheckBuffersAndQueue( void )
+{
+static UBaseType_t 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 */
+ 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( xTXDescriptorSemaphore != NULL )
+ {
+ uxCurrentSemCount = uxSemaphoreGetCount( xTXDescriptorSemaphore );
+ if( uxLowestSemCount > uxCurrentSemCount )
+ {
+ uxLowestSemCount = uxCurrentSemCount;
+ FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) );
+ }
+
+ }
+
+}
+/*-----------------------------------------------------------*/
+
+extern uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * NETWORK_BUFFER_SIZE ];
+void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
+{
+uint8_t *ucRAMBuffer = ucNetworkPackets;
+uint32_t ulIndex;
+
+ for( ulIndex = 0; ulIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ulIndex++ )
+ {
+ pxNetworkBuffers[ ulIndex ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
+ *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ulIndex ] ) );
+ ucRAMBuffer += NETWORK_BUFFER_SIZE;
+ }
+ cache_clean_invalidate();
+}
+/*-----------------------------------------------------------*/
+
+static void prvEMACHandlerTask( void *pvParameters )
+{
+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 );
+
+ for( ;; )
+ {
+ xResult = 0;
+ 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;
+ }
+ gmac_enable_management(GMAC, true);
+ if( xPhyCheckLinkStatus( &xPhyObject, xResult ) != 0 )
+ {
+ /* Something has changed to a Link Status, need re-check. */
+ prvEthernetUpdateConfig( pdFALSE );
+ }
+ gmac_enable_management(GMAC, false);
+ }
+}
+/*-----------------------------------------------------------*/