Real time embedded FreeRTOS RSS feed 
Homepage FreeRTOS+ Products FreeRTOS Labs Support Forum Contact / Enquiries

Porting FreeRTOS+TCP to a Different Microcontroller

Introduction

The implementation of the network interface, and in particular the Ethernet MAC driver, are crucial to the data throughput that can be achieved when using the embedded TCP/IP stack. For high throughput the MAC driver must make efficient use of the DMA and avoid copying data where possible. End to end zero copy is possible with FreeRTOS+TCP for UDP packets, and an advanced interface exists that also allows zero copy for TCP packets. There are also advanced options available that allow packets to be filtered before they are even sent to the embedded TCP/IP stack, and packets that are received in quick successions can be sent to the embedded TCP/IP stack in one go rather than individually.

However, few applications actually require throughput to be maximised, especially on small MCUs, and the implementer may instead opt to sacrifice throughput in favour or simplicity.

This page describes how to interface FreeRTOS+TCP with a network driver, and provides an outline example of both a simple and a faster (but more complex) interface. It is very important to refer to these examples as they demonstrate how network buffers are freed after data has been transmitted.

The network driver port layers that ship with FreeRTOS+TCP are located in the FreeRTOS-Plus-TCP/portable/NetworkInterface directory of the FreeRTOS+TCP download. Note however that these drivers have been created in order to allow testing of the embedded TCP/IP stack, and are not intended to represent optimised examples.


Summary Bullet Points

porting the free TCP IP stack to a different MCU
The network interface port layer sits
between the IP stack and the embedded
Ethernet hardware drivers
  • Each MCU to which FreeRTOS+TCP is ported requires an Ethernet MAC driver. It is assumed this already exists and is known to work.

  • FreeRTOS+TCP is ported to new hardware by providing a 'network interface port layer' that provides the interface between the embedded TCP/IP stack and the Ethernet MAC driver. See the image on the right.

  • The network interface port layer must provide a function called xNetworkInterfaceInitialise() that initialises the MAC driver.

  • The network interface port layer must provide a function called xNetworkInterfaceOutput() that sends data received from the embedded TCP/IP stack to the Ethernet MAC driver for transmission.

  • The network interface port layer must send packets received from the Ethernet MAC driver to the TCP/IP stack by calling xSendEventStructToIPTask().

  • Only if BufferAllocation_1.c is used for buffer allocation, the network interface port layer must statically allocate network buffers and provide a function called vNetworkInterfaceAllocateRAMToBuffers() to assign the statically allocated network buffers to network buffer descriptors.

  • Network buffers (the buffer in which the actual data is stored) are referenced using NetworkBufferDescriptor_t structures.

  • The embedded TCP/IP stack provides a set of porting utility functions to allow the port layer to perform actions such as obtaining and freeing network buffers.


This page provides more information on each of these steps, and provides two examples. The first example demonstrates how to implement a simple (but slower) driver. The second example demonstrates how to implement a more sophisticated (and faster) driver. It is very important to refer to these examples as they demonstrate how network buffers are freed after data has been transmitted.




Network Buffers and Network Buffer Descriptors

Ethernet (or other network) frames are stored in network buffers. A network buffer descriptor (a variable of type NetworkBufferDescriptor_t) is used to describe a network buffer, and pass network buffers between the TCP/IP stack and the network drivers.


Embedded network buffers

pucEthernetBuffer points to the start of the network buffer.
xDataLength holds the size of the buffer in bytes, excluding the Ethernet CRC bytes.


Only the following two members of the NetworkBufferDescriptor_t structure should be accessed:

  1. uint8_t *pucEthernetBuffer;

    pucEthernetBuffer points to the start of the network buffer.

  2. size_t xDataLength

    xDataLength holds the size of the network buffer pointed to by pucEthernetBuffer. The size is specified in bytes but the length excludes the bytes that hold the Ethernet frame's CRC byte.

pucGetNetworkBuffer() is used to obtain just the network buffer itself, and is normally only used in zero copy drivers.

pxGetNetworkBufferWithDescriptor() is used to obtain both a network buffer and a network buffer descriptor at the same time.


Function That Must be Implemented by the Port Layer

xNetworkInterfaceInitialise()

