3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 * https://www.FreeRTOS.org
23 * https://github.com/FreeRTOS
27 /* FreeRTOS includes. */
33 #include "xemaclite.h"
40 #include "lwip/pbuf.h"
42 #include <lwip/stats.h>
43 #include <lwip/snmp.h>
44 #include "netif/etharp.h"
46 /* Define those to better describe your network interface. */
50 /* When a packet is ready to be sent, if it cannot be sent immediately then
51 * the task performing the transmit will block for netifTX_BUFFER_FREE_WAIT
52 * milliseconds. It will do this a maximum of netifMAX_TX_ATTEMPTS before
55 #define netifTX_BUFFER_FREE_WAIT ( ( TickType_t ) 2UL / portTICK_PERIOD_MS )
56 #define netifMAX_TX_ATTEMPTS ( 5 )
58 #define netifMAX_MTU 1500
62 struct eth_addr *ethaddr;
63 /* Add whatever per-interface state that is needed here. */
67 * Copy the received data into a pbuf.
69 static struct pbuf *prvLowLevelInput( const unsigned char * const pucInputData, unsigned short usDataLength );
72 * Send data from a pbuf to the hardware.
74 static err_t prvLowLevelOutput( struct netif *pxNetIf, struct pbuf *p );
77 * Perform any hardware and/or driver initialisation necessary.
79 static void prvLowLevelInit( struct netif *pxNetIf );
82 * Functions that get registered as the Rx and Tx interrupt handers
85 static void prvRxHandler( void *pvNetIf );
86 static void prvTxHandler( void *pvUnused );
89 /*-----------------------------------------------------------*/
91 /* The instance of the xEmacLite IP being used in this driver. */
92 static XEmacLite xEMACInstance;
94 /*-----------------------------------------------------------*/
97 * In this function, the hardware should be initialized.
98 * Called from ethernetif_init().
100 * @param pxNetIf the already initialized lwip network interface structure
101 * for this etherpxNetIf
103 static void prvLowLevelInit( struct netif *pxNetIf )
105 portBASE_TYPE xStatus;
106 extern void vInitialisePHY( XEmacLite *xemaclitep );
107 unsigned portBASE_TYPE uxOriginalPriority;
109 /* Hardware initialisation can take some time, so temporarily lower the
110 task priority to ensure other functionality is not adversely effected.
111 The priority will get raised again before this function exits. */
112 uxOriginalPriority = uxTaskPriorityGet( NULL );
113 vTaskPrioritySet( NULL, tskIDLE_PRIORITY );
115 /* set MAC hardware address length */
116 pxNetIf->hwaddr_len = ETHARP_HWADDR_LEN;
118 /* set MAC hardware address */
119 pxNetIf->hwaddr[ 0 ] = configMAC_ADDR0;
120 pxNetIf->hwaddr[ 1 ] = configMAC_ADDR1;
121 pxNetIf->hwaddr[ 2 ] = configMAC_ADDR2;
122 pxNetIf->hwaddr[ 3 ] = configMAC_ADDR3;
123 pxNetIf->hwaddr[ 4 ] = configMAC_ADDR4;
124 pxNetIf->hwaddr[ 5 ] = configMAC_ADDR5;
126 /* device capabilities */
127 pxNetIf->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
129 /* maximum transfer unit */
130 pxNetIf->mtu = netifMAX_MTU;
132 /* Broadcast capability */
133 pxNetIf->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
135 /* Initialize the mac */
136 xStatus = XEmacLite_Initialize( &xEMACInstance, XPAR_EMACLITE_0_DEVICE_ID );
138 if( xStatus == XST_SUCCESS )
140 /* Set mac address */
141 XEmacLite_SetMacAddress( &xEMACInstance, ( Xuint8* )( pxNetIf->hwaddr ) );
143 /* Flush any frames already received */
144 XEmacLite_FlushReceive( &xEMACInstance );
146 /* Set Rx, Tx interrupt handlers */
147 XEmacLite_SetRecvHandler( &xEMACInstance, ( void * ) pxNetIf, prvRxHandler );
148 XEmacLite_SetSendHandler( &xEMACInstance, NULL, prvTxHandler );
150 /* Enable Rx, Tx interrupts */
151 XEmacLite_EnableInterrupts( &xEMACInstance );
153 /* Install the standard Xilinx library interrupt handler itself.
154 *NOTE* The xPortInstallInterruptHandler() API function must be used
156 xStatus = xPortInstallInterruptHandler( XPAR_INTC_0_EMACLITE_0_VEC_ID, ( XInterruptHandler ) XEmacLite_InterruptHandler, &xEMACInstance );
158 vInitialisePHY( &xEMACInstance );
160 /* Enable the interrupt in the interrupt controller.
161 *NOTE* The vPortEnableInterrupt() API function must be used for this
163 vPortEnableInterrupt( XPAR_INTC_0_EMACLITE_0_VEC_ID );
166 /* Reset the task priority back to its original value. */
167 vTaskPrioritySet( NULL, uxOriginalPriority );
169 configASSERT( xStatus == pdPASS );
173 * This function should do the actual transmission of the packet. The packet is
174 * contained in the pbuf that is passed to the function. This pbuf
177 * @param pxNetIf the lwip network interface structure for this etherpxNetIf
178 * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
179 * @return ERR_OK if the packet could be sent
180 * an err_t value if the packet couldn't be sent
182 * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
183 * strange results. You might consider waiting for space in the DMA queue
184 * to become available since the stack doesn't retry to send a packet
185 * dropped because of memory failure (except for the TCP timers).
188 static err_t prvLowLevelOutput( struct netif *pxNetIf, struct pbuf *p )
191 /* This is taken from lwIP example code and therefore does not conform
192 to the FreeRTOS coding standard. */
195 static unsigned char ucBuffer[ 1520 ] __attribute__((aligned(32)));
196 unsigned char *pucBuffer = ucBuffer;
197 unsigned char *pucChar;
198 struct eth_hdr *pxHeader;
199 u16_t usTotalLength = p->tot_len - ETH_PAD_SIZE;
200 err_t xReturn = ERR_OK;
205 #if defined(LWIP_DEBUG) && LWIP_NETIF_TX_SINGLE_PBUF
206 LWIP_ASSERT("p->next == NULL && p->len == p->tot_len", p->next == NULL && p->len == p->tot_len);
209 /* Initiate transfer. */
210 if( p->len == p->tot_len )
212 /* No pbuf chain, don't have to copy -> faster. */
213 pucBuffer = &( ( unsigned char * ) p->payload )[ ETH_PAD_SIZE ];
217 /* pbuf chain, copy into contiguous ucBuffer. */
218 if( p->tot_len >= sizeof( ucBuffer ) )
220 LINK_STATS_INC( link.lenerr );
221 LINK_STATS_INC( link.drop );
222 snmp_inc_ifoutdiscards( pxNetIf );
229 for( q = p; q != NULL; q = q->next )
231 /* Send the data from the pbuf to the interface, one pbuf at a
232 time. The size of the data in each pbuf is kept in the ->len
234 /* send data from(q->payload, q->len); */
235 LWIP_DEBUGF( NETIF_DEBUG, ( "NETIF: send pucChar %p q->payload %p q->len %i q->next %p\n", pucChar, q->payload, ( int ) q->len, ( void* ) q->next ) );
238 memcpy( pucChar, &( ( char * ) q->payload )[ ETH_PAD_SIZE ], q->len - ETH_PAD_SIZE );
239 pucChar += q->len - ETH_PAD_SIZE;
243 memcpy( pucChar, q->payload, q->len );
250 if( xReturn == ERR_OK )
252 for( x = 0; x < netifMAX_TX_ATTEMPTS; x++ )
254 xReturn = XEmacLite_Send( &xEMACInstance, pucBuffer, ( int ) usTotalLength );
255 if( xReturn == XST_SUCCESS )
261 vTaskDelay( netifTX_BUFFER_FREE_WAIT );
265 if( xReturn != XST_SUCCESS )
267 LINK_STATS_INC( link.memerr );
268 LINK_STATS_INC( link.drop );
269 snmp_inc_ifoutdiscards( pxNetIf );
274 LINK_STATS_INC( link.xmit );
275 snmp_add_ifoutoctets( pxNetIf, usTotalLength );
276 pxHeader = ( struct eth_hdr * )p->payload;
278 if( ( pxHeader->dest.addr[ 0 ] & 1 ) != 0 )
280 /* broadcast or multicast packet*/
281 snmp_inc_ifoutnucastpkts( pxNetIf );
286 snmp_inc_ifoutucastpkts( pxNetIf );
295 * Should allocate a pbuf and transfer the bytes of the incoming
296 * packet from the interface into the pbuf.
298 * @param pxNetIf the lwip network interface structure for this etherpxNetIf
299 * @return a pbuf filled with the received packet (including MAC header)
300 * NULL on memory error
302 static struct pbuf *prvLowLevelInput( const unsigned char * const pucInputData, unsigned short usDataLength )
304 struct pbuf *p = NULL, *q;
306 if( usDataLength > 0U )
309 len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
312 /* We allocate a pbuf chain of pbufs from the pool. */
313 p = pbuf_alloc( PBUF_RAW, usDataLength, PBUF_POOL );
318 pbuf_header( p, -ETH_PAD_SIZE ); /* drop the padding word */
321 /* We iterate over the pbuf chain until we have read the entire
322 * packet into the pbuf. */
324 for( q = p; q != NULL; q = q->next )
326 /* Read enough bytes to fill this pbuf in the chain. The
327 * available data in the pbuf is given by the q->len
329 * This does not necessarily have to be a memcpy, you can also preallocate
330 * pbufs for a DMA-enabled MAC and after receiving truncate it to the
331 * actually received size. In this case, ensure the usTotalLength member of the
332 * pbuf is the sum of the chained pbuf len members.
334 memcpy( q->payload, &( pucInputData[ usDataLength ] ), q->len );
335 usDataLength += q->len;
339 pbuf_header( p, ETH_PAD_SIZE ); /* reclaim the padding word */
342 LINK_STATS_INC(link.recv);
350 * Should be called at the beginning of the program to set up the
351 * network interface. It calls the function prvLowLevelInit() to do the
352 * actual setup of the hardware.
354 * This function should be passed as a parameter to pxNetIf_add().
356 * @param pxNetIf the lwip network interface structure for this etherpxNetIf
357 * @return ERR_OK if the loopif is initialized
358 * ERR_MEM if private data couldn't be allocated
359 * any other err_t on error
361 err_t ethernetif_init( struct netif *pxNetIf )
363 err_t xReturn = ERR_OK;
365 /* This is taken from lwIP example code and therefore does not conform
366 to the FreeRTOS coding standard. */
368 struct xEthernetIf *pxEthernetIf;
370 LWIP_ASSERT( "pxNetIf != NULL", ( pxNetIf != NULL ) );
372 pxEthernetIf = mem_malloc( sizeof( struct xEthernetIf ) );
373 if( pxEthernetIf == NULL )
375 LWIP_DEBUGF(NETIF_DEBUG, ( "ethernetif_init: out of memory\n" ) );
380 #if LWIP_NETIF_HOSTNAME
382 /* Initialize interface hostname */
383 pxNetIf->hostname = "lwip";
385 #endif /* LWIP_NETIF_HOSTNAME */
387 pxNetIf->state = pxEthernetIf;
388 pxNetIf->name[ 0 ] = IFNAME0;
389 pxNetIf->name[ 1 ] = IFNAME1;
391 /* We directly use etharp_output() here to save a function call.
392 * You can instead declare your own function an call etharp_output()
393 * from it if you have to do some checks before sending (e.g. if link
394 * is available...) */
395 pxNetIf->output = etharp_output;
396 pxNetIf->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP;
397 pxNetIf->hwaddr_len = ETHARP_HWADDR_LEN;
398 pxNetIf->mtu = netifMAX_MTU;
399 pxNetIf->linkoutput = prvLowLevelOutput;
401 pxEthernetIf->ethaddr = ( struct eth_addr * ) &( pxNetIf->hwaddr[ 0 ] );
403 /* initialize the hardware */
404 prvLowLevelInit( pxNetIf );
409 /*-----------------------------------------------------------*/
411 static void prvRxHandler( void *pvNetIf )
413 /* This is taken from lwIP example code and therefore does not conform
414 to the FreeRTOS coding standard. */
416 struct eth_hdr *pxHeader;
418 unsigned short usInputLength;
419 static unsigned char ucBuffer[ 1520 ] __attribute__((aligned(32)));
420 extern portBASE_TYPE xInsideISR;
421 struct netif *pxNetIf = ( struct netif * ) pvNetIf;
423 XIntc_AckIntr( XPAR_ETHERNET_LITE_BASEADDR, XPAR_ETHERNET_LITE_IP2INTC_IRPT_MASK );
425 /* Ensure the pbuf handling functions don't attempt to use critical
429 usInputLength = ( long ) XEmacLite_Recv( &xEMACInstance, ucBuffer );
431 /* move received packet into a new pbuf */
432 p = prvLowLevelInput( ucBuffer, usInputLength );
434 /* no packet could be read, silently ignore this */
437 /* points to packet payload, which starts with an Ethernet header */
438 pxHeader = p->payload;
440 switch( htons( pxHeader->type ) )
442 /* IP or ARP packet? */
445 /* full packet send to tcpip_thread to process */
446 if( pxNetIf->input( p, pxNetIf ) != ERR_OK )
448 LWIP_DEBUGF(NETIF_DEBUG, ( "ethernetif_input: IP input error\n" ) );
463 /*-----------------------------------------------------------*/
465 static void prvTxHandler( void *pvUnused )
468 XIntc_AckIntr( XPAR_ETHERNET_LITE_BASEADDR, XPAR_ETHERNET_LITE_IP2INTC_IRPT_MASK );