/* * FreeRTOS+TCP Labs Build 160919 (C) 2016 Real Time Engineers ltd. * Authors include Hein Tibosch and Richard Barry * ******************************************************************************* ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE *** *** *** *** *** *** FREERTOS+TCP IS STILL IN THE LAB (mainly because the FTP and HTTP *** *** demos have a dependency on FreeRTOS+FAT, which is only in the Labs *** *** download): *** *** *** *** FreeRTOS+TCP is functional and has been used in commercial products *** *** for some time. Be aware however that we are still refining its *** *** design, the source code does not yet quite conform to the strict *** *** coding and style standards mandated by Real Time Engineers ltd., and *** *** the documentation and testing is not necessarily complete. *** *** *** *** PLEASE REPORT EXPERIENCES USING THE SUPPORT RESOURCES FOUND ON THE *** *** URL: http://www.FreeRTOS.org/contact Active early adopters may, at *** *** the sole discretion of Real Time Engineers Ltd., be offered versions *** *** under a license other than that described below. *** *** *** *** *** ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE *** ******************************************************************************* * * FreeRTOS+TCP can be used under two different free open source licenses. The * license that applies is dependent on the processor on which FreeRTOS+TCP is * executed, as follows: * * If FreeRTOS+TCP is executed on one of the processors listed under the Special * License Arrangements heading of the FreeRTOS+TCP license information web * page, then it can be used under the terms of the FreeRTOS Open Source * License. If FreeRTOS+TCP is used on any other processor, then it can be used * under the terms of the GNU General Public License V2. Links to the relevant * licenses follow: * * The FreeRTOS+TCP License Information Page: http://www.FreeRTOS.org/tcp_license * The FreeRTOS Open Source License: http://www.FreeRTOS.org/license * The GNU General Public License Version 2: http://www.FreeRTOS.org/gpl-2.0.txt * * FreeRTOS+TCP is distributed in the hope that it will be useful. You cannot * use FreeRTOS+TCP unless you agree that you use the software 'as is'. * FreeRTOS+TCP is provided WITHOUT ANY WARRANTY; without even the implied * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they * implied, expressed, or statutory. * * 1 tab == 4 spaces! * * http://www.FreeRTOS.org * http://www.FreeRTOS.org/plus * http://www.FreeRTOS.org/labs * */ /****************************************************************************** * * See the following web page for essential buffer allocation scheme usage and * configuration details: * http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html * ******************************************************************************/ /* THIS FILE SHOULD NOT BE USED IF THE PROJECT INCLUDES A MEMORY ALLOCATOR * THAT WILL FRAGMENT THE HEAP MEMORY. For example, heap_2 must not be used, * heap_4 can be used. */ /* Standard includes. */ #include /* FreeRTOS includes. */ #include "FreeRTOS.h" #include "task.h" #include "semphr.h" /* FreeRTOS+TCP includes. */ #include "FreeRTOS_IP.h" #include "FreeRTOS_UDP_IP.h" #include "FreeRTOS_IP_Private.h" #include "NetworkInterface.h" #include "NetworkBufferManagement.h" #include "tcpip/tcpip.h" #include "tcpip/src/tcpip_private.h" #include "NetworkConfig.h" /* The obtained network buffer must be large enough to hold a packet that might * replace the packet that was requested to be sent. */ #if ipconfigUSE_TCP == 1 #define baMINIMAL_BUFFER_SIZE sizeof( TCPPacket_t ) #else #define baMINIMAL_BUFFER_SIZE sizeof( ARPPacket_t ) #endif /* ipconfigUSE_TCP == 1 */ /*_RB_ This is too complex not to have an explanation. */ #if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES ) #define ASSERT_CONCAT_( a, b ) a ## b #define ASSERT_CONCAT( a, b ) ASSERT_CONCAT_( a, b ) #define STATIC_ASSERT( e ) \ ; enum { ASSERT_CONCAT( assert_line_, __LINE__ ) = 1 / ( !!( e ) ) } STATIC_ASSERT( ipconfigETHERNET_MINIMUM_PACKET_BYTES <= baMINIMAL_BUFFER_SIZE ); #endif /* A list of free (available) NetworkBufferDescriptor_t structures. */ static List_t xFreeBuffersList; /* Some statistics about the use of buffers. */ static size_t uxMinimumFreeNetworkBuffers; /* Declares the pool of NetworkBufferDescriptor_t structures that are available * to the system. All the network buffers referenced from xFreeBuffersList exist * in this array. The array is not accessed directly except during initialisation, * when the xFreeBuffersList is filled (as all the buffers are free when the system * is booted). */ static NetworkBufferDescriptor_t xNetworkBufferDescriptors[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ]; /* This constant is defined as false to let FreeRTOS_TCP_IP.c know that the * network buffers have a variable size: resizing may be necessary */ const BaseType_t xBufferAllocFixedSize = pdFALSE; /* The semaphore used to obtain network buffers. */ static SemaphoreHandle_t xNetworkBufferSemaphore = NULL; /*-----------------------------------------------------------*/ #ifdef PIC32_USE_ETHERNET /* PIC32 specific stuff */ /* */ /* MAC packet acknowledgment, once MAC is done with it */ static bool PIC32_MacPacketAcknowledge( TCPIP_MAC_PACKET * pPkt, const void * param ); /* allocates a MAC packet that holds a data buffer that can be used by both: */ /* - the FreeRTOSIP (NetworkBufferDescriptor_t->pucEthernetBuffer) */ /* - the Harmony MAC driver: TCPIP_MAC_PACKET->pDSeg->segLoad */ /* from the beginning of the buffer: */ /* - 4 bytes pointer to the network descriptor (FreeRTOS) */ /* - 4 bytes pointer to the MAC packet (pic32_NetworkInterface.c) */ /* - 2 bytes offset from the MAC packet (Harmony MAC driver: segLoadOffset) */ /* */ /* NOTE: segLoadLen should NOT include: */ /* - the TCPIP_MAC_FRAME_OFFSET (== ipBUFFER_PADDING which should be == 10!) */ /* - the sizeof(TCPIP_MAC_ETHERNET_HEADER) */ /* These are added by the MAC packet allocation! */ /* */ static uint8_t * PIC32_PktAlloc( uint16_t pktLen, uint16_t segLoadLen, TCPIP_MAC_PACKET_ACK_FUNC ackF, TCPIP_MAC_PACKET ** pPtrPkt ) { uint8_t * pBuff = 0; /* allocate standard packet */ TCPIP_MAC_PACKET * pPkt = TCPIP_PKT_PacketAlloc( pktLen, segLoadLen, 0 ); /* set the MAC packet pointer in the packet */ if( pPkt != 0 ) { pBuff = pPkt->pDSeg->segLoad; TCPIP_MAC_PACKET ** ppkt = ( TCPIP_MAC_PACKET ** ) ( pBuff - PIC32_BUFFER_PKT_PTR_OSSET ); configASSERT( ( ( uint32_t ) ppkt & ( sizeof( uint32_t ) - 1 ) ) == 0 ); *ppkt = pPkt; /* store the packet it comes from */ pPkt->ackFunc = ackF; pPkt->ackParam = 0; } if( pPtrPkt != 0 ) { *pPtrPkt = pPkt; } return pBuff; } /* standard PIC32 MAC allocation function for a MAC packet */ /* this packet saves room for the FreeRTOS network descriptor */ /* at the beginning of the data buffer */ /* see NetworkBufferAllocate */ /* Note: flags parameter is ignored since that's used in the Harmony stack only */ TCPIP_MAC_PACKET * PIC32_MacPacketAllocate( uint16_t pktLen, uint16_t segLoadLen, TCPIP_MAC_PACKET_FLAGS flags ) { TCPIP_MAC_PACKET * pPkt; PIC32_PktAlloc( pktLen, segLoadLen, 0, &pPkt ); return pPkt; } /* standard PIC32 MAC packet acknowledgment */ /* function called once MAC is done with it */ static bool PIC32_MacPacketAcknowledge( TCPIP_MAC_PACKET * pPkt, const void * param ) { configASSERT( ( pPkt != 0 ) ); TCPIP_PKT_PacketFree( pPkt ); return false; } /* associates the current MAC packet with a network descriptor */ /* mainly for RX packet */ void PIC32_MacAssociate( TCPIP_MAC_PACKET * pRxPkt, NetworkBufferDescriptor_t * pxBufferDescriptor, size_t pktLength ) { uint8_t * pPktBuff = pRxPkt->pDSeg->segLoad; pxBufferDescriptor->pucEthernetBuffer = pPktBuff; pxBufferDescriptor->xDataLength = pktLength; /* make sure this is a properly allocated packet */ TCPIP_MAC_PACKET ** ppkt = ( TCPIP_MAC_PACKET ** ) ( pPktBuff - PIC32_BUFFER_PKT_PTR_OSSET ); configASSERT( ( ( uint32_t ) ppkt & ( sizeof( uint32_t ) - 1 ) ) == 0 ); if( *ppkt != pRxPkt ) { configASSERT( false ); } /* set the proper descriptor info */ NetworkBufferDescriptor_t ** ppDcpt = ( NetworkBufferDescriptor_t ** ) ( pPktBuff - ipBUFFER_PADDING ); configASSERT( ( ( uint32_t ) ppDcpt & ( sizeof( uint32_t ) - 1 ) ) == 0 ); *ppDcpt = pxBufferDescriptor; } /* debug functionality */ void PIC32_MacPacketOrphan( TCPIP_MAC_PACKET * pPkt ) { TCPIP_PKT_PacketFree( pPkt ); configASSERT( false ); } /* FreeRTOS allocation functions */ /* allocates a buffer that can be used by both: */ /* - the FreeRTOSIP (NetworkBufferDescriptor_t->pucEthernetBuffer) */ /* - the Harmony MAC driver: TCPIP_MAC_PACKET */ /* See PIC32_PktAlloc for details */ /* */ /* NOTE: reqLength should NOT include the ipBUFFER_PADDING (which should be == 10!) */ /* or the sizeof(TCPIP_MAC_ETHERNET_HEADER) */ /* These are added by the MAC packet allocation! */ /* */ uint8_t * NetworkBufferAllocate( size_t reqLength ) { return PIC32_PktAlloc( sizeof( TCPIP_MAC_PACKET ), reqLength, PIC32_MacPacketAcknowledge, 0 ); } /* deallocates a network buffer previously allocated */ /* with NetworkBufferAllocate */ void NetworkBufferFree( uint8_t * pNetworkBuffer ) { if( pNetworkBuffer != 0 ) { TCPIP_MAC_PACKET ** ppkt = ( TCPIP_MAC_PACKET ** ) ( pNetworkBuffer - PIC32_BUFFER_PKT_PTR_OSSET ); configASSERT( ( ( uint32_t ) ppkt & ( sizeof( uint32_t ) - 1 ) ) == 0 ); TCPIP_MAC_PACKET * pPkt = *ppkt; configASSERT( ( pPkt != 0 ) ); if( pPkt->ackFunc != 0 ) { ( *pPkt->ackFunc )( pPkt, pPkt->ackParam ); } else { /* ??? */ PIC32_MacPacketOrphan( pPkt ); } } } #endif /* #ifdef PIC32_USE_ETHERNET */ /*-----------------------------------------------------------*/ BaseType_t xNetworkBuffersInitialise( void ) { BaseType_t xReturn, x; /* Only initialise the buffers and their associated kernel objects if they * have not been initialised before. */ if( xNetworkBufferSemaphore == NULL ) { xNetworkBufferSemaphore = xSemaphoreCreateCounting( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ); configASSERT( xNetworkBufferSemaphore ); if( xNetworkBufferSemaphore != NULL ) { #if ( configQUEUE_REGISTRY_SIZE > 0 ) { vQueueAddToRegistry( xNetworkBufferSemaphore, "NetBufSem" ); } #endif /* configQUEUE_REGISTRY_SIZE */ /* If the trace recorder code is included name the semaphore for viewing * in FreeRTOS+Trace. */ #if ( ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 ) { extern QueueHandle_t xNetworkEventQueue; vTraceSetQueueName( xNetworkEventQueue, "IPStackEvent" ); vTraceSetQueueName( xNetworkBufferSemaphore, "NetworkBufferCount" ); } #endif /* ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 */ vListInitialise( &xFreeBuffersList ); /* Initialise all the network buffers. No storage is allocated to * the buffers yet. */ for( x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ ) { /* Initialise and set the owner of the buffer list items. */ xNetworkBufferDescriptors[ x ].pucEthernetBuffer = NULL; vListInitialiseItem( &( xNetworkBufferDescriptors[ x ].xBufferListItem ) ); listSET_LIST_ITEM_OWNER( &( xNetworkBufferDescriptors[ x ].xBufferListItem ), &xNetworkBufferDescriptors[ x ] ); /* Currently, all buffers are available for use. */ vListInsert( &xFreeBuffersList, &( xNetworkBufferDescriptors[ x ].xBufferListItem ) ); } uxMinimumFreeNetworkBuffers = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; } } if( xNetworkBufferSemaphore == NULL ) { xReturn = pdFAIL; } else { xReturn = pdPASS; } return xReturn; } /*-----------------------------------------------------------*/ uint8_t * pucGetNetworkBuffer( size_t * pxRequestedSizeBytes ) { uint8_t * pucEthernetBuffer; size_t xSize = *pxRequestedSizeBytes; if( xSize < baMINIMAL_BUFFER_SIZE ) { /* Buffers must be at least large enough to hold a TCP-packet with * headers, or an ARP packet, in case TCP is not included. */ xSize = baMINIMAL_BUFFER_SIZE; } /* Round up xSize to the nearest multiple of N bytes, * where N equals 'sizeof( size_t )'. */ if( ( xSize & ( sizeof( size_t ) - 1u ) ) != 0u ) { xSize = ( xSize | ( sizeof( size_t ) - 1u ) ) + 1u; } *pxRequestedSizeBytes = xSize; /* Allocate a buffer large enough to store the requested Ethernet frame size * and a pointer to a network buffer structure (hence the addition of * ipBUFFER_PADDING bytes). */ #ifdef PIC32_USE_ETHERNET pucEthernetBuffer = NetworkBufferAllocate( xSize - sizeof( TCPIP_MAC_ETHERNET_HEADER ) ); #else pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xSize + ipBUFFER_PADDING ); #endif /* #ifdef PIC32_USE_ETHERNET */ configASSERT( pucEthernetBuffer ); if( pucEthernetBuffer != NULL ) { /* Enough space is left at the start of the buffer to place a pointer to * the network buffer structure that references this Ethernet buffer. * Return a pointer to the start of the Ethernet buffer itself. */ #ifndef PIC32_USE_ETHERNET pucEthernetBuffer += ipBUFFER_PADDING; #endif /* #ifndef PIC32_USE_ETHERNET */ } return pucEthernetBuffer; } /*-----------------------------------------------------------*/ void vReleaseNetworkBuffer( uint8_t * pucEthernetBuffer ) { /* There is space before the Ethernet buffer in which a pointer to the * network buffer that references this Ethernet buffer is stored. Remove the * space before freeing the buffer. */ #ifdef PIC32_USE_ETHERNET NetworkBufferFree( pucEthernetBuffer ); #else if( pucEthernetBuffer != NULL ) { pucEthernetBuffer -= ipBUFFER_PADDING; vPortFree( ( void * ) pucEthernetBuffer ); } #endif /* #ifdef PIC32_USE_ETHERNET */ } /*-----------------------------------------------------------*/ NetworkBufferDescriptor_t * pxGetNetworkBufferWithDescriptor( size_t xRequestedSizeBytes, TickType_t xBlockTimeTicks ) { NetworkBufferDescriptor_t * pxReturn = NULL; size_t uxCount; if( ( xRequestedSizeBytes != 0u ) && ( xRequestedSizeBytes < ( size_t ) baMINIMAL_BUFFER_SIZE ) ) { /* ARP packets can replace application packets, so the storage must be * at least large enough to hold an ARP. */ xRequestedSizeBytes = baMINIMAL_BUFFER_SIZE; } #ifdef PIC32_USE_ETHERNET if( xRequestedSizeBytes != 0u ) { #endif /* #ifdef PIC32_USE_ETHERNET */ xRequestedSizeBytes += 2u; if( ( xRequestedSizeBytes & ( sizeof( size_t ) - 1u ) ) != 0u ) { xRequestedSizeBytes = ( xRequestedSizeBytes | ( sizeof( size_t ) - 1u ) ) + 1u; } #ifdef PIC32_USE_ETHERNET } #endif /* #ifdef PIC32_USE_ETHERNET */ /* If there is a semaphore available, there is a network buffer available. */ if( xSemaphoreTake( xNetworkBufferSemaphore, xBlockTimeTicks ) == pdPASS ) { /* Protect the structure as it is accessed from tasks and interrupts. */ taskENTER_CRITICAL(); { pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList ); uxListRemove( &( pxReturn->xBufferListItem ) ); } taskEXIT_CRITICAL(); /* Reading UBaseType_t, no critical section needed. */ uxCount = listCURRENT_LIST_LENGTH( &xFreeBuffersList ); if( uxMinimumFreeNetworkBuffers > uxCount ) { uxMinimumFreeNetworkBuffers = uxCount; } /* Allocate storage of exactly the requested size to the buffer. */ configASSERT( pxReturn->pucEthernetBuffer == NULL ); if( xRequestedSizeBytes > 0 ) { /* Extra space is obtained so a pointer to the network buffer can * be stored at the beginning of the buffer. */ #ifdef PIC32_USE_ETHERNET pxReturn->pucEthernetBuffer = NetworkBufferAllocate( xRequestedSizeBytes - sizeof( TCPIP_MAC_ETHERNET_HEADER ) ); #else pxReturn->pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xRequestedSizeBytes + ipBUFFER_PADDING ); #endif /* #ifdef PIC32_USE_ETHERNET */ if( pxReturn->pucEthernetBuffer == NULL ) { /* The attempt to allocate storage for the buffer payload failed, * so the network buffer structure cannot be used and must be * released. */ vReleaseNetworkBufferAndDescriptor( pxReturn ); pxReturn = NULL; } else { /* Store a pointer to the network buffer structure in the * buffer storage area, then move the buffer pointer on past the * stored pointer so the pointer value is not overwritten by the * application when the buffer is used. */ #ifdef PIC32_USE_ETHERNET *( ( NetworkBufferDescriptor_t ** ) ( pxReturn->pucEthernetBuffer - ipBUFFER_PADDING ) ) = pxReturn; #else *( ( NetworkBufferDescriptor_t ** ) ( pxReturn->pucEthernetBuffer ) ) = pxReturn; pxReturn->pucEthernetBuffer += ipBUFFER_PADDING; #endif /* #ifdef PIC32_USE_ETHERNET */ /* Store the actual size of the allocated buffer, which may be * greater than the original requested size. */ pxReturn->xDataLength = xRequestedSizeBytes; #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) { /* make sure the buffer is not linked */ pxReturn->pxNextBuffer = NULL; } #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ } } else { /* A descriptor is being returned without an associated buffer being * allocated. */ } } if( pxReturn == NULL ) { iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER(); } else { iptraceNETWORK_BUFFER_OBTAINED( pxReturn ); } return pxReturn; } /*-----------------------------------------------------------*/ void vReleaseNetworkBufferAndDescriptor( NetworkBufferDescriptor_t * const pxNetworkBuffer ) { BaseType_t xListItemAlreadyInFreeList; /* Ensure the buffer is returned to the list of free buffers before the * counting semaphore is 'given' to say a buffer is available. Release the * storage allocated to the buffer payload. THIS FILE SHOULD NOT BE USED * IF THE PROJECT INCLUDES A MEMORY ALLOCATOR THAT WILL FRAGMENT THE HEAP * MEMORY. For example, heap_2 must not be used, heap_4 can be used. */ vReleaseNetworkBuffer( pxNetworkBuffer->pucEthernetBuffer ); pxNetworkBuffer->pucEthernetBuffer = NULL; taskENTER_CRITICAL(); { xListItemAlreadyInFreeList = listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) ); if( xListItemAlreadyInFreeList == pdFALSE ) { vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) ); } } taskEXIT_CRITICAL(); /* * Update the network state machine, unless the program fails to release its 'xNetworkBufferSemaphore'. * The program should only try to release its semaphore if 'xListItemAlreadyInFreeList' is false. */ if( xListItemAlreadyInFreeList == pdFALSE ) { if ( xSemaphoreGive( xNetworkBufferSemaphore ) == pdTRUE ) { iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer ); } } else { iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer ); } } /*-----------------------------------------------------------*/ /* * Returns the number of free network buffers */ UBaseType_t uxGetNumberOfFreeNetworkBuffers( void ) { return listCURRENT_LIST_LENGTH( &xFreeBuffersList ); } /*-----------------------------------------------------------*/ UBaseType_t uxGetMinimumFreeNetworkBuffers( void ) { return uxMinimumFreeNetworkBuffers; } /*-----------------------------------------------------------*/ NetworkBufferDescriptor_t * pxResizeNetworkBufferWithDescriptor( NetworkBufferDescriptor_t * pxNetworkBuffer, size_t xNewSizeBytes ) { size_t xOriginalLength; uint8_t * pucBuffer; #ifdef PIC32_USE_ETHERNET xOriginalLength = pxNetworkBuffer->xDataLength; #else xOriginalLength = pxNetworkBuffer->xDataLength + ipBUFFER_PADDING; xNewSizeBytes = xNewSizeBytes + ipBUFFER_PADDING; #endif /* #ifdef PIC32_USE_ETHERNET */ pucBuffer = pucGetNetworkBuffer( &( xNewSizeBytes ) ); if( pucBuffer == NULL ) { /* In case the allocation fails, return NULL. */ pxNetworkBuffer = NULL; } else { pxNetworkBuffer->xDataLength = xNewSizeBytes; if( xNewSizeBytes > xOriginalLength ) { xNewSizeBytes = xOriginalLength; } #ifdef PIC32_USE_ETHERNET memcpy( pucBuffer, pxNetworkBuffer->pucEthernetBuffer, xNewSizeBytes ); *( ( NetworkBufferDescriptor_t ** ) ( pucBuffer - ipBUFFER_PADDING ) ) = pxNetworkBuffer; #else memcpy( pucBuffer - ipBUFFER_PADDING, pxNetworkBuffer->pucEthernetBuffer - ipBUFFER_PADDING, xNewSizeBytes ); #endif /* #ifdef PIC32_USE_ETHERNET */ vReleaseNetworkBuffer( pxNetworkBuffer->pucEthernetBuffer ); pxNetworkBuffer->pucEthernetBuffer = pucBuffer; } return pxNetworkBuffer; }