2 * Some constants, hardware definitions and comments taken from ST's HAL driver
3 * library, COPYRIGHT(c) 2015 STMicroelectronics.
8 Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
10 Permission is hereby granted, free of charge, to any person obtaining a copy of
11 this software and associated documentation files (the "Software"), to deal in
12 the Software without restriction, including without limitation the rights to
13 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
14 the Software, and to permit persons to whom the Software is furnished to do so,
15 subject to the following conditions:
17 The above copyright notice and this permission notice shall be included in all
18 copies or substantial portions of the Software.
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
22 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
23 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 http://aws.amazon.com/freertos
28 http://www.FreeRTOS.org
31 /* Standard includes. */
36 /* FreeRTOS includes. */
42 /* FreeRTOS+TCP includes. */
43 #include "FreeRTOS_IP.h"
44 #include "FreeRTOS_Sockets.h"
45 #include "FreeRTOS_IP_Private.h"
46 #include "FreeRTOS_DNS.h"
47 #include "FreeRTOS_ARP.h"
48 #include "NetworkBufferManagement.h"
49 #include "NetworkInterface.h"
50 #include "phyHandling.h"
53 #if defined( STM32F7xx )
54 #include "stm32f7xx_hal.h"
55 #elif defined( STM32F4xx )
56 #include "stm32f4xx_hal.h"
57 #elif defined( STM32F2xx )
58 #include "stm32f2xx_hal.h"
59 #elif !defined( _lint ) /* Lint does not like an #error */
63 #include "stm32fxx_hal_eth.h"
65 /* Interrupt events to process. Currently only the Rx event is processed
66 although code for other events is included to allow for possible future
68 #define EMAC_IF_RX_EVENT 1UL
69 #define EMAC_IF_TX_EVENT 2UL
70 #define EMAC_IF_ERR_EVENT 4UL
71 #define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )
73 #define ETH_DMA_ALL_INTS \
74 ( ETH_DMA_IT_TST | ETH_DMA_IT_PMT | ETH_DMA_IT_MMC | ETH_DMA_IT_NIS | ETH_DMA_IT_ER | \
75 ETH_DMA_IT_FBE | ETH_DMA_IT_RWT | ETH_DMA_IT_RPS | ETH_DMA_IT_RBU | ETH_DMA_IT_R | \
76 ETH_DMA_IT_TU | ETH_DMA_IT_RO | ETH_DMA_IT_TJT | ETH_DMA_IT_TPS | ETH_DMA_IT_T )
78 #ifndef niEMAC_HANDLER_TASK_PRIORITY
79 #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1
82 #define ipFRAGMENT_OFFSET_BIT_MASK ( ( uint16_t ) 0x0fff ) /* The bits in the two byte IP header field that make up the fragment offset value. */
84 #if( ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) || ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) )
85 #warning Consider enabling checksum offloading
88 #ifndef niDESCRIPTOR_WAIT_TIME_MS
89 #define niDESCRIPTOR_WAIT_TIME_MS 250uL
93 * Most users will want a PHY that negotiates about
94 * the connection properties: speed, dmix and duplex.
95 * On some rare cases, you want to select what is being
96 * advertised, properties like MDIX and duplex.
99 #if !defined( ipconfigETHERNET_AN_ENABLE )
100 /* Enable auto-negotiation */
101 #define ipconfigETHERNET_AN_ENABLE 1
104 #if !defined( ipconfigETHERNET_AUTO_CROSS_ENABLE )
105 #define ipconfigETHERNET_AUTO_CROSS_ENABLE 1
108 #if( ipconfigETHERNET_AN_ENABLE == 0 )
110 * The following three defines are only used in case there
111 * is no auto-negotiation.
113 #if !defined( ipconfigETHERNET_CROSSED_LINK )
114 #define ipconfigETHERNET_CROSSED_LINK 1
117 #if !defined( ipconfigETHERNET_USE_100MB )
118 #define ipconfigETHERNET_USE_100MB 1
121 #if !defined( ipconfigETHERNET_USE_FULL_DUPLEX )
122 #define ipconfigETHERNET_USE_FULL_DUPLEX 1
124 #endif /* ipconfigETHERNET_AN_ENABLE == 0 */
126 /* Default the size of the stack used by the EMAC deferred handler task to twice
127 the size of the stack used by the idle task - but allow this to be overridden in
128 FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */
129 #ifndef configEMAC_TASK_STACK_SIZE
130 #define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE )
133 /* Two choices must be made: RMII versus MII,
134 and the index of the PHY in use ( between 0 and 31 ). */
135 #ifndef ipconfigUSE_RMII
137 #define ipconfigUSE_RMII 1
139 #define ipconfigUSE_RMII 0
140 #endif /* STM32F7xx */
141 #endif /* ipconfigUSE_RMII */
143 #if( ipconfigUSE_RMII != 0 )
144 #warning Using RMII, make sure if this is correct
146 #warning Using MII, make sure if this is correct
151 eMACInit, /* Must initialise MAC. */
152 eMACPass, /* Initialisation was successful. */
153 eMACFailed, /* Initialisation failed. */
154 } eMAC_INIT_STATUS_TYPE;
156 static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit;
158 /*-----------------------------------------------------------*/
161 * A deferred interrupt handler task that processes
163 static void prvEMACHandlerTask( void *pvParameters );
166 * Force a negotiation with the Switch or Router and wait for LS.
168 static void prvEthernetUpdateConfig( BaseType_t xForce );
171 * See if there is a new packet and forward it to the IP-task.
173 static BaseType_t prvNetworkInterfaceInput( void );
175 #if( ipconfigUSE_LLMNR != 0 )
177 * For LLMNR, an extra MAC-address must be configured to
178 * be able to receive the multicast messages.
180 static void prvMACAddressConfig(ETH_HandleTypeDef *heth, uint32_t ulIndex, uint8_t *Addr);
184 * Check if a given packet should be accepted.
186 static BaseType_t xMayAcceptPacket( uint8_t *pcBuffer );
189 * Initialise the TX descriptors.
191 static void prvDMATxDescListInit( void );
194 * Initialise the RX descriptors.
196 static void prvDMARxDescListInit( void );
198 /* After packets have been sent, the network
199 buffers will be released. */
200 static void vClearTXBuffers( void );
202 /*-----------------------------------------------------------*/
204 /* Bit map of outstanding ETH interrupt events for processing. Currently only
205 the Rx interrupt is handled, although code is included for other events to
206 enable future expansion. */
207 static volatile uint32_t ulISREvents;
209 #if( ipconfigUSE_LLMNR == 1 )
210 static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC };
213 static EthernetPhy_t xPhyObject;
215 /* Ethernet handle. */
216 static ETH_HandleTypeDef xETH;
218 /* xTXDescriptorSemaphore is a counting semaphore with
219 a maximum count of ETH_TXBUFNB, which is the number of
220 DMA TX descriptors. */
221 static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
224 * Note: it is adviced to define both
226 * #define ipconfigZERO_COPY_RX_DRIVER 1
227 * #define ipconfigZERO_COPY_TX_DRIVER 1
229 * The method using memcpy is slower and probaly uses more RAM memory.
230 * The possibility is left in the code just for comparison.
232 * It is adviced to define ETH_TXBUFNB at least 4. Note that no
233 * TX buffers are allocated in a zero-copy driver.
235 /* MAC buffers: ---------------------------------------------------------*/
237 /* Put the DMA descriptors in '.first_data'.
238 This is important for STM32F7, which has an L1 data cache.
239 The first 64KB of the SRAM is not cached.
240 See README.TXT in this folder. */
242 /* Ethernet Rx MA Descriptor */
243 __attribute__ ((aligned (32)))
244 #if defined(STM32F7xx)
245 __attribute__ ((section(".first_data")))
247 ETH_DMADescTypeDef DMARxDscrTab[ ETH_RXBUFNB ];
249 #if( ipconfigZERO_COPY_RX_DRIVER == 0 )
250 /* Ethernet Receive Buffer */
251 __ALIGN_BEGIN uint8_t Rx_Buff[ ETH_RXBUFNB ][ ETH_RX_BUF_SIZE ] __ALIGN_END;
254 /* Ethernet Tx DMA Descriptor */
255 __attribute__ ((aligned (32)))
256 #if defined(STM32F7xx)
257 __attribute__ ((section(".first_data")))
259 ETH_DMADescTypeDef DMATxDscrTab[ ETH_TXBUFNB ];
261 #if( ipconfigZERO_COPY_TX_DRIVER == 0 )
262 /* Ethernet Transmit Buffer */
263 __ALIGN_BEGIN uint8_t Tx_Buff[ ETH_TXBUFNB ][ ETH_TX_BUF_SIZE ] __ALIGN_END;
266 /* DMATxDescToClear points to the next TX DMA descriptor
267 that must be cleared by vClearTXBuffers(). */
268 static __IO ETH_DMADescTypeDef *DMATxDescToClear;
270 /* Holds the handle of the task used as a deferred interrupt processor. The
271 handle is used so direct notifications can be sent to the task for all EMAC/DMA
272 related interrupts. */
273 static TaskHandle_t xEMACTaskHandle = NULL;
275 /* For local use only: describe the PHY's properties: */
276 const PhyProperties_t xPHYProperties =
278 #if( ipconfigETHERNET_AN_ENABLE != 0 )
279 .ucSpeed = PHY_SPEED_AUTO,
280 .ucDuplex = PHY_DUPLEX_AUTO,
282 #if( ipconfigETHERNET_USE_100MB != 0 )
283 .ucSpeed = PHY_SPEED_100,
285 .ucSpeed = PHY_SPEED_10,
288 #if( ipconfigETHERNET_USE_FULL_DUPLEX != 0 )
289 .ucDuplex = PHY_DUPLEX_FULL,
291 .ucDuplex = PHY_DUPLEX_HALF,
295 #if( ipconfigETHERNET_AN_ENABLE != 0 ) && ( ipconfigETHERNET_AUTO_CROSS_ENABLE != 0 )
296 .ucMDI_X = PHY_MDIX_AUTO,
297 #elif( ipconfigETHERNET_CROSSED_LINK != 0 )
298 .ucMDI_X = PHY_MDIX_CROSSED,
300 .ucMDI_X = PHY_MDIX_DIRECT,
304 /*-----------------------------------------------------------*/
306 void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef *heth )
308 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
312 /* Ethernet RX-Complete callback function, elsewhere declared as weak. */
313 ulISREvents |= EMAC_IF_RX_EVENT;
314 /* Wakeup the prvEMACHandlerTask. */
315 if( xEMACTaskHandle != NULL )
317 vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken );
318 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
321 /*-----------------------------------------------------------*/
323 void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef *heth )
325 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
329 /* This call-back is only useful in case packets are being sent
330 zero-copy. Once they're sent, the buffers will be released
331 by the function vClearTXBuffers(). */
332 ulISREvents |= EMAC_IF_TX_EVENT;
333 /* Wakeup the prvEMACHandlerTask. */
334 if( xEMACTaskHandle != NULL )
336 vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken );
337 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
340 /*-----------------------------------------------------------*/
342 static void vClearTXBuffers()
344 __IO ETH_DMADescTypeDef *txLastDescriptor = xETH.TxDesc;
345 size_t uxCount = ( ( UBaseType_t ) ETH_TXBUFNB ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
346 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
347 NetworkBufferDescriptor_t *pxNetworkBuffer;
351 /* This function is called after a TX-completion interrupt.
352 It will release each Network Buffer used in xNetworkInterfaceOutput().
353 'uxCount' represents the number of descriptors given to DMA for transmission.
354 After sending a packet, the DMA will clear the 'ETH_DMATXDESC_OWN' bit. */
355 while( ( uxCount > 0 ) && ( ( DMATxDescToClear->Status & ETH_DMATXDESC_OWN ) == 0 ) )
357 if( ( DMATxDescToClear == txLastDescriptor ) && ( uxCount != ETH_TXBUFNB ) )
361 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
363 ucPayLoad = ( uint8_t * )DMATxDescToClear->Buffer1Addr;
365 if( ucPayLoad != NULL )
367 pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad );
368 if( pxNetworkBuffer != NULL )
370 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ) ;
372 DMATxDescToClear->Buffer1Addr = ( uint32_t )0u;
375 #endif /* ipconfigZERO_COPY_TX_DRIVER */
377 DMATxDescToClear = ( ETH_DMADescTypeDef * )( DMATxDescToClear->Buffer2NextDescAddr );
380 /* Tell the counting semaphore that one more TX descriptor is available. */
381 xSemaphoreGive( xTXDescriptorSemaphore );
384 /*-----------------------------------------------------------*/
386 BaseType_t xNetworkInterfaceInitialise( void )
388 HAL_StatusTypeDef hal_eth_init_status;
391 if( xMacInitStatus == eMACInit )
393 xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ETH_TXBUFNB, ( UBaseType_t ) ETH_TXBUFNB );
394 if( xTXDescriptorSemaphore == NULL )
396 xMacInitStatus = eMACFailed;
403 xETH.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
404 xETH.Init.Speed = ETH_SPEED_100M;
405 xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
406 /* Value of PhyAddress doesn't matter, will be probed for. */
407 xETH.Init.PhyAddress = 0;
409 xETH.Init.MACAddr = ( uint8_t * ) FreeRTOS_GetMACAddress();
410 xETH.Init.RxMode = ETH_RXINTERRUPT_MODE;
412 #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
414 /* using the ETH_CHECKSUM_BY_HARDWARE option:
415 both the IP and the protocol checksums will be calculated
416 by the peripheral. */
417 xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
421 xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE;
425 #if( ipconfigUSE_RMII != 0 )
427 xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
431 xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII;
433 #endif /* ipconfigUSE_RMII */
435 hal_eth_init_status = HAL_ETH_Init( &xETH );
437 /* Only for inspection by debugger. */
438 ( void ) hal_eth_init_status;
440 /* Set the TxDesc and RxDesc pointers. */
441 xETH.TxDesc = DMATxDscrTab;
442 xETH.RxDesc = DMARxDscrTab;
444 /* Make sure that all unused fields are cleared. */
445 memset( &DMATxDscrTab, '\0', sizeof( DMATxDscrTab ) );
446 memset( &DMARxDscrTab, '\0', sizeof( DMARxDscrTab ) );
448 /* Initialize Tx Descriptors list: Chain Mode */
449 DMATxDescToClear = DMATxDscrTab;
451 /* Initialise TX-descriptors. */
452 prvDMATxDescListInit();
454 /* Initialise RX-descriptors. */
455 prvDMARxDescListInit();
457 #if( ipconfigUSE_LLMNR != 0 )
459 /* Program the LLMNR address at index 1. */
460 prvMACAddressConfig( &xETH, ETH_MAC_ADDRESS1, ( uint8_t *) xLLMNR_MACAddress );
464 /* Force a negotiation with the Switch or Router and wait for LS. */
465 prvEthernetUpdateConfig( pdTRUE );
467 /* The deferred interrupt handler task is created at the highest
468 possible priority to ensure the interrupt handler can return directly
469 to it. The task's handle is stored in xEMACTaskHandle so interrupts can
470 notify the task when there is something to process. */
471 if( xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle ) == pdPASS )
473 /* The xTXDescriptorSemaphore and the task are created successfully. */
474 xMacInitStatus = eMACPass;
478 xMacInitStatus = eMACFailed;
481 } /* if( xEMACTaskHandle == NULL ) */
483 if( xMacInitStatus != eMACPass )
485 /* EMAC initialisation failed, return pdFAIL. */
490 if( xPhyObject.ulLinkStatusMask != 0uL )
492 xETH.Instance->DMAIER |= ETH_DMA_ALL_INTS;
494 FreeRTOS_printf( ( "Link Status is high\n" ) ) ;
498 /* For now pdFAIL will be returned. But prvEMACHandlerTask() is running
499 and it will keep on checking the PHY and set 'ulLinkStatusMask' when necessary. */
503 /* When returning non-zero, the stack will become active and
504 start DHCP (in configured) */
507 /*-----------------------------------------------------------*/
509 static void prvDMATxDescListInit()
511 ETH_DMADescTypeDef *pxDMADescriptor;
514 /* Get the pointer on the first member of the descriptor list */
515 pxDMADescriptor = DMATxDscrTab;
517 /* Fill each DMA descriptor with the right values */
518 for( xIndex = 0; xIndex < ETH_TXBUFNB; xIndex++, pxDMADescriptor++ )
520 /* Set Second Address Chained bit */
521 pxDMADescriptor->Status = ETH_DMATXDESC_TCH;
523 #if( ipconfigZERO_COPY_TX_DRIVER == 0 )
525 /* Set Buffer1 address pointer */
526 pxDMADescriptor->Buffer1Addr = ( uint32_t )( Tx_Buff[ xIndex ] );
530 if( xETH.Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE )
532 /* Set the DMA Tx descriptors checksum insertion for TCP, UDP, and ICMP */
533 pxDMADescriptor->Status |= ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL;
537 pxDMADescriptor->Status &= ~( ( uint32_t ) ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL );
540 /* Initialize the next descriptor with the Next Descriptor Polling Enable */
541 if( xIndex < ETH_TXBUFNB - 1 )
543 /* Set next descriptor address register with next descriptor base address */
544 pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) ( pxDMADescriptor + 1 );
548 /* For last descriptor, set next descriptor address register equal to the first descriptor base address */
549 pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMATxDscrTab;
553 /* Set Transmit Descriptor List Address Register */
554 xETH.Instance->DMATDLAR = ( uint32_t ) DMATxDscrTab;
556 /*-----------------------------------------------------------*/
558 static void prvDMARxDescListInit()
560 ETH_DMADescTypeDef *pxDMADescriptor;
566 /* Get the pointer on the first member of the descriptor list */
567 pxDMADescriptor = DMARxDscrTab;
569 /* Fill each DMA descriptor with the right values */
570 for( xIndex = 0; xIndex < ETH_RXBUFNB; xIndex++, pxDMADescriptor++ )
573 /* Set Buffer1 size and Second Address Chained bit */
574 pxDMADescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | (uint32_t)ETH_RX_BUF_SIZE;
576 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
578 /* Set Buffer1 address pointer */
579 NetworkBufferDescriptor_t *pxBuffer;
581 pxBuffer = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, 100ul );
582 /* If the assert below fails, make sure that there are at least 'ETH_RXBUFNB'
583 Network Buffers available during start-up ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ) */
584 configASSERT( pxBuffer != NULL );
585 if( pxBuffer != NULL )
587 pxDMADescriptor->Buffer1Addr = (uint32_t)pxBuffer->pucEthernetBuffer;
588 pxDMADescriptor->Status = ETH_DMARXDESC_OWN;
593 /* Set Buffer1 address pointer */
594 pxDMADescriptor->Buffer1Addr = ( uint32_t )( Rx_Buff[ xIndex ] );
595 /* Set Own bit of the Rx descriptor Status */
596 pxDMADescriptor->Status = ETH_DMARXDESC_OWN;
600 /* Initialize the next descriptor with the Next Descriptor Polling Enable */
601 if( xIndex < ETH_RXBUFNB - 1 )
603 /* Set next descriptor address register with next descriptor base address */
604 pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t )( pxDMADescriptor + 1 );
608 /* For last descriptor, set next descriptor address register equal to the first descriptor base address */
609 pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMARxDscrTab;
613 /* Set Receive Descriptor List Address Register */
614 xETH.Instance->DMARDLAR = ( uint32_t ) DMARxDscrTab;
616 /*-----------------------------------------------------------*/
618 static void prvMACAddressConfig(ETH_HandleTypeDef *heth, uint32_t ulIndex, uint8_t *Addr)
624 /* Calculate the selected MAC address high register. */
625 ulTempReg = 0x80000000ul | ( ( uint32_t ) Addr[ 5 ] << 8 ) | ( uint32_t ) Addr[ 4 ];
627 /* Load the selected MAC address high register. */
628 ( *(__IO uint32_t *)( ( uint32_t ) ( ETH_MAC_ADDR_HBASE + ulIndex ) ) ) = ulTempReg;
630 /* Calculate the selected MAC address low register. */
631 ulTempReg = ( ( uint32_t ) Addr[ 3 ] << 24 ) | ( ( uint32_t ) Addr[ 2 ] << 16 ) | ( ( uint32_t ) Addr[ 1 ] << 8 ) | Addr[ 0 ];
633 /* Load the selected MAC address low register */
634 ( *(__IO uint32_t *) ( ( uint32_t ) ( ETH_MAC_ADDR_LBASE + ulIndex ) ) ) = ulTempReg;
636 /*-----------------------------------------------------------*/
638 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend )
640 BaseType_t xReturn = pdFAIL;
641 uint32_t ulTransmitSize = 0;
642 __IO ETH_DMADescTypeDef *pxDmaTxDesc;
643 /* Do not wait too long for a free TX DMA buffer. */
644 const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u );
646 /* Open a do {} while ( 0 ) loop to be able to call break. */
649 #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
651 ProtocolPacket_t *pxPacket;
653 /* If the peripheral must calculate the checksum, it wants
654 the protocol checksum to have a value of zero. */
655 pxPacket = ( ProtocolPacket_t * ) ( pxDescriptor->pucEthernetBuffer );
657 if( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
659 pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t )0u;
662 #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */
663 if( xPhyObject.ulLinkStatusMask != 0 )
665 if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
667 /* Time-out waiting for a free TX descriptor. */
671 /* This function does the actual transmission of the packet. The packet is
672 contained in 'pxDescriptor' that is passed to the function. */
673 pxDmaTxDesc = xETH.TxDesc;
675 /* Is this buffer available? */
676 configASSERT ( ( pxDmaTxDesc->Status & ETH_DMATXDESC_OWN ) == 0 );
679 /* Is this buffer available? */
680 /* Get bytes in current buffer. */
681 ulTransmitSize = pxDescriptor->xDataLength;
683 if( ulTransmitSize > ETH_TX_BUF_SIZE )
685 ulTransmitSize = ETH_TX_BUF_SIZE;
688 #if( ipconfigZERO_COPY_TX_DRIVER == 0 )
690 /* Copy the bytes. */
691 memcpy( ( void * ) pxDmaTxDesc->Buffer1Addr, pxDescriptor->pucEthernetBuffer, ulTransmitSize );
695 configASSERT( bReleaseAfterSend != 0 );
697 /* Move the buffer. */
698 pxDmaTxDesc->Buffer1Addr = ( uint32_t )pxDescriptor->pucEthernetBuffer;
699 /* The Network Buffer has been passed to DMA, no need to release it. */
700 bReleaseAfterSend = pdFALSE_UNSIGNED;
702 #endif /* ipconfigZERO_COPY_TX_DRIVER */
704 /* Ask to set the IPv4 checksum.
705 Also need an Interrupt on Completion so that 'vClearTXBuffers()' will be called.. */
706 #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
708 pxDmaTxDesc->Status |= ETH_DMATXDESC_CIC_TCPUDPICMP_FULL | ETH_DMATXDESC_IC;
712 pxDmaTxDesc->Status &= ~( ( uint32_t ) ETH_DMATXDESC_CIC );
713 pxDmaTxDesc->Status |= ETH_DMATXDESC_IC;
718 /* Prepare transmit descriptors to give to DMA. */
720 /* Set LAST and FIRST segment */
721 pxDmaTxDesc->Status |= ETH_DMATXDESC_FS | ETH_DMATXDESC_LS;
723 pxDmaTxDesc->ControlBufferSize = ( ulTransmitSize & ETH_DMATXDESC_TBS1 );
725 #if( NETWORK_BUFFERS_CACHED != 0 )
727 BaseType_t xlength = CACHE_LINE_SIZE * ( ( ulTransmitSize + NETWORK_BUFFER_HEADER_SIZE + CACHE_LINE_SIZE - 1 ) / CACHE_LINE_SIZE );
728 uint32_t *pulBuffer = ( uint32_t )( pxDescriptor->pucEthernetBuffer - NETWORK_BUFFER_HEADER_SIZE );
729 cache_clean_invalidate_by_addr( pulBuffer, xlength );
733 /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
734 pxDmaTxDesc->Status |= ETH_DMATXDESC_OWN;
736 /* Point to next descriptor */
737 xETH.TxDesc = ( ETH_DMADescTypeDef * ) ( xETH.TxDesc->Buffer2NextDescAddr );
738 /* Ensure completion of memory access */
740 /* Resume DMA transmission*/
741 xETH.Instance->DMATPDR = 0;
742 iptraceNETWORK_INTERFACE_TRANSMIT();
748 /* The PHY has no Link Status, packet shall be dropped. */
751 /* The buffer has been sent so can be released. */
752 if( bReleaseAfterSend != pdFALSE )
754 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
759 /*-----------------------------------------------------------*/
761 static BaseType_t xMayAcceptPacket( uint8_t *pcBuffer )
763 const ProtocolPacket_t *pxProtPacket = ( const ProtocolPacket_t * )pcBuffer;
765 switch( pxProtPacket->xTCPPacket.xEthernetHeader.usFrameType )
767 case ipARP_FRAME_TYPE:
768 /* Check it later. */
770 case ipIPv4_FRAME_TYPE:
774 /* Refuse the packet. */
778 #if( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 )
780 const IPHeader_t *pxIPHeader = &(pxProtPacket->xTCPPacket.xIPHeader);
781 uint32_t ulDestinationIPAddress;
783 /* Ensure that the incoming packet is not fragmented (only outgoing packets
784 * can be fragmented) as these are the only handled IP frames currently. */
785 if( ( pxIPHeader->usFragmentOffset & FreeRTOS_ntohs( ipFRAGMENT_OFFSET_BIT_MASK ) ) != 0U )
789 /* HT: Might want to make the following configurable because
790 * most IP messages have a standard length of 20 bytes */
792 /* 0x45 means: IPv4 with an IP header of 5 x 4 = 20 bytes
793 * 0x47 means: IPv4 with an IP header of 7 x 4 = 28 bytes */
794 if( pxIPHeader->ucVersionHeaderLength < 0x45 || pxIPHeader->ucVersionHeaderLength > 0x4F )
799 ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress;
800 /* Is the packet for this node? */
801 if( ( ulDestinationIPAddress != *ipLOCAL_IP_ADDRESS_POINTER ) &&
802 /* Is it a broadcast address x.x.x.255 ? */
803 ( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xff ) != 0xff ) &&
804 #if( ipconfigUSE_LLMNR == 1 )
805 ( ulDestinationIPAddress != ipLLMNR_IP_ADDR ) &&
807 ( *ipLOCAL_IP_ADDRESS_POINTER != 0 ) ) {
808 FreeRTOS_printf( ( "Drop IP %lxip\n", FreeRTOS_ntohl( ulDestinationIPAddress ) ) );
812 if( pxIPHeader->ucProtocol == ipPROTOCOL_UDP )
814 uint16_t usSourcePort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usSourcePort );
815 uint16_t usDestinationPort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort );
817 if( ( xPortHasUDPSocket( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort ) == pdFALSE )
818 #if ipconfigUSE_LLMNR == 1
819 && ( usDestinationPort != ipLLMNR_PORT )
820 && ( usSourcePort != ipLLMNR_PORT )
822 #if ipconfigUSE_NBNS == 1
823 && ( usDestinationPort != ipNBNS_PORT )
824 && ( usSourcePort != ipNBNS_PORT )
826 #if ipconfigUSE_DNS == 1
827 && ( usSourcePort != ipDNS_PORT )
830 /* Drop this packet, not for this device. */
831 FreeRTOS_printf( ( "Drop: UDP port %d -> %d\n", usSourcePort, usDestinationPort ) );
836 #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */
839 /*-----------------------------------------------------------*/
841 static void prvPassEthMessages( NetworkBufferDescriptor_t *pxDescriptor )
843 IPStackEvent_t xRxEvent;
845 xRxEvent.eEventType = eNetworkRxEvent;
846 xRxEvent.pvData = ( void * ) pxDescriptor;
848 if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 1000 ) != pdPASS )
850 /* The buffer could not be sent to the stack so must be released again.
851 This is a deferred handler taskr, not a real interrupt, so it is ok to
852 use the task level function here. */
853 #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
857 NetworkBufferDescriptor_t *pxNext = pxDescriptor->pxNextBuffer;
858 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
859 pxDescriptor = pxNext;
860 } while( pxDescriptor != NULL );
864 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
866 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
867 iptraceETHERNET_RX_EVENT_LOST();
868 FreeRTOS_printf( ( "prvPassEthMessages: Can not queue return packet!\n" ) );
872 iptraceNETWORK_INTERFACE_RECEIVE();
876 static BaseType_t prvNetworkInterfaceInput( void )
878 NetworkBufferDescriptor_t *pxCurDescriptor;
879 NetworkBufferDescriptor_t *pxNewDescriptor = NULL;
880 #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
881 NetworkBufferDescriptor_t *pxFirstDescriptor = NULL;
882 NetworkBufferDescriptor_t *pxLastDescriptor = NULL;
884 BaseType_t xReceivedLength = 0;
885 __IO ETH_DMADescTypeDef *pxDMARxDescriptor;
886 const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( niDESCRIPTOR_WAIT_TIME_MS );
889 pxDMARxDescriptor = xETH.RxDesc;
891 while( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_OWN ) == 0u )
893 BaseType_t xAccepted = pdTRUE;
894 /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
895 xReceivedLength = ( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_FL ) >> ETH_DMARXDESC_FRAMELENGTHSHIFT ) - 4;
897 pucBuffer = (uint8_t *) pxDMARxDescriptor->Buffer1Addr;
899 /* Update the ETHERNET DMA global Rx descriptor with next Rx descriptor */
901 /* Selects the next DMA Rx descriptor list for next buffer to read */
902 xETH.RxDesc = ( ETH_DMADescTypeDef* )pxDMARxDescriptor->Buffer2NextDescAddr;
904 /* In order to make the code easier and faster, only packets in a single buffer
905 will be accepted. This can be done by making the buffers large enough to
906 hold a complete Ethernet packet (1536 bytes).
907 Therefore, two sanity checks: */
908 configASSERT( xReceivedLength <= ETH_RX_BUF_SIZE );
910 if( ( pxDMARxDescriptor->Status & ( ETH_DMARXDESC_CE | ETH_DMARXDESC_IPV4HCE | ETH_DMARXDESC_FT ) ) != ETH_DMARXDESC_FT )
912 /* Not an Ethernet frame-type or a checmsum error. */
917 /* See if this packet must be handled. */
918 xAccepted = xMayAcceptPacket( pucBuffer );
921 if( xAccepted != pdFALSE )
923 /* The packet wil be accepted, but check first if a new Network Buffer can
924 be obtained. If not, the packet will still be dropped. */
925 pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, xDescriptorWaitTime );
927 if( pxNewDescriptor == NULL )
929 /* A new descriptor can not be allocated now. This packet will be dropped. */
933 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
935 /* Find out which Network Buffer was originally passed to the descriptor. */
936 pxCurDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer );
937 configASSERT( pxCurDescriptor != NULL );
941 /* In this mode, the two descriptors are the same. */
942 pxCurDescriptor = pxNewDescriptor;
943 if( pxNewDescriptor != NULL )
945 /* The packet is acepted and a new Network Buffer was created,
946 copy data to the Network Bufffer. */
947 memcpy( pxNewDescriptor->pucEthernetBuffer, pucBuffer, xReceivedLength );
952 if( xAccepted != pdFALSE )
954 pxCurDescriptor->xDataLength = xReceivedLength;
955 #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
957 pxCurDescriptor->pxNextBuffer = NULL;
959 if( pxFirstDescriptor == NULL )
961 // Becomes the first message
962 pxFirstDescriptor = pxCurDescriptor;
964 else if( pxLastDescriptor != NULL )
967 pxLastDescriptor->pxNextBuffer = pxCurDescriptor;
970 pxLastDescriptor = pxCurDescriptor;
974 prvPassEthMessages( pxCurDescriptor );
979 /* Release descriptors to DMA */
980 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
982 /* Set Buffer1 address pointer */
983 if( pxNewDescriptor != NULL )
985 pxDMARxDescriptor->Buffer1Addr = (uint32_t)pxNewDescriptor->pucEthernetBuffer;
989 /* The packet was dropped and the same Network
990 Buffer will be used to receive a new packet. */
993 #endif /* ipconfigZERO_COPY_RX_DRIVER */
995 /* Set Buffer1 size and Second Address Chained bit */
996 pxDMARxDescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | (uint32_t)ETH_RX_BUF_SIZE;
997 pxDMARxDescriptor->Status = ETH_DMARXDESC_OWN;
999 /* Ensure completion of memory access */
1001 /* When Rx Buffer unavailable flag is set clear it and resume
1003 if( ( xETH.Instance->DMASR & ETH_DMASR_RBUS ) != 0 )
1005 /* Clear RBUS ETHERNET DMA flag. */
1006 xETH.Instance->DMASR = ETH_DMASR_RBUS;
1008 /* Resume DMA reception. */
1009 xETH.Instance->DMARPDR = 0;
1011 pxDMARxDescriptor = xETH.RxDesc;
1014 #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
1016 if( pxFirstDescriptor != NULL )
1018 prvPassEthMessages( pxFirstDescriptor );
1021 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
1023 return ( xReceivedLength > 0 );
1025 /*-----------------------------------------------------------*/
1028 BaseType_t xSTM32_PhyRead( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue )
1030 uint16_t usPrevAddress = xETH.Init.PhyAddress;
1032 HAL_StatusTypeDef xHALResult;
1034 xETH.Init.PhyAddress = xAddress;
1035 xHALResult = HAL_ETH_ReadPHYRegister( &xETH, ( uint16_t )xRegister, pulValue );
1036 xETH.Init.PhyAddress = usPrevAddress;
1038 if( xHALResult == HAL_OK )
1048 /*-----------------------------------------------------------*/
1050 BaseType_t xSTM32_PhyWrite( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue )
1052 uint16_t usPrevAddress = xETH.Init.PhyAddress;
1054 HAL_StatusTypeDef xHALResult;
1056 xETH.Init.PhyAddress = xAddress;
1057 xHALResult = HAL_ETH_WritePHYRegister( &xETH, ( uint16_t )xRegister, ulValue );
1058 xETH.Init.PhyAddress = usPrevAddress;
1060 if( xHALResult == HAL_OK )
1070 /*-----------------------------------------------------------*/
1072 void vMACBProbePhy( void )
1074 vPhyInitialise( &xPhyObject, xSTM32_PhyRead, xSTM32_PhyWrite );
1075 xPhyDiscover( &xPhyObject );
1076 xPhyConfigure( &xPhyObject, &xPHYProperties );
1078 /*-----------------------------------------------------------*/
1080 static void prvEthernetUpdateConfig( BaseType_t xForce )
1082 FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS mask %02lX Force %d\n",
1083 xPhyObject.ulLinkStatusMask,
1086 if( ( xForce != pdFALSE ) || ( xPhyObject.ulLinkStatusMask != 0 ) )
1088 /* Restart the auto-negotiation. */
1089 if( xETH.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE )
1091 xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) );
1093 /* Configure the MAC with the Duplex Mode fixed by the
1094 auto-negotiation process. */
1095 if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL )
1097 xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
1101 xETH.Init.DuplexMode = ETH_MODE_HALFDUPLEX;
1104 /* Configure the MAC with the speed fixed by the
1105 auto-negotiation process. */
1106 if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 )
1108 xETH.Init.Speed = ETH_SPEED_10M;
1112 xETH.Init.Speed = ETH_SPEED_100M;
1115 else /* AutoNegotiation Disable */
1117 /* Check parameters */
1118 assert_param( IS_ETH_SPEED( xETH.Init.Speed ) );
1119 assert_param( IS_ETH_DUPLEX_MODE( xETH.Init.DuplexMode ) );
1121 if( xETH.Init.DuplexMode == ETH_MODE_FULLDUPLEX )
1123 xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_HALF;
1127 xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_FULL;
1130 if( xETH.Init.Speed == ETH_SPEED_10M )
1132 xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_10;
1136 xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_100;
1139 xPhyObject.xPhyPreferences.ucMDI_X = PHY_MDIX_AUTO;
1141 /* Use predefined (fixed) configuration. */
1142 xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) );
1145 /* ETHERNET MAC Re-Configuration */
1146 HAL_ETH_ConfigMAC( &xETH, (ETH_MACInitTypeDef *) NULL);
1148 /* Restart MAC interface */
1149 HAL_ETH_Start( &xETH);
1153 /* Stop MAC interface */
1154 HAL_ETH_Stop( &xETH );
1157 /*-----------------------------------------------------------*/
1159 BaseType_t xGetPhyLinkStatus( void )
1163 if( xPhyObject.ulLinkStatusMask != 0 )
1174 /*-----------------------------------------------------------*/
1176 /* Uncomment this in case BufferAllocation_1.c is used. */
1178 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
1181 #if defined(STM32F7xx)
1182 __attribute__ ((section(".first_data")))
1184 uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * ETH_MAX_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) );
1185 uint8_t *ucRAMBuffer = ucNetworkPackets;
1188 for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
1190 pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
1191 *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
1192 ucRAMBuffer += ETH_MAX_PACKET_SIZE;
1195 /*-----------------------------------------------------------*/
1197 static void prvEMACHandlerTask( void *pvParameters )
1199 UBaseType_t uxLastMinBufferCount = 0;
1200 #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
1201 UBaseType_t uxLastMinQueueSpace = 0;
1203 UBaseType_t uxCurrentCount;
1205 const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
1207 /* Remove compiler warnings about unused parameters. */
1208 ( void ) pvParameters;
1213 uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
1214 if( uxLastMinBufferCount != uxCurrentCount )
1216 /* The logging produced below may be helpful
1217 while tuning +TCP: see how many buffers are in use. */
1218 uxLastMinBufferCount = uxCurrentCount;
1219 FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
1220 uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
1223 if( xTXDescriptorSemaphore != NULL )
1225 static UBaseType_t uxLowestSemCount = ( UBaseType_t ) ETH_TXBUFNB - 1;
1227 uxCurrentCount = uxSemaphoreGetCount( xTXDescriptorSemaphore );
1228 if( uxLowestSemCount > uxCurrentCount )
1230 uxLowestSemCount = uxCurrentCount;
1231 FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) );
1236 #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
1238 uxCurrentCount = uxGetMinimumIPQueueSpace();
1239 if( uxLastMinQueueSpace != uxCurrentCount )
1241 /* The logging produced below may be helpful
1242 while tuning +TCP: see how many buffers are in use. */
1243 uxLastMinQueueSpace = uxCurrentCount;
1244 FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
1247 #endif /* ipconfigCHECK_IP_QUEUE_SPACE */
1249 if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 )
1251 /* No events to process now, wait for the next. */
1252 ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );
1255 if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
1257 ulISREvents &= ~EMAC_IF_RX_EVENT;
1259 xResult = prvNetworkInterfaceInput();
1262 if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
1264 /* Code to release TX buffers if zero-copy is used. */
1265 ulISREvents &= ~EMAC_IF_TX_EVENT;
1266 /* Check if DMA packets have been delivered. */
1270 if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 )
1272 /* Future extension: logging about errors that occurred. */
1273 ulISREvents &= ~EMAC_IF_ERR_EVENT;
1275 if( xPhyCheckLinkStatus( &xPhyObject, xResult ) != 0 )
1277 /* Something has changed to a Link Status, need re-check. */
1278 prvEthernetUpdateConfig( pdFALSE );
1282 /*-----------------------------------------------------------*/
1284 void ETH_IRQHandler( void )
1286 HAL_ETH_IRQHandler( &xETH );