xNetworkInterfaceInitialise() must prepare the Ethernet MAC to send and receive data. In most cases this will just involve calling whichever initialise function is provided with the Ethernet MAC peripheral drivers - which will in turn ensure the MAC hardware is enabled and clocked, as well as configure the MAC peripheral's DMA descriptors.

xNetworkInterfaceInitialise() does not take any parameters, returns pdPASS if the initialisation was successful, and returns pdFAIL if the initialisation fails.


BaseType_t xNetworkInterfaceInitialise( void );
								
The xNetworkInterfaceInitialise() function prototype


xNetworkInterfaceOutput()

The TCP/IP stack calls xNetworkInterfaceOutput() whenever a network buffer is ready to be transmitted.

The buffer to transmit is described by the descriptor passed into the function using the function's pxDescriptor parameter. If xReleaseAfterSend does not equal pdFALSE then both the buffer and the buffer's descriptor must be released (returned) back to the embedded TCP/IP stack by the driver code when they are no longer required. If xReleaseAfterSend is pdFALSE then both the network buffer and the buffer's descriptor will be released by the TCP/IP stack itself (in which case the driver does not need to release them).

Note that, at the time of writing, the value returned from xNetworkInterfaceOutput() is ignored. The embedded TCP/IP stack will NOT call xNetworkInterfaceOutput() for the same network buffer twice, even if the first call to xNetworkInterfaceOutput() could not send the network buffer onto the network.

Basic and more advanced examples are provided below, and the FreeRTOS-Plus-TCP/portable/NetworkInterface directory of the FreeRTOS+TCP download contained examples that can be referenced. Note however that the examples in the download may not be optimised.


BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor,
                                    BaseType_t xReleaseAfterSend );
								
The xNetworkInterfaceOutput() function prototype


vNetworkInterfaceAllocateRAMToBuffers()
only when BufferAllocation_1.c is used

BufferAllocation_1.c uses pre-allocated network buffers that are normally statically allocated at compile time.

The number of network buffers that must be allocated is set by the ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS definition in FreeRTOSIPConfig.h, and the size of each buffer must be ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING ). ipTOTAL_ETHERNET_FRAME_SIZE is calculated automatically from the value of ipconfigNETWORK_MTU, and ipBUFFER_PADDING is calculated automatically from ipconfigBUFFER_PADDING.

Networking hardware can impose strict alignment requirements on the allocated buffers, so it is recommended that the buffers are allocated in the embedded Ethernet driver itself - that way the buffer's alignment can always be made to match the hardware's requirements.

The embedded TCP/IP stack allocates the network buffer descriptors, but does not know anything about the alignment of the network buffers themselves. Therefore the embedded Ethernet driver must also provide a function called vNetworkInterfaceAllocateRAMToBuffers() that allocates a statically declared buffer to each descriptor. Note that ipBUFFER_PADDING bytes at the beginning of the buffer are left for use by the embedded TCP/IP stack itself. See the example below.


void vNetworkInterfaceAllocateRAMToBuffers(
       NetworkBufferDescriptor_t xDescriptors[ ipconfigNUM_NETWORK_BUFFERS ] );
								
The vNetworkInterfaceAllocateRAMToBuffers() function prototype



/* First statically allocate the buffers, ensuring an additional ipBUFFER_PADDING
bytes are allocated to each buffer.  This example makes no effort to align
the start of the buffers, but most hardware will have an alignment requirement.
If an alignment is required then the size of each buffer must be adjusted to
ensure it also ends on an alignment boundary.  Below shows an example assuming
the buffers must also end on an 8-byte boundary. */
#define BUFFER_SIZE ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
#define BUFFER_SIZE_ROUNDED_UP ( ( BUFFER_SIZE + 7 ) & ~0x07UL )
static uint8_t ucBuffers[ ipconfigNUM_NETWORK_BUFFERS ][ BUFFER_SIZE_ROUNDED_UP ];

