X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;ds=sidebyside;f=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2Fportable%2FNetworkInterface%2FSTM32Fxx%2FNetworkInterface.c;h=b9278c0429f610119232cbb4283452b56f39d4cb;hb=b15dfacb6026af3b0ba697e5753844923b468d2b;hp=6fad9216e8aaa5c2f40902cafd5db637f4116d6b;hpb=4334233a064299a09d167a497889d3860932a587;p=freertos diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/STM32Fxx/NetworkInterface.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/STM32Fxx/NetworkInterface.c index 6fad9216e..b9278c042 100644 --- a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/STM32Fxx/NetworkInterface.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/STM32Fxx/NetworkInterface.c @@ -1,1287 +1,1421 @@ -/* - * Some constants, hardware definitions and comments taken from ST's HAL driver - * library, COPYRIGHT(c) 2015 STMicroelectronics. - */ - -/* -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 "FreeRTOS_DNS.h" -#include "FreeRTOS_ARP.h" -#include "NetworkBufferManagement.h" -#include "NetworkInterface.h" -#include "phyHandling.h" - -/* ST includes. */ -#if defined( STM32F7xx ) - #include "stm32f7xx_hal.h" -#elif defined( STM32F4xx ) - #include "stm32f4xx_hal.h" -#elif defined( STM32F2xx ) - #include "stm32f2xx_hal.h" -#elif !defined( _lint ) /* Lint does not like an #error */ - #error What part? -#endif - -#include "stm32fxx_hal_eth.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 ) - -#define ETH_DMA_ALL_INTS \ - ( ETH_DMA_IT_TST | ETH_DMA_IT_PMT | ETH_DMA_IT_MMC | ETH_DMA_IT_NIS | ETH_DMA_IT_ER | \ - ETH_DMA_IT_FBE | ETH_DMA_IT_RWT | ETH_DMA_IT_RPS | ETH_DMA_IT_RBU | ETH_DMA_IT_R | \ - ETH_DMA_IT_TU | ETH_DMA_IT_RO | ETH_DMA_IT_TJT | ETH_DMA_IT_TPS | ETH_DMA_IT_T ) - -#ifndef niEMAC_HANDLER_TASK_PRIORITY - #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1 -#endif - -#define ipFRAGMENT_OFFSET_BIT_MASK ( ( uint16_t ) 0x0fff ) /* The bits in the two byte IP header field that make up the fragment offset value. */ - -#if( ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) || ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) ) - #warning Consider enabling checksum offloading -#endif - -#ifndef niDESCRIPTOR_WAIT_TIME_MS - #define niDESCRIPTOR_WAIT_TIME_MS 250uL -#endif - -/* - * Most users will want a PHY that negotiates about - * the connection properties: speed, dmix and duplex. - * On some rare cases, you want to select what is being - * advertised, properties like MDIX and duplex. - */ - -#if !defined( ipconfigETHERNET_AN_ENABLE ) - /* Enable auto-negotiation */ - #define ipconfigETHERNET_AN_ENABLE 1 -#endif - -#if !defined( ipconfigETHERNET_AUTO_CROSS_ENABLE ) - #define ipconfigETHERNET_AUTO_CROSS_ENABLE 1 -#endif - -#if( ipconfigETHERNET_AN_ENABLE == 0 ) - /* - * The following three defines are only used in case there - * is no auto-negotiation. - */ - #if !defined( ipconfigETHERNET_CROSSED_LINK ) - #define ipconfigETHERNET_CROSSED_LINK 1 - #endif - - #if !defined( ipconfigETHERNET_USE_100MB ) - #define ipconfigETHERNET_USE_100MB 1 - #endif - - #if !defined( ipconfigETHERNET_USE_FULL_DUPLEX ) - #define ipconfigETHERNET_USE_FULL_DUPLEX 1 - #endif -#endif /* ipconfigETHERNET_AN_ENABLE == 0 */ - -/* Default the size of the stack used by the EMAC deferred handler task to twice -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 ( 2 * configMINIMAL_STACK_SIZE ) -#endif - -/* Two choices must be made: RMII versus MII, -and the index of the PHY in use ( between 0 and 31 ). */ -#ifndef ipconfigUSE_RMII - #ifdef STM32F7xx - #define ipconfigUSE_RMII 1 - #else - #define ipconfigUSE_RMII 0 - #endif /* STM32F7xx */ -#endif /* ipconfigUSE_RMII */ - -#if( ipconfigUSE_RMII != 0 ) - #warning Using RMII, make sure if this is correct -#else - #warning Using MII, make sure if this is correct -#endif - -typedef enum -{ - eMACInit, /* Must initialise MAC. */ - eMACPass, /* Initialisation was successful. */ - eMACFailed, /* Initialisation failed. */ -} eMAC_INIT_STATUS_TYPE; - -static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit; - -/*-----------------------------------------------------------*/ - -/* - * A deferred interrupt handler task that processes - */ -static void prvEMACHandlerTask( void *pvParameters ); - -/* - * Force a negotiation with the Switch or Router and wait for LS. - */ -static void prvEthernetUpdateConfig( BaseType_t xForce ); - -/* - * See if there is a new packet and forward it to the IP-task. - */ -static BaseType_t prvNetworkInterfaceInput( void ); - -#if( ipconfigUSE_LLMNR != 0 ) - /* - * For LLMNR, an extra MAC-address must be configured to - * be able to receive the multicast messages. - */ - static void prvMACAddressConfig(ETH_HandleTypeDef *heth, uint32_t ulIndex, uint8_t *Addr); -#endif - -/* - * Check if a given packet should be accepted. - */ -static BaseType_t xMayAcceptPacket( uint8_t *pcBuffer ); - -/* - * Initialise the TX descriptors. - */ -static void prvDMATxDescListInit( void ); - -/* - * Initialise the RX descriptors. - */ -static void prvDMARxDescListInit( void ); - -/* After packets have been sent, the network -buffers will be released. */ -static void vClearTXBuffers( 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; - -#if( ipconfigUSE_LLMNR == 1 ) - static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; -#endif - -static EthernetPhy_t xPhyObject; - -/* Ethernet handle. */ -static ETH_HandleTypeDef xETH; - -/* xTXDescriptorSemaphore is a counting semaphore with -a maximum count of ETH_TXBUFNB, which is the number of -DMA TX descriptors. */ -static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; - -/* - * Note: it is adviced to define both - * - * #define ipconfigZERO_COPY_RX_DRIVER 1 - * #define ipconfigZERO_COPY_TX_DRIVER 1 - * - * The method using memcpy is slower and probaly uses more RAM memory. - * The possibility is left in the code just for comparison. - * - * It is adviced to define ETH_TXBUFNB at least 4. Note that no - * TX buffers are allocated in a zero-copy driver. - */ -/* MAC buffers: ---------------------------------------------------------*/ - -/* Put the DMA descriptors in '.first_data'. -This is important for STM32F7, which has an L1 data cache. -The first 64KB of the SRAM is not cached. */ - -/* Ethernet Rx MA Descriptor */ -__attribute__ ((aligned (32))) -__attribute__ ((section(".first_data"))) - ETH_DMADescTypeDef DMARxDscrTab[ ETH_RXBUFNB ]; - -#if( ipconfigZERO_COPY_RX_DRIVER == 0 ) - /* Ethernet Receive Buffer */ - __ALIGN_BEGIN uint8_t Rx_Buff[ ETH_RXBUFNB ][ ETH_RX_BUF_SIZE ] __ALIGN_END; -#endif - -/* Ethernet Tx DMA Descriptor */ -__attribute__ ((aligned (32))) -__attribute__ ((section(".first_data"))) - ETH_DMADescTypeDef DMATxDscrTab[ ETH_TXBUFNB ]; - -#if( ipconfigZERO_COPY_TX_DRIVER == 0 ) - /* Ethernet Transmit Buffer */ - __ALIGN_BEGIN uint8_t Tx_Buff[ ETH_TXBUFNB ][ ETH_TX_BUF_SIZE ] __ALIGN_END; -#endif - -/* DMATxDescToClear points to the next TX DMA descriptor -that must be cleared by vClearTXBuffers(). */ -static __IO ETH_DMADescTypeDef *DMATxDescToClear; - -/* 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. */ -static TaskHandle_t xEMACTaskHandle = 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 -}; - -/*-----------------------------------------------------------*/ - -void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef *heth ) -{ -BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - ( void ) heth; - - /* Ethernet RX-Complete callback function, elsewhere declared as weak. */ - ulISREvents |= EMAC_IF_RX_EVENT; - /* Wakeup the prvEMACHandlerTask. */ - if( xEMACTaskHandle != NULL ) - { - vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); - portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); - } -} -/*-----------------------------------------------------------*/ - -void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef *heth ) -{ -BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - ( void ) heth; - - /* This call-back is only useful in case packets are being sent - zero-copy. Once they're sent, the buffers will be released - by the function vClearTXBuffers(). */ - ulISREvents |= EMAC_IF_TX_EVENT; - /* Wakeup the prvEMACHandlerTask. */ - if( xEMACTaskHandle != NULL ) - { - vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); - portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); - } -} -/*-----------------------------------------------------------*/ - -static void vClearTXBuffers() -{ -__IO ETH_DMADescTypeDef *txLastDescriptor = xETH.TxDesc; -size_t uxCount = ( ( UBaseType_t ) ETH_TXBUFNB ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); -#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) - NetworkBufferDescriptor_t *pxNetworkBuffer; - uint8_t *ucPayLoad; -#endif - - /* This function is called after a TX-completion interrupt. - It will release each Network Buffer used in xNetworkInterfaceOutput(). - 'uxCount' represents the number of descriptors given to DMA for transmission. - After sending a packet, the DMA will clear the 'ETH_DMATXDESC_OWN' bit. */ - while( ( uxCount > 0 ) && ( ( DMATxDescToClear->Status & ETH_DMATXDESC_OWN ) == 0 ) ) - { - if( ( DMATxDescToClear == txLastDescriptor ) && ( uxCount != ETH_TXBUFNB ) ) - { - break; - } - #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) - { - ucPayLoad = ( uint8_t * )DMATxDescToClear->Buffer1Addr; - - if( ucPayLoad != NULL ) - { - pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad ); - if( pxNetworkBuffer != NULL ) - { - vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ) ; - } - DMATxDescToClear->Buffer1Addr = ( uint32_t )0u; - } - } - #endif /* ipconfigZERO_COPY_TX_DRIVER */ - - DMATxDescToClear = ( ETH_DMADescTypeDef * )( DMATxDescToClear->Buffer2NextDescAddr ); - - uxCount--; - /* Tell the counting semaphore that one more TX descriptor is available. */ - xSemaphoreGive( xTXDescriptorSemaphore ); - } -} -/*-----------------------------------------------------------*/ - -BaseType_t xNetworkInterfaceInitialise( void ) -{ -HAL_StatusTypeDef hal_eth_init_status; -BaseType_t xResult; - - if( xMacInitStatus == eMACInit ) - { - xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ETH_TXBUFNB, ( UBaseType_t ) ETH_TXBUFNB ); - if( xTXDescriptorSemaphore == NULL ) - { - xMacInitStatus = eMACFailed; - } - else - { - /* Initialise ETH */ - - xETH.Instance = ETH; - xETH.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; - xETH.Init.Speed = ETH_SPEED_100M; - xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX; - /* Value of PhyAddress doesn't matter, will be probed for. */ - xETH.Init.PhyAddress = 0; - - xETH.Init.MACAddr = ( uint8_t * ) FreeRTOS_GetMACAddress(); - xETH.Init.RxMode = ETH_RXINTERRUPT_MODE; - - #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) - { - /* using the ETH_CHECKSUM_BY_HARDWARE option: - both the IP and the protocol checksums will be calculated - by the peripheral. */ - xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; - } - #else - { - xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE; - } - #endif - - #if( ipconfigUSE_RMII != 0 ) - { - xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; - } - #else - { - xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII; - } - #endif /* ipconfigUSE_RMII */ - - hal_eth_init_status = HAL_ETH_Init( &xETH ); - - /* Only for inspection by debugger. */ - ( void ) hal_eth_init_status; - - /* Set the TxDesc and RxDesc pointers. */ - xETH.TxDesc = DMATxDscrTab; - xETH.RxDesc = DMARxDscrTab; - - /* Make sure that all unused fields are cleared. */ - memset( &DMATxDscrTab, '\0', sizeof( DMATxDscrTab ) ); - memset( &DMARxDscrTab, '\0', sizeof( DMARxDscrTab ) ); - - /* Initialize Tx Descriptors list: Chain Mode */ - DMATxDescToClear = DMATxDscrTab; - - /* Initialise TX-descriptors. */ - prvDMATxDescListInit(); - - /* Initialise RX-descriptors. */ - prvDMARxDescListInit(); - - #if( ipconfigUSE_LLMNR != 0 ) - { - /* Program the LLMNR address at index 1. */ - prvMACAddressConfig( &xETH, ETH_MAC_ADDRESS1, ( uint8_t *) xLLMNR_MACAddress ); - } - #endif - - /* Force a negotiation with the Switch or Router and wait for LS. */ - prvEthernetUpdateConfig( pdTRUE ); - - /* The deferred interrupt handler task is created at the highest - possible priority to ensure the interrupt handler can return directly - to it. The task's handle is stored in xEMACTaskHandle so interrupts can - notify the task when there is something to process. */ - if( xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle ) == pdPASS ) - { - /* The xTXDescriptorSemaphore and the task are created successfully. */ - xMacInitStatus = eMACPass; - } - else - { - xMacInitStatus = eMACFailed; - } - } - } /* if( xEMACTaskHandle == NULL ) */ - - if( xMacInitStatus != eMACPass ) - { - /* EMAC initialisation failed, return pdFAIL. */ - xResult = pdFAIL; - } - else - { - if( xPhyObject.ulLinkStatusMask != 0uL ) - { - xETH.Instance->DMAIER |= ETH_DMA_ALL_INTS; - xResult = pdPASS; - FreeRTOS_printf( ( "Link Status is high\n" ) ) ; - } - else - { - /* For now pdFAIL will be returned. But prvEMACHandlerTask() is running - and it will keep on checking the PHY and set 'ulLinkStatusMask' when necessary. */ - xResult = pdFAIL; - } - } - /* When returning non-zero, the stack will become active and - start DHCP (in configured) */ - return xResult; -} -/*-----------------------------------------------------------*/ - -static void prvDMATxDescListInit() -{ -ETH_DMADescTypeDef *pxDMADescriptor; -BaseType_t xIndex; - - /* Get the pointer on the first member of the descriptor list */ - pxDMADescriptor = DMATxDscrTab; - - /* Fill each DMA descriptor with the right values */ - for( xIndex = 0; xIndex < ETH_TXBUFNB; xIndex++, pxDMADescriptor++ ) - { - /* Set Second Address Chained bit */ - pxDMADescriptor->Status = ETH_DMATXDESC_TCH; - - #if( ipconfigZERO_COPY_TX_DRIVER == 0 ) - { - /* Set Buffer1 address pointer */ - pxDMADescriptor->Buffer1Addr = ( uint32_t )( Tx_Buff[ xIndex ] ); - } - #endif - - if( xETH.Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE ) - { - /* Set the DMA Tx descriptors checksum insertion for TCP, UDP, and ICMP */ - pxDMADescriptor->Status |= ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL; - } - else - { - pxDMADescriptor->Status &= ~( ( uint32_t ) ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL ); - } - - /* Initialize the next descriptor with the Next Descriptor Polling Enable */ - if( xIndex < ETH_TXBUFNB - 1 ) - { - /* Set next descriptor address register with next descriptor base address */ - pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) ( pxDMADescriptor + 1 ); - } - else - { - /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ - pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMATxDscrTab; - } - } - - /* Set Transmit Descriptor List Address Register */ - xETH.Instance->DMATDLAR = ( uint32_t ) DMATxDscrTab; -} -/*-----------------------------------------------------------*/ - -static void prvDMARxDescListInit() -{ -ETH_DMADescTypeDef *pxDMADescriptor; -BaseType_t xIndex; - /* - * RX-descriptors. - */ - - /* Get the pointer on the first member of the descriptor list */ - pxDMADescriptor = DMARxDscrTab; - - /* Fill each DMA descriptor with the right values */ - for( xIndex = 0; xIndex < ETH_RXBUFNB; xIndex++, pxDMADescriptor++ ) - { - - /* Set Buffer1 size and Second Address Chained bit */ - pxDMADescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | (uint32_t)ETH_RX_BUF_SIZE; - - #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) - { - /* Set Buffer1 address pointer */ - NetworkBufferDescriptor_t *pxBuffer; - - pxBuffer = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, 100ul ); - /* If the assert below fails, make sure that there are at least 'ETH_RXBUFNB' - Network Buffers available during start-up ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ) */ - configASSERT( pxBuffer != NULL ); - if( pxBuffer != NULL ) - { - pxDMADescriptor->Buffer1Addr = (uint32_t)pxBuffer->pucEthernetBuffer; - pxDMADescriptor->Status = ETH_DMARXDESC_OWN; - } - } - #else - { - /* Set Buffer1 address pointer */ - pxDMADescriptor->Buffer1Addr = ( uint32_t )( Rx_Buff[ xIndex ] ); - /* Set Own bit of the Rx descriptor Status */ - pxDMADescriptor->Status = ETH_DMARXDESC_OWN; - } - #endif - - /* Initialize the next descriptor with the Next Descriptor Polling Enable */ - if( xIndex < ETH_RXBUFNB - 1 ) - { - /* Set next descriptor address register with next descriptor base address */ - pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t )( pxDMADescriptor + 1 ); - } - else - { - /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ - pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMARxDscrTab; - } - - } - /* Set Receive Descriptor List Address Register */ - xETH.Instance->DMARDLAR = ( uint32_t ) DMARxDscrTab; -} -/*-----------------------------------------------------------*/ - -static void prvMACAddressConfig(ETH_HandleTypeDef *heth, uint32_t ulIndex, uint8_t *Addr) -{ -uint32_t ulTempReg; - - ( void ) heth; - - /* Calculate the selected MAC address high register. */ - ulTempReg = 0x80000000ul | ( ( uint32_t ) Addr[ 5 ] << 8 ) | ( uint32_t ) Addr[ 4 ]; - - /* Load the selected MAC address high register. */ - ( *(__IO uint32_t *)( ( uint32_t ) ( ETH_MAC_ADDR_HBASE + ulIndex ) ) ) = ulTempReg; - - /* Calculate the selected MAC address low register. */ - ulTempReg = ( ( uint32_t ) Addr[ 3 ] << 24 ) | ( ( uint32_t ) Addr[ 2 ] << 16 ) | ( ( uint32_t ) Addr[ 1 ] << 8 ) | Addr[ 0 ]; - - /* Load the selected MAC address low register */ - ( *(__IO uint32_t *) ( ( uint32_t ) ( ETH_MAC_ADDR_LBASE + ulIndex ) ) ) = ulTempReg; -} -/*-----------------------------------------------------------*/ - -BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend ) -{ -BaseType_t xReturn = pdFAIL; -uint32_t ulTransmitSize = 0; -__IO ETH_DMADescTypeDef *pxDmaTxDesc; -/* Do not wait too long for a free TX DMA buffer. */ -const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u ); - - /* Open a do {} while ( 0 ) loop to be able to call break. */ - do - { - if( xCheckLoopback( pxDescriptor, bReleaseAfterSend ) != 0 ) - { - /* The packet has been sent back to the IP-task. - The IP-task will further handle it. - Do not release the descriptor. */ - bReleaseAfterSend = pdFALSE; - break; - } - #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 == ( uint8_t ) ipPROTOCOL_ICMP ) - { - pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t )0u; - } - } - #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */ - if( xPhyObject.ulLinkStatusMask != 0 ) - { - if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) - { - /* Time-out waiting for a free TX descriptor. */ - break; - } - - /* This function does the actual transmission of the packet. The packet is - contained in 'pxDescriptor' that is passed to the function. */ - pxDmaTxDesc = xETH.TxDesc; - - /* Is this buffer available? */ - configASSERT ( ( pxDmaTxDesc->Status & ETH_DMATXDESC_OWN ) == 0 ); - - { - /* Is this buffer available? */ - /* Get bytes in current buffer. */ - ulTransmitSize = pxDescriptor->xDataLength; - - if( ulTransmitSize > ETH_TX_BUF_SIZE ) - { - ulTransmitSize = ETH_TX_BUF_SIZE; - } - - #if( ipconfigZERO_COPY_TX_DRIVER == 0 ) - { - /* Copy the bytes. */ - memcpy( ( void * ) pxDmaTxDesc->Buffer1Addr, pxDescriptor->pucEthernetBuffer, ulTransmitSize ); - } - #else - { - configASSERT( bReleaseAfterSend != 0 ); - - /* Move the buffer. */ - pxDmaTxDesc->Buffer1Addr = ( uint32_t )pxDescriptor->pucEthernetBuffer; - /* The Network Buffer has been passed to DMA, no need to release it. */ - bReleaseAfterSend = pdFALSE_UNSIGNED; - } - #endif /* ipconfigZERO_COPY_TX_DRIVER */ - - /* Ask to set the IPv4 checksum. - Also need an Interrupt on Completion so that 'vClearTXBuffers()' will be called.. */ - #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) - { - pxDmaTxDesc->Status |= ETH_DMATXDESC_CIC_TCPUDPICMP_FULL | ETH_DMATXDESC_IC; - } - #else - { - pxDmaTxDesc->Status &= ~( ( uint32_t ) ETH_DMATXDESC_CIC ); - pxDmaTxDesc->Status |= ETH_DMATXDESC_IC; - } - #endif - - - /* Prepare transmit descriptors to give to DMA. */ - - /* Set LAST and FIRST segment */ - pxDmaTxDesc->Status |= ETH_DMATXDESC_FS | ETH_DMATXDESC_LS; - /* Set frame size */ - pxDmaTxDesc->ControlBufferSize = ( ulTransmitSize & ETH_DMATXDESC_TBS1 ); - - #if( NETWORK_BUFFERS_CACHED != 0 ) - { - BaseType_t xlength = CACHE_LINE_SIZE * ( ( ulTransmitSize + NETWORK_BUFFER_HEADER_SIZE + CACHE_LINE_SIZE - 1 ) / CACHE_LINE_SIZE ); - uint32_t *pulBuffer = ( uint32_t )( pxDescriptor->pucEthernetBuffer - NETWORK_BUFFER_HEADER_SIZE ); - cache_clean_invalidate_by_addr( pulBuffer, xlength ); - } - #endif - - /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ - pxDmaTxDesc->Status |= ETH_DMATXDESC_OWN; - - /* Point to next descriptor */ - xETH.TxDesc = ( ETH_DMADescTypeDef * ) ( xETH.TxDesc->Buffer2NextDescAddr ); - /* Ensure completion of memory access */ - __DSB(); - /* Resume DMA transmission*/ - xETH.Instance->DMATPDR = 0; - iptraceNETWORK_INTERFACE_TRANSMIT(); - xReturn = pdPASS; - } - } - else - { - /* The PHY has no Link Status, packet shall be dropped. */ - } - } while( 0 ); - /* The buffer has been sent so can be released. */ - if( bReleaseAfterSend != pdFALSE ) - { - vReleaseNetworkBufferAndDescriptor( pxDescriptor ); - } - - return xReturn; -} -/*-----------------------------------------------------------*/ - -static BaseType_t xMayAcceptPacket( uint8_t *pcBuffer ) -{ -const ProtocolPacket_t *pxProtPacket = ( const ProtocolPacket_t * )pcBuffer; - - switch( pxProtPacket->xTCPPacket.xEthernetHeader.usFrameType ) - { - case ipARP_FRAME_TYPE: - /* Check it later. */ - return pdTRUE; - case ipIPv4_FRAME_TYPE: - /* Check it here. */ - break; - default: - /* Refuse the packet. */ - return pdFALSE; - } - - #if( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) - { - const IPHeader_t *pxIPHeader = &(pxProtPacket->xTCPPacket.xIPHeader); - uint32_t ulDestinationIPAddress; - - /* Ensure that the incoming packet is not fragmented (only outgoing packets - * can be fragmented) as these are the only handled IP frames currently. */ - if( ( pxIPHeader->usFragmentOffset & FreeRTOS_ntohs( ipFRAGMENT_OFFSET_BIT_MASK ) ) != 0U ) - { - return pdFALSE; - } - /* HT: Might want to make the following configurable because - * most IP messages have a standard length of 20 bytes */ - - /* 0x45 means: IPv4 with an IP header of 5 x 4 = 20 bytes - * 0x47 means: IPv4 with an IP header of 7 x 4 = 28 bytes */ - if( pxIPHeader->ucVersionHeaderLength < 0x45 || pxIPHeader->ucVersionHeaderLength > 0x4F ) - { - return pdFALSE; - } - - ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress; - /* Is the packet for this node? */ - if( ( ulDestinationIPAddress != *ipLOCAL_IP_ADDRESS_POINTER ) && - /* Is it a broadcast address x.x.x.255 ? */ - ( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xff ) != 0xff ) && - #if( ipconfigUSE_LLMNR == 1 ) - ( ulDestinationIPAddress != ipLLMNR_IP_ADDR ) && - #endif - ( *ipLOCAL_IP_ADDRESS_POINTER != 0 ) ) { - FreeRTOS_printf( ( "Drop IP %lxip\n", FreeRTOS_ntohl( ulDestinationIPAddress ) ) ); - return pdFALSE; - } - - if( pxIPHeader->ucProtocol == ipPROTOCOL_UDP ) - { - uint16_t usSourcePort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usSourcePort ); - uint16_t usDestinationPort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort ); - - if( ( xPortHasUDPSocket( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort ) == pdFALSE ) - #if ipconfigUSE_LLMNR == 1 - && ( usDestinationPort != ipLLMNR_PORT ) - && ( usSourcePort != ipLLMNR_PORT ) - #endif - #if ipconfigUSE_NBNS == 1 - && ( usDestinationPort != ipNBNS_PORT ) - && ( usSourcePort != ipNBNS_PORT ) - #endif - #if ipconfigUSE_DNS == 1 - && ( usSourcePort != ipDNS_PORT ) - #endif - ) { - /* Drop this packet, not for this device. */ - /* FreeRTOS_printf( ( "Drop: UDP port %d -> %d\n", usSourcePort, usDestinationPort ) ); */ - return pdFALSE; - } - } - } - #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ - return pdTRUE; -} -/*-----------------------------------------------------------*/ - -static void prvPassEthMessages( NetworkBufferDescriptor_t *pxDescriptor ) -{ -IPStackEvent_t xRxEvent; - - xRxEvent.eEventType = eNetworkRxEvent; - xRxEvent.pvData = ( void * ) pxDescriptor; - - if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 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. */ - #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) - { - do - { - NetworkBufferDescriptor_t *pxNext = pxDescriptor->pxNextBuffer; - vReleaseNetworkBufferAndDescriptor( pxDescriptor ); - pxDescriptor = pxNext; - } while( pxDescriptor != NULL ); - } - #else - { - vReleaseNetworkBufferAndDescriptor( pxDescriptor ); - } - #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ - iptraceETHERNET_RX_EVENT_LOST(); - FreeRTOS_printf( ( "prvPassEthMessages: Can not queue return packet!\n" ) ); - } - else - { - iptraceNETWORK_INTERFACE_RECEIVE(); - } -} - -static BaseType_t prvNetworkInterfaceInput( void ) -{ -NetworkBufferDescriptor_t *pxCurDescriptor; -NetworkBufferDescriptor_t *pxNewDescriptor = NULL; -#if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) - NetworkBufferDescriptor_t *pxFirstDescriptor = NULL; - NetworkBufferDescriptor_t *pxLastDescriptor = NULL; -#endif -BaseType_t xReceivedLength = 0; -__IO ETH_DMADescTypeDef *pxDMARxDescriptor; -const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( niDESCRIPTOR_WAIT_TIME_MS ); -uint8_t *pucBuffer; - - pxDMARxDescriptor = xETH.RxDesc; - - while( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_OWN ) == 0u ) - { - BaseType_t xAccepted = pdTRUE; - /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ - xReceivedLength = ( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_FL ) >> ETH_DMARXDESC_FRAMELENGTHSHIFT ) - 4; - - pucBuffer = (uint8_t *) pxDMARxDescriptor->Buffer1Addr; - - /* Update the ETHERNET DMA global Rx descriptor with next Rx descriptor */ - /* Chained Mode */ - /* Selects the next DMA Rx descriptor list for next buffer to read */ - xETH.RxDesc = ( ETH_DMADescTypeDef* )pxDMARxDescriptor->Buffer2NextDescAddr; - - /* In order to make the code easier and faster, only packets in a single buffer - will be accepted. This can be done by making the buffers large enough to - hold a complete Ethernet packet (1536 bytes). - Therefore, two sanity checks: */ - configASSERT( xReceivedLength <= ETH_RX_BUF_SIZE ); - - if( ( pxDMARxDescriptor->Status & ( ETH_DMARXDESC_CE | ETH_DMARXDESC_IPV4HCE | ETH_DMARXDESC_FT ) ) != ETH_DMARXDESC_FT ) - { - /* Not an Ethernet frame-type or a checmsum error. */ - xAccepted = pdFALSE; - } - else - { - /* See if this packet must be handled. */ - xAccepted = xMayAcceptPacket( pucBuffer ); - } - - if( xAccepted != pdFALSE ) - { - /* The packet wil be accepted, but check first if a new Network Buffer can - be obtained. If not, the packet will still be dropped. */ - pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, xDescriptorWaitTime ); - - if( pxNewDescriptor == NULL ) - { - /* A new descriptor can not be allocated now. This packet will be dropped. */ - xAccepted = pdFALSE; - } - } - #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) - { - /* Find out which Network Buffer was originally passed to the descriptor. */ - pxCurDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer ); - configASSERT( pxCurDescriptor != NULL ); - } - #else - { - /* In this mode, the two descriptors are the same. */ - pxCurDescriptor = pxNewDescriptor; - if( pxNewDescriptor != NULL ) - { - /* The packet is acepted and a new Network Buffer was created, - copy data to the Network Bufffer. */ - memcpy( pxNewDescriptor->pucEthernetBuffer, pucBuffer, xReceivedLength ); - } - } - #endif - - if( xAccepted != pdFALSE ) - { - pxCurDescriptor->xDataLength = xReceivedLength; - #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) - { - pxCurDescriptor->pxNextBuffer = NULL; - - if( pxFirstDescriptor == NULL ) - { - // Becomes the first message - pxFirstDescriptor = pxCurDescriptor; - } - else if( pxLastDescriptor != NULL ) - { - // Add to the tail - pxLastDescriptor->pxNextBuffer = pxCurDescriptor; - } - - pxLastDescriptor = pxCurDescriptor; - } - #else - { - prvPassEthMessages( pxCurDescriptor ); - } - #endif - } - - /* Release descriptors to DMA */ - #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) - { - /* Set Buffer1 address pointer */ - if( pxNewDescriptor != NULL ) - { - pxDMARxDescriptor->Buffer1Addr = (uint32_t)pxNewDescriptor->pucEthernetBuffer; - } - else - { - /* The packet was dropped and the same Network - Buffer will be used to receive a new packet. */ - } - } - #endif /* ipconfigZERO_COPY_RX_DRIVER */ - - /* Set Buffer1 size and Second Address Chained bit */ - pxDMARxDescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | (uint32_t)ETH_RX_BUF_SIZE; - pxDMARxDescriptor->Status = ETH_DMARXDESC_OWN; - - /* Ensure completion of memory access */ - __DSB(); - /* When Rx Buffer unavailable flag is set clear it and resume - reception. */ - if( ( xETH.Instance->DMASR & ETH_DMASR_RBUS ) != 0 ) - { - /* Clear RBUS ETHERNET DMA flag. */ - xETH.Instance->DMASR = ETH_DMASR_RBUS; - - /* Resume DMA reception. */ - xETH.Instance->DMARPDR = 0; - } - pxDMARxDescriptor = xETH.RxDesc; - } - - #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) - { - if( pxFirstDescriptor != NULL ) - { - prvPassEthMessages( pxFirstDescriptor ); - } - } - #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ - - return ( xReceivedLength > 0 ); -} -/*-----------------------------------------------------------*/ - - -BaseType_t xSTM32_PhyRead( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue ) -{ -uint16_t usPrevAddress = xETH.Init.PhyAddress; -BaseType_t xResult; -HAL_StatusTypeDef xHALResult; - - xETH.Init.PhyAddress = xAddress; - xHALResult = HAL_ETH_ReadPHYRegister( &xETH, ( uint16_t )xRegister, pulValue ); - xETH.Init.PhyAddress = usPrevAddress; - - if( xHALResult == HAL_OK ) - { - xResult = 0; - } - else - { - xResult = -1; - } - return xResult; -} -/*-----------------------------------------------------------*/ - -BaseType_t xSTM32_PhyWrite( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue ) -{ -uint16_t usPrevAddress = xETH.Init.PhyAddress; -BaseType_t xResult; -HAL_StatusTypeDef xHALResult; - - xETH.Init.PhyAddress = xAddress; - xHALResult = HAL_ETH_WritePHYRegister( &xETH, ( uint16_t )xRegister, ulValue ); - xETH.Init.PhyAddress = usPrevAddress; - - if( xHALResult == HAL_OK ) - { - xResult = 0; - } - else - { - xResult = -1; - } - return xResult; -} -/*-----------------------------------------------------------*/ - -void vMACBProbePhy( void ) -{ - vPhyInitialise( &xPhyObject, xSTM32_PhyRead, xSTM32_PhyWrite ); - xPhyDiscover( &xPhyObject ); - xPhyConfigure( &xPhyObject, &xPHYProperties ); -} -/*-----------------------------------------------------------*/ - -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 ) ) - { - /* Restart the auto-negotiation. */ - if( xETH.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE ) - { - xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) ); - - /* Configure the MAC with the Duplex Mode fixed by the - auto-negotiation process. */ - if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL ) - { - xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX; - } - else - { - xETH.Init.DuplexMode = ETH_MODE_HALFDUPLEX; - } - - /* Configure the MAC with the speed fixed by the - auto-negotiation process. */ - if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 ) - { - xETH.Init.Speed = ETH_SPEED_10M; - } - else - { - xETH.Init.Speed = ETH_SPEED_100M; - } - } - else /* AutoNegotiation Disable */ - { - /* Check parameters */ - assert_param( IS_ETH_SPEED( xETH.Init.Speed ) ); - assert_param( IS_ETH_DUPLEX_MODE( xETH.Init.DuplexMode ) ); - - if( xETH.Init.DuplexMode == ETH_MODE_FULLDUPLEX ) - { - xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_HALF; - } - else - { - xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_FULL; - } - - if( xETH.Init.Speed == ETH_SPEED_10M ) - { - xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_10; - } - else - { - xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_100; - } - - xPhyObject.xPhyPreferences.ucMDI_X = PHY_MDIX_AUTO; - - /* Use predefined (fixed) configuration. */ - xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) ); - } - - /* ETHERNET MAC Re-Configuration */ - HAL_ETH_ConfigMAC( &xETH, (ETH_MACInitTypeDef *) NULL); - - /* Restart MAC interface */ - HAL_ETH_Start( &xETH); - } - else - { - /* Stop MAC interface */ - HAL_ETH_Stop( &xETH ); - } -} -/*-----------------------------------------------------------*/ - -BaseType_t xGetPhyLinkStatus( void ) -{ -BaseType_t xReturn; - - if( xPhyObject.ulLinkStatusMask != 0 ) - { - xReturn = pdPASS; - } - else - { - xReturn = pdFAIL; - } - - return xReturn; -} -/*-----------------------------------------------------------*/ - -/* Uncomment this in case BufferAllocation_1.c is used. */ - -void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) -{ -static __attribute__ ((section(".first_data"))) uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * ETH_MAX_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) ); -uint8_t *ucRAMBuffer = ucNetworkPackets; -uint32_t ul; - - for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ ) - { - pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING; - *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) ); - ucRAMBuffer += ETH_MAX_PACKET_SIZE; - } -} -/*-----------------------------------------------------------*/ - -static void prvEMACHandlerTask( void *pvParameters ) -{ -UBaseType_t uxLastMinBufferCount = 0; -#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) -UBaseType_t uxLastMinQueueSpace = 0; -#endif -UBaseType_t uxCurrentCount; -BaseType_t xResult; -const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); - - /* Remove compiler warnings about unused parameters. */ - ( void ) pvParameters; - - for( ;; ) - { - xResult = 0; - 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 ) - { - static UBaseType_t uxLowestSemCount = ( UBaseType_t ) ETH_TXBUFNB - 1; - - uxCurrentCount = uxSemaphoreGetCount( xTXDescriptorSemaphore ); - if( uxLowestSemCount > uxCurrentCount ) - { - uxLowestSemCount = uxCurrentCount; - FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) ); - } - - } - - #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; - - xResult = prvNetworkInterfaceInput(); - } - - if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) - { - /* Code to release TX buffers if zero-copy is used. */ - ulISREvents &= ~EMAC_IF_TX_EVENT; - /* Check if DMA packets have been delivered. */ - vClearTXBuffers(); - } - - if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 ) - { - /* Future extension: logging about errors that occurred. */ - ulISREvents &= ~EMAC_IF_ERR_EVENT; - } - if( xPhyCheckLinkStatus( &xPhyObject, xResult ) != 0 ) - { - /* Something has changed to a Link Status, need re-check. */ - prvEthernetUpdateConfig( pdFALSE ); - } - } -} -/*-----------------------------------------------------------*/ - -void ETH_IRQHandler( void ) -{ - HAL_ETH_IRQHandler( &xETH ); -} - +/* + * Some constants, hardware definitions and comments taken from ST's HAL driver + * library, COPYRIGHT(c) 2015 STMicroelectronics. + */ + +/* +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 "FreeRTOS_DNS.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +/* ST includes. */ +#include "stm32f4xx_hal.h" + +#ifndef BMSR_LINK_STATUS + #define BMSR_LINK_STATUS 0x0004UL +#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 ETH_DMA_ALL_INTS \ + ( ETH_DMA_IT_TST | ETH_DMA_IT_PMT | ETH_DMA_IT_MMC | ETH_DMA_IT_NIS | ETH_DMA_IT_ER | \ + ETH_DMA_IT_FBE | ETH_DMA_IT_RWT | ETH_DMA_IT_RPS | ETH_DMA_IT_RBU | ETH_DMA_IT_R | \ + ETH_DMA_IT_TU | ETH_DMA_IT_RO | ETH_DMA_IT_TJT | ETH_DMA_IT_TPS | ETH_DMA_IT_T ) + +/* Naming and numbering of PHY registers. */ +#define PHY_REG_00_BMCR 0x00 /* Basic Mode Control Register */ +#define PHY_REG_01_BMSR 0x01 /* Basic Mode Status Register */ +#define PHY_REG_02_PHYSID1 0x02 /* PHYS ID 1 */ +#define PHY_REG_03_PHYSID2 0x03 /* PHYS ID 2 */ +#define PHY_REG_04_ADVERTISE 0x04 /* Advertisement control reg */ + +#define PHY_ID_LAN8720 0x0007c0f0 +#define PHY_ID_DP83848I 0x20005C90 + +#ifndef USE_STM324xG_EVAL + #define USE_STM324xG_EVAL 1 +#endif + +#if( USE_STM324xG_EVAL == 0 ) + #define EXPECTED_PHY_ID PHY_ID_LAN8720 + #define PHY_REG_1F_PHYSPCS 0x1F /* 31 RW PHY Special Control Status */ + /* Use 3 bits in register 31 */ + #define PHYSPCS_SPEED_MASK 0x0C + #define PHYSPCS_SPEED_10 0x04 + #define PHYSPCS_SPEED_100 0x08 + #define PHYSPCS_FULL_DUPLEX 0x10 +#else + #define EXPECTED_PHY_ID PHY_ID_DP83848I + + #define PHY_REG_10_PHY_SR 0x10 /* PHY status register Offset */ + #define PHY_REG_19_PHYCR 0x19 /* 25 RW PHY Control Register */ +#endif + +/* Some defines used internally here to indicate preferences about speed, MDIX +(wired direct or crossed), and duplex (half or full). */ +#define PHY_SPEED_10 1 +#define PHY_SPEED_100 2 +#define PHY_SPEED_AUTO (PHY_SPEED_10|PHY_SPEED_100) + +#define PHY_MDIX_DIRECT 1 +#define PHY_MDIX_CROSSED 2 +#define PHY_MDIX_AUTO (PHY_MDIX_CROSSED|PHY_MDIX_DIRECT) + +#define PHY_DUPLEX_HALF 1 +#define PHY_DUPLEX_FULL 2 +#define PHY_DUPLEX_AUTO (PHY_DUPLEX_FULL|PHY_DUPLEX_HALF) + +#define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020) /*!< Auto-Negotiation process completed */ + +/* + * Description of all capabilities that can be advertised to + * the peer (usually a switch or router). + */ +#define ADVERTISE_CSMA 0x0001 /* Only selector supported. */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex. */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex. */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex. */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex. */ + +#define ADVERTISE_ALL ( ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL) + +/* + * Value for the 'PHY_REG_00_BMCR', the PHY's Basic Mode Control Register + */ +#define BMCR_FULLDPLX 0x0100 /* Full duplex. */ +#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart. */ +#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation. */ +#define BMCR_SPEED100 0x2000 /* Select 100Mbps. */ +#define BMCR_RESET 0x8000 /* Reset the PHY. */ + +#define PHYCR_MDIX_EN 0x8000 /* Enable Auto MDIX. */ +#define PHYCR_MDIX_FORCE 0x4000 /* Force MDIX crossed. */ + +#define ipFRAGMENT_OFFSET_BIT_MASK ( ( uint16_t ) 0x0fff ) /* The bits in the two byte IP header field that make up the fragment offset value. */ + +/* + * Most users will want a PHY that negotiates about + * the connection properties: speed, dmix and duplex. + * On some rare cases, you want to select what is being + * advertised, properties like MDIX and duplex. + */ + +#if !defined( ipconfigETHERNET_AN_ENABLE ) + /* Enable auto-negotiation */ + #define ipconfigETHERNET_AN_ENABLE 1 +#endif + +#if !defined( ipconfigETHERNET_AUTO_CROSS_ENABLE ) + #define ipconfigETHERNET_AUTO_CROSS_ENABLE 1 +#endif + +#if( ipconfigETHERNET_AN_ENABLE == 0 ) + /* + * The following three defines are only used in case there + * is no auto-negotiation. + */ + #if !defined( ipconfigETHERNET_CROSSED_LINK ) + #define ipconfigETHERNET_CROSSED_LINK 1 + #endif + + #if !defined( ipconfigETHERNET_USE_100MB ) + #define ipconfigETHERNET_USE_100MB 1 + #endif + + #if !defined( ipconfigETHERNET_USE_FULL_DUPLEX ) + #define ipconfigETHERNET_USE_FULL_DUPLEX 1 + #endif +#endif /* ipconfigETHERNET_AN_ENABLE == 0 */ + +/* Default the size of the stack used by the EMAC deferred handler task to twice +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 ( 2 * configMINIMAL_STACK_SIZE ) +#endif + +/*-----------------------------------------------------------*/ + +/* + * A deferred interrupt handler task that processes + */ +static void prvEMACHandlerTask( void *pvParameters ); + +/* + * Force a negotiation with the Switch or Router and wait for LS. + */ +static void prvEthernetUpdateConfig( BaseType_t xForce ); + +/* + * See if there is a new packet and forward it to the IP-task. + */ +static BaseType_t prvNetworkInterfaceInput( void ); + +#if( ipconfigUSE_LLMNR != 0 ) + /* + * For LLMNR, an extra MAC-address must be configured to + * be able to receive the multicast messages. + */ + static void prvMACAddressConfig(ETH_HandleTypeDef *heth, uint32_t ulIndex, uint8_t *Addr); +#endif + +/* + * Check if a given packet should be accepted. + */ +static BaseType_t xMayAcceptPacket( uint8_t *pcBuffer ); + +/* + * Initialise the TX descriptors. + */ +static void prvDMATxDescListInit( void ); + +/* + * Initialise the RX descriptors. + */ +static void prvDMARxDescListInit( void ); + +#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + /* After packets have been sent, the network + buffers will be released. */ + static void vClearTXBuffers( void ); +#endif /* ipconfigZERO_COPY_TX_DRIVER */ + +/*-----------------------------------------------------------*/ + +typedef struct _PhyProperties_t +{ + uint8_t speed; + uint8_t mdix; + uint8_t duplex; + uint8_t spare; +} PhyProperties_t; + +/* 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; + +#if( ipconfigUSE_LLMNR == 1 ) + static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; +#endif + +/* Ethernet handle. */ +static ETH_HandleTypeDef xETH; + +#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + /* xTXDescriptorSemaphore is a counting semaphore with + a maximum count of ETH_TXBUFNB, which is the number of + DMA TX descriptors. */ + static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; +#endif /* ipconfigZERO_COPY_TX_DRIVER */ + +/* + * Note: it is adviced to define both + * + * #define ipconfigZERO_COPY_RX_DRIVER 1 + * #define ipconfigZERO_COPY_TX_DRIVER 1 + * + * The method using memcpy is slower and probaly uses more RAM memory. + * The possibility is left in the code just for comparison. + * + * It is adviced to define ETH_TXBUFNB at least 4. Note that no + * TX buffers are allocated in a zero-copy driver. + */ +/* MAC buffers: ---------------------------------------------------------*/ +__ALIGN_BEGIN ETH_DMADescTypeDef DMARxDscrTab[ ETH_RXBUFNB ] __ALIGN_END;/* Ethernet Rx MA Descriptor */ +#if( ipconfigZERO_COPY_RX_DRIVER == 0 ) + __ALIGN_BEGIN uint8_t Rx_Buff[ ETH_RXBUFNB ][ ETH_RX_BUF_SIZE ] __ALIGN_END; /* Ethernet Receive Buffer */ +#endif + +__ALIGN_BEGIN ETH_DMADescTypeDef DMATxDscrTab[ ETH_TXBUFNB ] __ALIGN_END;/* Ethernet Tx DMA Descriptor */ +#if( ipconfigZERO_COPY_TX_DRIVER == 0 ) + __ALIGN_BEGIN uint8_t Tx_Buff[ ETH_TXBUFNB ][ ETH_TX_BUF_SIZE ] __ALIGN_END; /* Ethernet Transmit Buffer */ +#endif + +#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + /* DMATxDescToClear points to the next TX DMA descriptor + that must be cleared by vClearTXBuffers(). */ + static __IO ETH_DMADescTypeDef *DMATxDescToClear; +#endif + +/* Value to be written into the 'Basic mode Control Register'. */ +static uint32_t ulBCRvalue; + +/* Value to be written into the 'Advertisement Control Register'. */ +static uint32_t ulACRValue; + +/* ucMACAddress as it appears in main.c */ +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. */ +static TaskHandle_t xEMACTaskHandle = NULL; + +/* For local use only: describe the PHY's properties: */ +const PhyProperties_t xPHYProperties = +{ + #if( ipconfigETHERNET_AN_ENABLE != 0 ) + .speed = PHY_SPEED_AUTO, + .duplex = PHY_DUPLEX_AUTO, + #else + #if( ipconfigETHERNET_USE_100MB != 0 ) + .speed = PHY_SPEED_100, + #else + .speed = PHY_SPEED_10, + #endif + + #if( ipconfigETHERNET_USE_FULL_DUPLEX != 0 ) + .duplex = PHY_DUPLEX_FULL, + #else + .duplex = PHY_DUPLEX_HALF, + #endif + #endif + + #if( ipconfigETHERNET_AN_ENABLE != 0 ) && ( ipconfigETHERNET_AUTO_CROSS_ENABLE != 0 ) + .mdix = PHY_MDIX_AUTO, + #elif( ipconfigETHERNET_CROSSED_LINK != 0 ) + .mdix = PHY_MDIX_CROSSED, + #else + .mdix = PHY_MDIX_DIRECT, + #endif +}; + +/*-----------------------------------------------------------*/ + +void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef *heth ) +{ +BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + /* Ethernet RX-Complete callback function, elsewhere declared as weak. */ + ulISREvents |= EMAC_IF_RX_EVENT; + /* Wakeup the prvEMACHandlerTask. */ + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + } +} +/*-----------------------------------------------------------*/ + +#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef *heth ) + { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + /* This call-back is only useful in case packets are being sent + zero-copy. Once they're sent, the buffers will be released + by the function vClearTXBuffers(). */ + ulISREvents |= EMAC_IF_TX_EVENT; + /* Wakeup the prvEMACHandlerTask. */ + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + } + + } +#endif /* ipconfigZERO_COPY_TX_DRIVER */ + +/*-----------------------------------------------------------*/ + +#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + static void vClearTXBuffers() + { + __IO ETH_DMADescTypeDef *txLastDescriptor = xETH.TxDesc; + NetworkBufferDescriptor_t *pxNetworkBuffer; + uint8_t *ucPayLoad; + size_t uxCount = ( ( UBaseType_t ) ETH_TXBUFNB ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + /* This function is called after a TX-completion interrupt. + It will release each Network Buffer used in xNetworkInterfaceOutput(). + 'uxCount' represents the number of descriptors given to DMA for transmission. + After sending a packet, the DMA will clear the 'ETH_DMATXDESC_OWN' bit. */ + while( ( uxCount > 0 ) && ( ( DMATxDescToClear->Status & ETH_DMATXDESC_OWN ) == 0 ) ) + { + if( ( DMATxDescToClear == txLastDescriptor ) && ( uxCount != ETH_TXBUFNB ) ) + { + break; + } + + ucPayLoad = ( uint8_t * )DMATxDescToClear->Buffer1Addr; + + if( ucPayLoad != NULL ) + { + pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad ); + if( pxNetworkBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ) ; + } + DMATxDescToClear->Buffer1Addr = ( uint32_t )0u; + } + + DMATxDescToClear = ( ETH_DMADescTypeDef * )( DMATxDescToClear->Buffer2NextDescAddr ); + + uxCount--; + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + } + } +#endif /* ipconfigZERO_COPY_TX_DRIVER */ +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ +HAL_StatusTypeDef hal_eth_init_status; +BaseType_t xResult; + + if( xEMACTaskHandle == NULL ) + { + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + if( xTXDescriptorSemaphore == NULL ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ETH_TXBUFNB, ( UBaseType_t ) ETH_TXBUFNB ); + configASSERT( xTXDescriptorSemaphore ); + } + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + /* Initialise ETH */ + + xETH.Instance = ETH; + xETH.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; + xETH.Init.Speed = ETH_SPEED_100M; + xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX; + xETH.Init.PhyAddress = 1; + + xETH.Init.MACAddr = ( uint8_t *) ucMACAddress; + xETH.Init.RxMode = ETH_RXINTERRUPT_MODE; + + /* using the ETH_CHECKSUM_BY_HARDWARE option: + both the IP and the protocol checksums will be calculated + by the peripheral. */ + xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; + + xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII; + hal_eth_init_status = HAL_ETH_Init( &xETH ); + + /* Only for inspection by debugger. */ + ( void ) hal_eth_init_status; + + /* Set the TxDesc and RxDesc pointers. */ + xETH.TxDesc = DMATxDscrTab; + xETH.RxDesc = DMARxDscrTab; + + /* Make sure that all unused fields are cleared. */ + memset( &DMATxDscrTab, '\0', sizeof( DMATxDscrTab ) ); + memset( &DMARxDscrTab, '\0', sizeof( DMARxDscrTab ) ); + + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Initialize Tx Descriptors list: Chain Mode */ + DMATxDescToClear = DMATxDscrTab; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + /* Initialise TX-descriptors. */ + prvDMATxDescListInit(); + + /* Initialise RX-descriptors. */ + prvDMARxDescListInit(); + + #if( ipconfigUSE_LLMNR != 0 ) + { + /* Program the LLMNR address at index 1. */ + prvMACAddressConfig( &xETH, ETH_MAC_ADDRESS1, ( uint8_t *) xLLMNR_MACAddress ); + } + #endif + + /* Force a negotiation with the Switch or Router and wait for LS. */ + prvEthernetUpdateConfig( pdTRUE ); + + /* The deferred interrupt handler task is created at the highest + possible priority to ensure the interrupt handler can return directly + to it. The task's handle is stored in xEMACTaskHandle so interrupts can + notify the task when there is something to process. */ + xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle ); + } /* if( xEMACTaskHandle == NULL ) */ + + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + xETH.Instance->DMAIER |= ETH_DMA_ALL_INTS; + xResult = pdPASS; + FreeRTOS_printf( ( "Link Status is high\n" ) ) ; + } + else + { + /* For now pdFAIL will be returned. But prvEMACHandlerTask() is running + and it will keep on checking the PHY and set ulPHYLinkStatus when necessary. */ + xResult = pdFAIL; + FreeRTOS_printf( ( "Link Status still low\n" ) ) ; + } + /* When returning non-zero, the stack will become active and + start DHCP (in configured) */ + return xResult; +} +/*-----------------------------------------------------------*/ + +static void prvDMATxDescListInit() +{ +ETH_DMADescTypeDef *pxDMADescriptor; +BaseType_t xIndex; + + /* Get the pointer on the first member of the descriptor list */ + pxDMADescriptor = DMATxDscrTab; + + /* Fill each DMA descriptor with the right values */ + for( xIndex = 0; xIndex < ETH_TXBUFNB; xIndex++, pxDMADescriptor++ ) + { + /* Set Second Address Chained bit */ + pxDMADescriptor->Status = ETH_DMATXDESC_TCH; + + #if( ipconfigZERO_COPY_TX_DRIVER == 0 ) + { + /* Set Buffer1 address pointer */ + pxDMADescriptor->Buffer1Addr = ( uint32_t )( Tx_Buff[ xIndex ] ); + } + #endif + + if( xETH.Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE ) + { + /* Set the DMA Tx descriptors checksum insertion for TCP, UDP, and ICMP */ + pxDMADescriptor->Status |= ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL; + } + + /* Initialize the next descriptor with the Next Descriptor Polling Enable */ + if( xIndex < ETH_TXBUFNB - 1 ) + { + /* Set next descriptor address register with next descriptor base address */ + pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) ( pxDMADescriptor + 1 ); + } + else + { + /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ + pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMATxDscrTab; + } + } + + /* Set Transmit Descriptor List Address Register */ + xETH.Instance->DMATDLAR = ( uint32_t ) DMATxDscrTab; +} +/*-----------------------------------------------------------*/ + +static void prvDMARxDescListInit() +{ +ETH_DMADescTypeDef *pxDMADescriptor; +BaseType_t xIndex; + /* + * RX-descriptors. + */ + + /* Get the pointer on the first member of the descriptor list */ + pxDMADescriptor = DMARxDscrTab; + + /* Fill each DMA descriptor with the right values */ + for( xIndex = 0; xIndex < ETH_RXBUFNB; xIndex++, pxDMADescriptor++ ) + { + + /* Set Buffer1 size and Second Address Chained bit */ + pxDMADescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | (uint32_t)ETH_RX_BUF_SIZE; + + #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + /* Set Buffer1 address pointer */ + NetworkBufferDescriptor_t *pxBuffer; + + pxBuffer = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, 100ul ); + /* If the assert below fails, make sure that there are at least 'ETH_RXBUFNB' + Network Buffers available during start-up ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ) */ + configASSERT( pxBuffer != NULL ); + if( pxBuffer != NULL ) + { + pxDMADescriptor->Buffer1Addr = (uint32_t)pxBuffer->pucEthernetBuffer; + pxDMADescriptor->Status = ETH_DMARXDESC_OWN; + } + } + #else + { + /* Set Buffer1 address pointer */ + pxDMADescriptor->Buffer1Addr = ( uint32_t )( Rx_Buff[ xIndex ] ); + /* Set Own bit of the Rx descriptor Status */ + pxDMADescriptor->Status = ETH_DMARXDESC_OWN; + } + #endif + + /* Initialize the next descriptor with the Next Descriptor Polling Enable */ + if( xIndex < ETH_RXBUFNB - 1 ) + { + /* Set next descriptor address register with next descriptor base address */ + pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t )( pxDMADescriptor + 1 ); + } + else + { + /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ + pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMARxDscrTab; + } + + } + /* Set Receive Descriptor List Address Register */ + xETH.Instance->DMARDLAR = ( uint32_t ) DMARxDscrTab; +} +/*-----------------------------------------------------------*/ + +static void prvMACAddressConfig(ETH_HandleTypeDef *heth, uint32_t ulIndex, uint8_t *Addr) +{ +uint32_t ulTempReg; + + /* Calculate the selected MAC address high register. */ + ulTempReg = 0x80000000ul | ( ( uint32_t ) Addr[ 5 ] << 8 ) | ( uint32_t ) Addr[ 4 ]; + + /* Load the selected MAC address high register. */ + ( *(__IO uint32_t *)( ( uint32_t ) ( ETH_MAC_ADDR_HBASE + ulIndex ) ) ) = ulTempReg; + + /* Calculate the selected MAC address low register. */ + ulTempReg = ( ( uint32_t ) Addr[ 3 ] << 24 ) | ( ( uint32_t ) Addr[ 2 ] << 16 ) | ( ( uint32_t ) Addr[ 1 ] << 8 ) | Addr[ 0 ]; + + /* Load the selected MAC address low register */ + ( *(__IO uint32_t *) ( ( uint32_t ) ( ETH_MAC_ADDR_LBASE + ulIndex ) ) ) = ulTempReg; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend ) +{ +BaseType_t xReturn = pdFAIL; +uint32_t ulTransmitSize = 0; +__IO ETH_DMADescTypeDef *pxDmaTxDesc; +/* Do not wait too long for a free TX DMA buffer. */ +const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u ); + + #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 + + /* Open a do {} while ( 0 ) loop to be able to call break. */ + do + { + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + if( xTXDescriptorSemaphore == NULL ) + { + break; + } + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + /* Time-out waiting for a free TX descriptor. */ + break; + } + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + /* This function does the actual transmission of the packet. The packet is + contained in 'pxDescriptor' that is passed to the function. */ + pxDmaTxDesc = xETH.TxDesc; + + /* Is this buffer available? */ + if( ( pxDmaTxDesc->Status & ETH_DMATXDESC_OWN ) == 0 ) + { + /* Is this buffer available? */ + /* Get bytes in current buffer. */ + ulTransmitSize = pxDescriptor->xDataLength; + + if( ulTransmitSize > ETH_TX_BUF_SIZE ) + { + ulTransmitSize = ETH_TX_BUF_SIZE; + } + + #if( ipconfigZERO_COPY_TX_DRIVER == 0 ) + { + /* Copy the bytes. */ + memcpy( ( void * ) pxDmaTxDesc->Buffer1Addr, pxDescriptor->pucEthernetBuffer, ulTransmitSize ); + pxDmaTxDesc->Status |= ETH_DMATXDESC_CIC_TCPUDPICMP_FULL; + } + #else + { + /* Move the buffer. */ + pxDmaTxDesc->Buffer1Addr = ( uint32_t )pxDescriptor->pucEthernetBuffer; + /* Ask to set the IPv4 checksum. + Also need an Interrupt on Completion so that 'vClearTXBuffers()' will be called.. */ + pxDmaTxDesc->Status |= ETH_DMATXDESC_CIC_TCPUDPICMP_FULL | ETH_DMATXDESC_IC; + /* The Network Buffer has been passed to DMA, no need to release it. */ + bReleaseAfterSend = pdFALSE_UNSIGNED; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + /* Prepare transmit descriptors to give to DMA. */ + + /* Set LAST and FIRST segment */ + pxDmaTxDesc->Status |= ETH_DMATXDESC_FS | ETH_DMATXDESC_LS; + /* Set frame size */ + pxDmaTxDesc->ControlBufferSize = ( ulTransmitSize & ETH_DMATXDESC_TBS1 ); + /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ + pxDmaTxDesc->Status |= ETH_DMATXDESC_OWN; + + /* Point to next descriptor */ + xETH.TxDesc = ( ETH_DMADescTypeDef * ) ( xETH.TxDesc->Buffer2NextDescAddr ); + + /* Resume DMA transmission*/ + xETH.Instance->DMATPDR = 0; + iptraceNETWORK_INTERFACE_TRANSMIT(); + xReturn = pdPASS; + } + } + else + { + /* The PHY has no Link Status, packet shall be dropped. */ + } + } while( 0 ); + /* The buffer has been sent so can be released. */ + if( bReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xMayAcceptPacket( uint8_t *pcBuffer ) +{ +const ProtocolPacket_t *pxProtPacket = ( const ProtocolPacket_t * )pcBuffer; + + switch( pxProtPacket->xTCPPacket.xEthernetHeader.usFrameType ) + { + case ipARP_FRAME_TYPE: + /* Check it later. */ + return pdTRUE; + case ipIPv4_FRAME_TYPE: + /* Check it here. */ + break; + default: + /* Refuse the packet. */ + return pdFALSE; + } + + #if( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) + { + const IPHeader_t *pxIPHeader = &(pxProtPacket->xTCPPacket.xIPHeader); + uint32_t ulDestinationIPAddress; + + /* Ensure that the incoming packet is not fragmented (only outgoing packets + * can be fragmented) as these are the only handled IP frames currently. */ + if( ( pxIPHeader->usFragmentOffset & FreeRTOS_ntohs( ipFRAGMENT_OFFSET_BIT_MASK ) ) != 0U ) + { + return pdFALSE; + } + /* HT: Might want to make the following configurable because + * most IP messages have a standard length of 20 bytes */ + + /* 0x45 means: IPv4 with an IP header of 5 x 4 = 20 bytes + * 0x47 means: IPv4 with an IP header of 7 x 4 = 28 bytes */ + if( pxIPHeader->ucVersionHeaderLength < 0x45 || pxIPHeader->ucVersionHeaderLength > 0x4F ) + { + return pdFALSE; + } + + ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress; + /* Is the packet for this node? */ + if( ( ulDestinationIPAddress != *ipLOCAL_IP_ADDRESS_POINTER ) && + /* Is it a broadcast address x.x.x.255 ? */ + ( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xff ) != 0xff ) && + #if( ipconfigUSE_LLMNR == 1 ) + ( ulDestinationIPAddress != ipLLMNR_IP_ADDR ) && + #endif + ( *ipLOCAL_IP_ADDRESS_POINTER != 0 ) ) { + FreeRTOS_printf( ( "Drop IP %lxip\n", FreeRTOS_ntohl( ulDestinationIPAddress ) ) ); + return pdFALSE; + } + + if( pxIPHeader->ucProtocol == ipPROTOCOL_UDP ) + { + uint16_t port = pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort; + + if( ( xPortHasUDPSocket( port ) == pdFALSE ) + #if ipconfigUSE_LLMNR == 1 + && ( port != FreeRTOS_ntohs( ipLLMNR_PORT ) ) + #endif + #if ipconfigUSE_NBNS == 1 + && ( port != FreeRTOS_ntohs( ipNBNS_PORT ) ) + #endif + #if ipconfigUSE_DNS == 1 + && ( pxProtPacket->xUDPPacket.xUDPHeader.usSourcePort != FreeRTOS_ntohs( ipDNS_PORT ) ) + #endif + ) { + /* Drop this packet, not for this device. */ + return pdFALSE; + } + } + } + #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvNetworkInterfaceInput( void ) +{ +NetworkBufferDescriptor_t *pxCurDescriptor; +NetworkBufferDescriptor_t *pxNewDescriptor = NULL; +BaseType_t xReceivedLength, xAccepted; +__IO ETH_DMADescTypeDef *pxDMARxDescriptor; +xIPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; +const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 ); +uint8_t *pucBuffer; + + pxDMARxDescriptor = xETH.RxDesc; + + if( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_OWN) == 0 ) + { + /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ + xReceivedLength = ( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_FL ) >> ETH_DMARXDESC_FRAMELENGTHSHIFT ) - 4; + + pucBuffer = (uint8_t *) pxDMARxDescriptor->Buffer1Addr; + + /* Update the ETHERNET DMA global Rx descriptor with next Rx descriptor */ + /* Chained Mode */ + /* Selects the next DMA Rx descriptor list for next buffer to read */ + xETH.RxDesc = ( ETH_DMADescTypeDef* )pxDMARxDescriptor->Buffer2NextDescAddr; + } + else + { + xReceivedLength = 0; + } + + /* Obtain the size of the packet and put it into the "usReceivedLength" variable. */ + /* In order to make the code easier and faster, only packets in a single buffer + will be accepted. This can be done by making the buffers large enough to + hold a complete Ethernet packet (1536 bytes). */ + if( xReceivedLength > 0ul && xReceivedLength < ETH_RX_BUF_SIZE ) + { + if( ( pxDMARxDescriptor->Status & ( ETH_DMARXDESC_CE | ETH_DMARXDESC_IPV4HCE | ETH_DMARXDESC_FT ) ) != ETH_DMARXDESC_FT ) + { + /* Not an Ethernet frame-type or a checmsum error. */ + xAccepted = pdFALSE; + } + else + { + /* See if this packet must be handled. */ + xAccepted = xMayAcceptPacket( pucBuffer ); + } + + if( xAccepted != pdFALSE ) + { + /* The packet wil be accepted, but check first if a new Network Buffer can + be obtained. If not, the packet will still be dropped. */ + pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, xDescriptorWaitTime ); + + if( pxNewDescriptor == NULL ) + { + /* A new descriptor can not be allocated now. This packet will be dropped. */ + xAccepted = pdFALSE; + } + } + #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + /* Find out which Network Buffer was originally passed to the descriptor. */ + pxCurDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer ); + configASSERT( pxCurDescriptor != NULL ); + } + #else + { + /* In this mode, the two descriptors are the same. */ + pxCurDescriptor = pxNewDescriptor; + if( pxNewDescriptor != NULL ) + { + /* The packet is acepted and a new Network Buffer was created, + copy data to the Network Bufffer. */ + memcpy( pxNewDescriptor->pucEthernetBuffer, pucBuffer, xReceivedLength ); + } + } + #endif + + if( xAccepted != pdFALSE ) + { + pxCurDescriptor->xDataLength = xReceivedLength; + xRxEvent.pvData = ( void * ) pxCurDescriptor; + + /* Pass the data to the TCP/IP task for processing. */ + if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFALSE ) + { + /* Could not send the descriptor into the TCP/IP stack, it + must be released. */ + vReleaseNetworkBufferAndDescriptor( pxCurDescriptor ); + iptraceETHERNET_RX_EVENT_LOST(); + } + else + { + iptraceNETWORK_INTERFACE_RECEIVE(); + } + } + + /* Release descriptors to DMA */ + #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + /* Set Buffer1 address pointer */ + if( pxNewDescriptor != NULL ) + { + pxDMARxDescriptor->Buffer1Addr = (uint32_t)pxNewDescriptor->pucEthernetBuffer; + } + else + { + /* The packet was dropped and the same Network + Buffer will be used to receive a new packet. */ + } + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + /* Set Buffer1 size and Second Address Chained bit */ + pxDMARxDescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | (uint32_t)ETH_RX_BUF_SIZE; + pxDMARxDescriptor->Status = ETH_DMARXDESC_OWN; + + /* When Rx Buffer unavailable flag is set clear it and resume + reception. */ + if( ( xETH.Instance->DMASR & ETH_DMASR_RBUS ) != 0 ) + { + /* Clear RBUS ETHERNET DMA flag. */ + xETH.Instance->DMASR = ETH_DMASR_RBUS; + + /* Resume DMA reception. */ + xETH.Instance->DMARPDR = 0; + } + } + + return ( xReceivedLength > 0 ); +} +/*-----------------------------------------------------------*/ + +void vMACBProbePhy( void ) +{ +uint32_t ulConfig, ulAdvertise, ulLower, ulUpper, ulMACPhyID, ulValue; +TimeOut_t xPhyTime; +TickType_t xRemTime = 0; +#if( EXPECTED_PHY_ID == PHY_ID_DP83848I ) + uint32_t ulPhyControl; +#endif + + HAL_ETH_ReadPHYRegister(&xETH, PHY_REG_03_PHYSID2, &ulLower); + HAL_ETH_ReadPHYRegister(&xETH, PHY_REG_02_PHYSID1, &ulUpper); + + ulMACPhyID = ( ( ulUpper << 16 ) & 0xFFFF0000 ) | ( ulLower & 0xFFF0 ); + + /* The expected ID for the 'LAN8720' is 0x0007c0f0. */ + /* The expected ID for the 'DP83848I' is 0x20005C90. */ + + FreeRTOS_printf( ( "PHY ID %lX (%s)\n", ulMACPhyID, + ( ulMACPhyID == EXPECTED_PHY_ID ) ? "OK" : "Unknown" ) ); + + /* Remove compiler warning if FreeRTOS_printf() is not defined. */ + ( void ) ulMACPhyID; + + /* Set advertise register. */ + if( ( xPHYProperties.speed == PHY_SPEED_AUTO ) && ( xPHYProperties.duplex == PHY_DUPLEX_AUTO ) ) + { + ulAdvertise = ADVERTISE_CSMA | ADVERTISE_ALL; + /* Reset auto-negotiation capability. */ + } + else + { + ulAdvertise = ADVERTISE_CSMA; + + if( xPHYProperties.speed == PHY_SPEED_AUTO ) + { + if( xPHYProperties.duplex == PHY_DUPLEX_FULL ) + { + ulAdvertise |= ADVERTISE_10FULL | ADVERTISE_100FULL; + } + else + { + ulAdvertise |= ADVERTISE_10HALF | ADVERTISE_100HALF; + } + } + else if( xPHYProperties.duplex == PHY_DUPLEX_AUTO ) + { + if( xPHYProperties.speed == PHY_SPEED_10 ) + { + ulAdvertise |= ADVERTISE_10FULL | ADVERTISE_10HALF; + } + else + { + ulAdvertise |= ADVERTISE_100FULL | ADVERTISE_100HALF; + } + } + else if( xPHYProperties.speed == PHY_SPEED_100 ) + { + if( xPHYProperties.duplex == PHY_DUPLEX_FULL ) + { + ulAdvertise |= ADVERTISE_100FULL; + } + else + { + ulAdvertise |= ADVERTISE_100HALF; + } + } + else + { + if( xPHYProperties.duplex == PHY_DUPLEX_FULL ) + { + ulAdvertise |= ADVERTISE_10FULL; + } + else + { + ulAdvertise |= ADVERTISE_10HALF; + } + } + } + + /* Read Control register. */ + HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_00_BMCR, &ulConfig ); + + HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, ulConfig | BMCR_RESET ); + xRemTime = ( TickType_t ) pdMS_TO_TICKS( 1000UL ); + vTaskSetTimeOutState( &xPhyTime ); + + for( ; ; ) + { + HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_00_BMCR, &ulValue ); + if( ( ulValue & BMCR_RESET ) == 0 ) + { + FreeRTOS_printf( ( "BMCR_RESET ready\n" ) ); + break; + } + if( xTaskCheckForTimeOut( &xPhyTime, &xRemTime ) != pdFALSE ) + { + FreeRTOS_printf( ( "BMCR_RESET timed out\n" ) ); + break; + } + } + HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, ulConfig & ~BMCR_RESET ); + + vTaskDelay( pdMS_TO_TICKS( 50ul ) ); + + /* Write advertise register. */ + HAL_ETH_WritePHYRegister( &xETH, PHY_REG_04_ADVERTISE, ulAdvertise ); + + /* + AN_EN AN1 AN0 Forced Mode + 0 0 0 10BASE-T, Half-Duplex + 0 0 1 10BASE-T, Full-Duplex + 0 1 0 100BASE-TX, Half-Duplex + 0 1 1 100BASE-TX, Full-Duplex + AN_EN AN1 AN0 Advertised Mode + 1 0 0 10BASE-T, Half/Full-Duplex + 1 0 1 100BASE-TX, Half/Full-Duplex + 1 1 0 10BASE-T Half-Duplex + 100BASE-TX, Half-Duplex + 1 1 1 10BASE-T, Half/Full-Duplex + 100BASE-TX, Half/Full-Duplex + */ + + /* Read Control register. */ + HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_00_BMCR, &ulConfig ); + + ulConfig &= ~( BMCR_ANRESTART | BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX ); + + /* HT 12/9/14: always set AN-restart and AN-enable, even though the choices + are limited. */ + ulConfig |= (BMCR_ANRESTART | BMCR_ANENABLE); + + if( xPHYProperties.speed == PHY_SPEED_100 ) + { + ulConfig |= BMCR_SPEED100; + } + else if( xPHYProperties.speed == PHY_SPEED_10 ) + { + ulConfig &= ~BMCR_SPEED100; + } + + if( xPHYProperties.duplex == PHY_DUPLEX_FULL ) + { + ulConfig |= BMCR_FULLDPLX; + } + else if( xPHYProperties.duplex == PHY_DUPLEX_HALF ) + { + ulConfig &= ~BMCR_FULLDPLX; + } + + #if( EXPECTED_PHY_ID == PHY_ID_LAN8720 ) + { + } + #elif( EXPECTED_PHY_ID == PHY_ID_DP83848I ) + { + /* Read PHY Control register. */ + HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_19_PHYCR, &ulPhyControl ); + + /* Clear bits which might get set: */ + ulPhyControl &= ~( PHYCR_MDIX_EN|PHYCR_MDIX_FORCE ); + + if( xPHYProperties.mdix == PHY_MDIX_AUTO ) + { + ulPhyControl |= PHYCR_MDIX_EN; + } + else if( xPHYProperties.mdix == PHY_MDIX_CROSSED ) + { + /* Force direct link = Use crossed RJ45 cable. */ + ulPhyControl &= ~PHYCR_MDIX_FORCE; + } + else + { + /* Force crossed link = Use direct RJ45 cable. */ + ulPhyControl |= PHYCR_MDIX_FORCE; + } + /* update PHY Control Register. */ + HAL_ETH_WritePHYRegister( &xETH, PHY_REG_19_PHYCR, ulPhyControl ); + } + #endif + FreeRTOS_printf( ( "+TCP: advertise: %lX config %lX\n", ulAdvertise, ulConfig ) ); + + /* Now the two values to global values for later use. */ + ulBCRvalue = ulConfig; + ulACRValue = ulAdvertise; +} +/*-----------------------------------------------------------*/ + +static void prvEthernetUpdateConfig( BaseType_t xForce ) +{ +__IO uint32_t ulTimeout = 0; +uint32_t ulRegValue = 0; + + FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS %d Force %d\n", + ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 , + xForce ) ); + + if( ( xForce != pdFALSE ) || ( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) ) + { + /* Restart the auto-negotiation. */ + if( xETH.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE ) + { + /* Enable Auto-Negotiation. */ + HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, ulBCRvalue | BMCR_ANRESTART ); + HAL_ETH_WritePHYRegister( &xETH, PHY_REG_04_ADVERTISE, ulACRValue); + + /* Wait until the auto-negotiation will be completed */ + do + { + ulTimeout++; + HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_01_BMSR, &ulRegValue ); + } while( ( ( ulRegValue & PHY_AUTONEGO_COMPLETE) == 0 ) && ( ulTimeout < PHY_READ_TO ) ); + + HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, ulBCRvalue & ~BMCR_ANRESTART ); + + if( ulTimeout < PHY_READ_TO ) + { + /* Reset Timeout counter. */ + ulTimeout = 0; + + HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_01_BMSR, &ulRegValue); + if( ( ulRegValue & BMSR_LINK_STATUS ) != 0 ) + { + ulPHYLinkStatus |= BMSR_LINK_STATUS; + } + else + { + ulPHYLinkStatus &= ~( BMSR_LINK_STATUS ); + } + + #if( EXPECTED_PHY_ID == PHY_ID_LAN8720 ) + { + /* 31 RW PHY Special Control Status */ + uint32_t ulControlStatus; + + HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_1F_PHYSPCS, &ulControlStatus); + ulRegValue = 0; + if( ( ulControlStatus & PHYSPCS_FULL_DUPLEX ) != 0 ) + { + ulRegValue |= PHY_DUPLEX_STATUS; + } + if( ( ulControlStatus & PHYSPCS_SPEED_MASK ) == PHYSPCS_SPEED_10 ) + { + ulRegValue |= PHY_SPEED_STATUS; + } + + } + #elif( EXPECTED_PHY_ID == PHY_ID_DP83848I ) + { + /* Read the result of the auto-negotiation. */ + HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_10_PHY_SR, &ulRegValue); + } + #endif + FreeRTOS_printf( ( ">> Autonego ready: %08lx: %s duplex %u mbit %s status\n", + ulRegValue, + (ulRegValue & PHY_DUPLEX_STATUS) ? "full" : "half", + (ulRegValue & PHY_SPEED_STATUS) ? 10 : 100, + ((ulPHYLinkStatus |= BMSR_LINK_STATUS) != 0) ? "high" : "low" ) ); + + /* Configure the MAC with the Duplex Mode fixed by the + auto-negotiation process. */ + if( ( ulRegValue & PHY_DUPLEX_STATUS ) != ( uint32_t ) RESET ) + { + /* Set Ethernet duplex mode to Full-duplex following the + auto-negotiation. */ + xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX; + } + else + { + /* Set Ethernet duplex mode to Half-duplex following the + auto-negotiation. */ + xETH.Init.DuplexMode = ETH_MODE_HALFDUPLEX; + } + + /* Configure the MAC with the speed fixed by the + auto-negotiation process. */ + if( ( ulRegValue & PHY_SPEED_STATUS) != 0 ) + { + /* Set Ethernet speed to 10M following the + auto-negotiation. */ + xETH.Init.Speed = ETH_SPEED_10M; + } + else + { + /* Set Ethernet speed to 100M following the + auto-negotiation. */ + xETH.Init.Speed = ETH_SPEED_100M; + } + } /* if( ulTimeout < PHY_READ_TO ) */ + } + else /* AutoNegotiation Disable */ + { + uint16_t usValue; + + /* Check parameters */ + assert_param( IS_ETH_SPEED( xETH.Init.Speed ) ); + assert_param( IS_ETH_DUPLEX_MODE( xETH.Init.DuplexMode ) ); + + /* Set MAC Speed and Duplex Mode to PHY */ + usValue = ( uint16_t ) ( xETH.Init.DuplexMode >> 3 ) | ( uint16_t ) ( xETH.Init.Speed >> 1 ); + HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, usValue ); + } + + /* ETHERNET MAC Re-Configuration */ + HAL_ETH_ConfigMAC( &xETH, (ETH_MACInitTypeDef *) NULL); + + /* Restart MAC interface */ + HAL_ETH_Start( &xETH); + } + else + { + /* Stop MAC interface */ + HAL_ETH_Stop( &xETH ); + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ +BaseType_t xReturn; + + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void *pvParameters ) +{ +TimeOut_t xPhyTime; +TickType_t xPhyRemTime; +UBaseType_t uxLastMinBufferCount = 0; +#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) +UBaseType_t uxLastMinQueueSpace = 0; +#endif +UBaseType_t uxCurrentCount; +BaseType_t xResult = 0; +uint32_t xStatus; +const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + 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( ipconfigZERO_COPY_TX_DRIVER != 0 ) + if( xTXDescriptorSemaphore != NULL ) + { + static UBaseType_t uxLowestSemCount = ( UBaseType_t ) ETH_TXBUFNB - 1; + + uxCurrentCount = uxSemaphoreGetCount( xTXDescriptorSemaphore ); + if( uxLowestSemCount > uxCurrentCount ) + { + uxLowestSemCount = uxCurrentCount; + FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) ); + } + + } + #endif + #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; + + xResult = prvNetworkInterfaceInput(); + if( xResult > 0 ) + { + while( prvNetworkInterfaceInput() > 0 ) + { + } + } + } + + if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) + { + /* Code to release TX buffers if zero-copy is used. */ + ulISREvents &= ~EMAC_IF_TX_EVENT; + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Check if DMA packets have been delivered. */ + vClearTXBuffers(); + } + #endif + } + + 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 ) + { + HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_01_BMSR, &xStatus ); + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) ) + { + ulPHYLinkStatus = xStatus; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) ); + prvEthernetUpdateConfig( pdFALSE ); + } + + 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 ); + } + } + } +} +/*-----------------------------------------------------------*/ + +void ETH_IRQHandler( void ) +{ + HAL_ETH_IRQHandler( &xETH ); +}