/* Next provide the vNetworkInterfaceAllocateRAMToBuffers() function, which
simply fills in the pucEthernetBuffer member of each descriptor. */
void vNetworkInterfaceAllocateRAMToBuffers(
    NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFERS ] )
{
BaseType_t x;

    for( x = 0; x < ipconfigNUM_NETWORK_BUFFERS; x++ )
    {
        /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
        beginning of the allocated buffer. */
        pxNetworkBuffers[ x ].pucEthernetBuffer = &( ucBuffers[ x ][ ipBUFFER_PADDING ] );

        /* The following line is also required, but will not be required in
        future versions. */
        *( ( uint32_t * ) &ucBuffers[ x ][ 0 ] ) = ( uint32_t ) &( pxNetworkBuffers[ x ] );
    }
}
								
An example implementation of vNetworkBufferInterfaceAllocateRAMToBuffers().


Functions Provided by the TCP/IP Stack For Use By The Port Layer

The port layer can use the following function:
  • pxGetNetworkBufferWithDescriptor() -
    Obtains both a network buffer and a descriptor that describes the network buffer. This function can also be used to obtain just a network buffer descriptor - which can be useful when implementing zero copy drivers.

  • vReleaseNetworkBufferAndDescriptor() -
    Releases (returns to the embedded TCP/IP stack) a network buffer descriptor, and the network buffer referenced by the descriptor (if any).

  • pxNetworkBufferGetFromISR() -
    [not available when BufferAllocation_2.c is used]

  • vNetworkBufferReleaseFromISR() -
    [not available when BufferAllocation_2.c is used]

  • eConsiderFrameForProcessing() -
    Used to determine if data received from the network needs to be passed to the embedded TCP/IP stack. Ideally this function would be called from the network interrupt to allow received packets to be discarded at the earliest possible opportunity.

  • xSendEventStructToIPTask() -
    xSendEventStructToIPTask() is a function used by the embedded TCP/IP stack itself to send various events to the RTOS task that is running the embedded TCP/IP stack. The port layer uses the function with eNetworkRxEvent events to pass received data into the stack for processing.

  • pucGetNetworkBuffer() -
    Obtains just a network buffer, without a network buffer descriptor. This function is normally only used in zero copy interfaces to allocate buffers to DMA descriptors.

  • vReleaseNetworkBuffer() -
    Releases (returns to the embedded TCP/IP stack) a network buffer by itself - without a network buffer descriptor. This function is normally only used in zero copy interfaces where network buffers were allocated to DMA descriptors.


Receiving Data

The Ethernet MAC driver will place received Ethernet frames into a buffer. The port layer has to:

  1. Determine if the received data needs to be sent to the embedded TCP/IP stack. Ideally this would be done in the receive interrupt itself to allow unnecessary packets to be dropped at the earliest possible time.

  2. Allocate a network buffer descriptor.

  3. Set the xDataLength and pucEthernetBuffer members of the allocated descriptor accordingly (see both the basic and zero copy examples later on this page).

  4. Call xSendEventStructToIPTask() to send the network buffer descriptor into the embedded TCP/IP stack for processing (see both the basic and zero copy examples later on this page).


typedef struct IP_TASK_COMMANDS
{
    /* Specifies the type of event being sent to the RTOS task and must be set to
    eNetworkRxEvent to signify a receive event. */
    eIPEvent_t eEventType;

    /* Points to additional data about the event.  In this case set pvData to the
    the address of the network buffer descriptor. */
    void *pvData;
} IPStackEvent_t;
								
The IPStackEvent_t type



/* The timeout is specified in RTOS ticks.  Returns pdTRUE if the message was
sent successfully, otherwise return pdFALSE. */
BaseType_t xSendEventStructToIPTask( const IPStackEvent_t *pxEvent, TickType_t xTimeout )
								
The xSendEventStructToIPTask() function prototype


Basic and more advanced examples are provided below. The network driver port layers that ship with FreeRTOS+TCP (which are not necessarily optimised) can be found in the FreeRTOS-Plus-TCP/portable/NetworkInterface directory.


Network Interface Port Layer Examples

1. Example of a Basic Network Interface Port Layer

Simple network interfaces can be created by copying Ethernet frames between buffers allocated by the Ethernet MAC driver libraries and buffers allocated by the port layer. [A more efficient zero copy alternative is provided after the simple example.]

Example implementation of xNetworkInterfaceInitialise() for a basic port layer


BaseType_t xNetworkInterfaceInitialise( void )
{
BaseType_t xReturn;

    /*
     * Perform the hardware specific network initialisation here.  Typically
     * that will involve using the Ethernet driver library to initialise the
     * Ethernet (or other network) hardware, initialise DMA descriptors, and
     * perform a PHY auto-negotiation to obtain a network link.
     *
     * This example assumes InitialiseNetwork() is an Ethernet peripheral driver
     * library function that returns 0 if the initialisation fails.
     */
    if( InitialiseNetwork() == 0 )
    {
        xReturn = pdFAIL;
    }
    else
    {
        xReturn = pdPASS;
    }

    return xReturn;
}
								
xNetworkInterfaceInitialise() is hardware specific, therefore this example describes what needs to
be done without showing any detail


Example implementation of xNetworkInterfaceOutput() for a basic port layer


BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor,
                                    BaseType_t xReleaseAfterSend )
{
    /* Simple network interfaces (as opposed to more efficient zero copy network
    interfaces) just use Ethernet peripheral driver library functions to copy
    data from the FreeRTOS+TCP buffer into the peripheral driver's own buffer.
    This example assumes SendData() is a peripheral driver library function that
    takes a pointer to the start of the data to be sent and the length of the
    data to be sent as two separate parameters.  The start of the data is located
    by pxDescriptor->pucEthernetBuffer.  The length of the data is located
    by pxDescriptor->xDataLength. */
    SendData( pxDescriptor->pucBuffer, pxDescriptor->xDataLength );

    /* Call the standard trace macro to log the send event. */
    iptraceNETWORK_INTERFACE_TRANSMIT();

    if( xReleaseAfterSend != pdFALSE )
    {
        /* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet
        buffer.  The Ethernet buffer is therefore no longer needed, and must be
        freed for re-use. */
        vReleaseNetworkBufferAndDescriptor( pxDescriptor );
    }

    return pdTRUE;
}
								
Example implementation of xNetworkInterfaceOutput() suitable for a
simple (rather than zero copy) network interface implementation


Example of passing received data to the TCP/IP in a basic port layer

When a packet is received from the Ethernet (or other network) driver the port layer must use a NetworkBufferDescriptor_t structure to describe the packet, then call xSendEventStructToIPTask() to send the NetworkBufferDescriptor_t structure to the embedded TCP/IP stack.

NOTE 1: If BufferAllocation_2.c is used then network buffer descriptors and Ethernet buffers cannot be allocated from inside an interrupt service routine (ISR). In this case the Ethernet MAC receive interrupt can defer the receive processing to a task. This is demonstrated below.

NOTE 2: There are numerous advanced techniques that can be employed to minimise the amount of data sent from the port layer into the embedded TCP/IP stack. For example, eConsiderFrameForProcessing() can be called to determine if the received Ethernet frame needs to be sent to the embedded TCP/IP stack at all, and Ethernet frames that are received in quick succession can be sent to the embedded TCP/IP stack in one go. See the Hardware and Driver Specific Settings section of the FreeRTOS+TCP configuration page for more information.


/* The deferred interrupt handler is a standard RTOS task.  FreeRTOS's centralised
deferred interrupt handling capabilities can also be used. */
static void prvEMACDeferredInterruptHandlerTask( void *pvParameters )
{
NetworkBufferDescriptor_t *pxBufferDescriptor;
size_t xBytesReceived;
/* Used to indicate that xSendEventStructToIPTask() is being called because
of an Ethernet receive event. */
IPStackEvent_t xRxEvent;

    for( ;; )
    {
        /* Wait for the Ethernet MAC interrupt to indicate that another packet
        has been received.  The task notification is used in a similar way to a
        counting semaphore to count Rx events, but is a lot more efficient than
        a semaphore. */
        ulTaskNotifyTake( pdFALSE, portMAX_DELAY );

        /* See how much data was received.  Here it is assumed ReceiveSize() is
        a peripheral driver function that returns the number of bytes in the
        received Ethernet frame. */
        xBytesReceived = ReceiveSize();

        if( xBytesReceived > 0 )
        {
            /* Allocate a network buffer descriptor that points to a buffer
            large enough to hold the received frame.  As this is the simple
            rather than efficient example the received data will just be copied
            into this buffer. */
            pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 );

            if( pxBufferDescriptor != NULL )
            {
                /* pxBufferDescriptor->pucEthernetBuffer now points to an Ethernet
                buffer large enough to hold the received data.  Copy the
                received data into pcNetworkBuffer->pucEthernetBuffer.  Here it
                is assumed ReceiveData() is a peripheral driver function that
                copies the received data into a buffer passed in as the function's
                parameter.  Remember! While is is a simple robust technique -
                it is not efficient.  An example that uses a zero copy technique
                is provided further down this page. */
                ReceiveData( pxBufferDescriptor->pucEthernetBuffer );
                pxBufferDescriptor->xDataLength = xBytesReceived;

                /* See if the data contained in the received Ethernet frame needs
                to be processed.  NOTE! It is preferable to do this in
                the interrupt service routine itself, which would remove the need
                to unblock this task for packets that don't need processing. */
                if( eConsiderFrameForProcessing( pxBufferDescriptor->pucEthernetBuffer )
                                                                      == eProcessBuffer )
                {
                    /* The event about to be sent to the TCP/IP is an Rx event. */
                    xRxEvent.eEventType = eNetworkRxEvent;

                    /* pvData is used to point to the network buffer descriptor that
                    now references the received data. */
                    xRxEvent.pvData = ( void * ) pxBufferDescriptor;

                    /* Send the data to the TCP/IP stack. */
                    if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
                    {
                        /* The buffer could not be sent to the IP task so the buffer
                        must be released. */
                        vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );

                        /* Make a call to the standard trace macro to log the
                        occurrence. */
                        iptraceETHERNET_RX_EVENT_LOST();
                    }
                    else
                    {
                        /* The message was successfully sent to the TCP/IP stack.
                        Call the standard trace macro to log the occurrence. */
                        iptraceNETWORK_INTERFACE_RECEIVE();
                    }
                }
                else
                {
                    /* The Ethernet frame can be dropped, but the Ethernet buffer
                    must be released. */
                    vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
                }
            }
            else
            {
                /* The event was lost because a network buffer was not available.
                Call the standard trace macro to log the occurrence. */
                iptraceETHERNET_RX_EVENT_LOST();
            }
        }
    }
}
								
An example of a simple (rather than more efficient zero copy) receive handler


2. Example of a More Efficient Network Interface Port Layer

It is intended that this section is read after the section that describes how to create a simple network interface port layer.

Simple network interfaces copy Ethernet frames between buffers used and managed by the TCP/IP stack and buffers used and managed by the Ethernet (or other network) MAC drivers. Copying data between buffers makes the driver's implementation simple, but is inefficient.

Zero copy network interfaces do not copy data between buffers, but instead pass references to buffers between the TCP/IP stack and the Ethernet MAC drivers.

Zero copy interfaces are more complex, and can rarely be created without editing the Ethernet MAC drivers themselves.

If transmission is performed using zero copy then it is necessary to set ipconfigZERO_COPY_TX_DRIVER to 1.

Most Ethernet hardware will use DMA (Direct Memory Access) to move frames between the Ethernet hardware and pre-allocated RAM buffers. Normally the pre-allocated memory buffers are referenced using a set of DMA descriptors. DMA descriptors are normally chained - each descriptor points to the next in the chain, with the last in the chain pointing back to the first.

Chained embedded Ethernet buffers

Chained DMA descriptors


Example implementation of xNetworkInterfaceInitialise() for a zero copy port layer

xNetworkInterfaceInitialise() must use pucGetNetworkBuffer() to obtain the pointers to which the receive DMA descriptors point. It is not necessary to allocate any buffers for the transmit DMA descriptors - the buffers will be passed in (by reference) as data becomes available to send.

Zero copy Ethernet driver initialisation

The DMA Rx descriptors are initialised to point to buffers that were allocated by pucGetNetworkBuffer().
The DMA Tx descriptors do not point to any buffers after they have been initialised.


Example implementation of xNetworkInterfaceOutput() for a zero copy layer

xNetworkInterfaceOutput() does not copy the frame being transmitted to a buffer that is being managed by the MAC driver (it can't because the DMA's Tx descriptors are not pointing to any buffers) but instead updates the next DMA Tx descriptor so the descriptor points to the buffer that already contains the data.

NOTE: The Ethernet buffer must be released after the data it contains has been transmitted. If BufferAllocation_2.c is used the Ethernet buffer cannot be released from the Ethernet Transmit End interrupt, so must be released by the xNetworkInterfaceOutput() function the next time the same DMA descriptor is used. Often only one or two descriptors are used for transmitting data anyway, so this does not waste too much RAM.


BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor,
                                    BaseType_t xReleaseAfterSend )
{
DMADescriptor_t *pxDMATxDescriptor;

    /* This example assumes GetNextTxDescriptor() is an Ethernet MAC driver library
    function that returns a pointer to a DMA descriptor of type DMADescriptor_t. */
    pxDMATxDescriptor = GetNextTxDescriptor();

    /* Further, this example assumes the DMADescriptor_t type has a member
    called pucEthernetBuffer that points to the buffer the DMA will transmit, and
    a member called xDataLength that holds the length of the data the DMA will
    transmit.  If BufferAllocation_2.c is being used then the DMA descriptor may
    still be pointing to the buffer it last transmitted.  If this is the case
    then the old buffer must be released (returned to the TCP/IP stack) before
    descriptor is updated to point to the new data waiting to be transmitted. */
    if( pxDMATxDescriptor->pucEthernetBuffer != NULL )
    {
        /* Note this is releasing just an Ethernet buffer, not a network buffer
        descriptor as the descriptor has already been released. */
        vReleaseNetworkBuffer( pxDMATxDescriptor->pucEthernetBuffer );
    }

    /* Configure the DMA descriptor to send the data referenced by the network buffer
    descriptor.  This example assumes SendData() is an Ethernet peripheral driver
    function. */
    pxDMATxDescriptor->pucEthernetBuffer = pxDescriptor->pucEthernetBuffer;
    pxDMATxDescriptor->xDataLength = pxDescriptor->xDataLength;
    SendData( pxDMATxDescriptor );

    /* Call the standard trace macro to log the send event. */
    iptraceNETWORK_INTERFACE_TRANSMIT();

    /* The network buffer descriptor must now be returned to the TCP/IP stack, but
    the Ethernet buffer referenced by the network buffer descriptor is still in
    use by the DMA.  Remove the reference to the Ethernet buffer from the network
    buffer descriptor so releasing the network buffer descriptor does not result
    in the Ethernet buffer also being released.  xReleaseAfterSend() should never
    equal pdFALSE when  ipconfigZERO_COPY_TX_DRIVER is set to 1 (as it should be
    if data is transmitted using a zero copy driver.*/
    if( xReleaseAfterSend != pdFALSE )
    {
        pxDescriptor->pucEthernetBuffer = NULL;
        vReleaseNetworkBufferAndDescriptor( pxDescriptor );
    }

    return pdTRUE;
}
								
An example zero copy implementation of xNetworkInterfaceOutput()


Receiving Data Using Zero-Copy

If reception is performed using zero copy then it is necessary to set ipconfigZERO_COPY_RX_DRIVER to 1.

The receive DMA will place received frames into the buffer pointed to by the the receive DMA descriptor. The buffer was allocated using a call to pucGetNetworkBuffer(), which allows it to be referenced from a network buffer descriptor, and therefore passed by reference directly into the TCP/IP stack. A new empty network buffer is then allocated, and the receive DMA descriptor is updated to point to the empty buffer ready to receive the next packet.

All the notes regarding the implementation of the simple receive handler (including advanced features to improve efficiency) apply to the zero copy receive handler and are not repeated here.


/* The deferred interrupt handler is a standard RTOS task.  FreeRTOS's centralised
deferred interrupt handling capabilities can also be used - however for additional
speed use BufferAllocation_1.c to perform the entire operation in the interrupt
handler. */
static void prvEMACDeferredInterruptHandlerTask( void *pvParameters )
{
NetworkBufferDescriptor_t *pxDescriptor;
size_t xBytesReceived;
DMADescriptor_t *pxDMARxDescriptor;
uint8_t *pucTemp;
/* Used to indicate that xSendEventStructToIPTask() is being called because
of an Ethernet receive event. */
IPStackEvent_t xRxEvent;

  for( ;; )
  {
    /* Wait for the Ethernet MAC interrupt to indicate that another packet
    has been received.  The task notification is used in a similar way to a
    counting semaphore to count Rx events, but is a lot more efficient than
    a semaphore. */
    ulTaskNotifyTake( pdFALSE, portMAX_DELAY );

    /* This example assumes GetNextRxDescriptor() is an Ethernet MAC driver
    library function that returns a pointer to the DMA descriptor (of type
    DMADescriptor_t again) that references the Ethernet buffer containing the
    received data. */
    pxDMARxDescriptor = GetNextRxDescriptor();

    /* Allocate a new network buffer descriptor that references an Ethernet
    frame large enough to hold the maximum network packet size (as defined
    in the FreeRTOSIPConfig.h header file). */
    pxDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 );

    /* Copy the pointer to the newly allocated Ethernet frame to a temporary
    variable. */
    pucTemp = pxDescriptor->pucEthernetBuffer;

    /* This example assumes that the DMADescriptor_t type has a member
    called pucEthernetBuffer that points to the Ethernet buffer containing
    the received data, and a member called xDataLength that holds the length
    of the received data.  Update the newly allocated network buffer descriptor
    to point to the Ethernet buffer that contains the received data. */
    pxDescriptor->pucEthernetBuffer = pxDMARxDescriptor->pucEthernetBuffer;
    pxDescriptor->xDataLength = pxDMARxDescriptor->xDataLength;

    /* Update the Ethernet Rx DMA descriptor to point to the newly allocated
    Ethernet buffer. */
    pxDMARxDescriptor->puxEthernetBuffer = pucTemp;

    /* A pointer to the descriptor is stored at the front of the buffer, so
    swap these too. */
    *( ( NetworkBufferDescriptor_t ** )
       ( pxDescriptor->pucEthernetBuffer - ipBUFFER_PADDING ) ) = pxDescriptor;

    *( ( NetworkBufferDescriptor_t ** )
       ( pxDMARxDescriptor->pucEthernetBuffer - ipBUFFER_PADDING ) ) = pxDMARxDescriptor;

    /*
     * The network buffer descriptor now points to the Ethernet buffer that
     * contains the received data, and the Ethernet DMA descriptor now points
     * to a newly allocated (and empty) Ethernet buffer ready to receive more
     * data.  No data was copied.  Only pointers to data were swapped.
     *
     * THE REST OF THE RECEIVE HANDLER FUNCTION FOLLOWS THE EXAMPLE PROVIDED
     * FOR THE SIMPLE ETHERNET INTERFACE IMPLEMENTATION, whereby the network
     * buffer descriptor is sent to the TCP/IP on the network event queue.
     */



    /* See if the data contained in the received Ethernet frame needs
    to be processed.  NOTE! It might be possible to do this in
    the interrupt service routine itself, which would remove the need
    to unblock this task for packets that don't need processing. */
    if( eConsiderFrameForProcessing( pxDescriptor->pucEthernetBuffer )
                                == eProcessBuffer )
    {
      /* The event about to be sent to the TCP/IP is an Rx event. */
      xRxEvent.eEventType = eNetworkRxEvent;

      /* pvData is used to point to the network buffer descriptor that
      references the received data. */
      xRxEvent.pvData = ( void * ) pxDescriptor;

      /* Send the data to the TCP/IP stack. */
      if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
      {
        /* The buffer could not be sent to the IP task so the buffer
        must be released. */
        vReleaseNetworkBufferAndDescriptor( pxDescriptor );

        /* Make a call to the standard trace macro to log the
        occurrence. */
        iptraceETHERNET_RX_EVENT_LOST();
      }
      else
      {
        /* The message was successfully sent to the TCP/IP stack.
        Call the standard trace macro to log the occurrence. */
        iptraceNETWORK_INTERFACE_RECEIVE();
      }
    }
    else
    {
      /* The Ethernet frame can be dropped, but the Ethernet buffer
      must be released. */
      vReleaseNetworkBufferAndDescriptor( pxDescriptor );
    }
  }
}
        						
An example of a zero copy receive handler function


[ Back to the top ]    [ About FreeRTOS ]    [ Privacy ]    [ FreeRTOS Labs Sitemap ]    [ Main FreeRTOS Sitemap ]    [ ]




Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.