1 /******************************************************************************
2 *
3 * Copyright (C) 2010 - 2015 Xilinx, Inc. All rights reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * Use of the Software is limited solely to applications:
16 * (a) running on a Xilinx device, or
17 * (b) that interact with a Xilinx device through a bus or interconnect.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
24 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 *
27 * Except as contained in this notice, the name of the Xilinx shall not be used
28 * in advertising or otherwise to promote the sale, use or other dealings in
29 * this Software without prior written authorization from Xilinx.
30 *
31 ******************************************************************************/
32
33 /*
34 * FreeRTOS+TCP V2.3.3
35 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
36 *
37 * Permission is hereby granted, free of charge, to any person obtaining a copy of
38 * this software and associated documentation files (the "Software"), to deal in
39 * the Software without restriction, including without limitation the rights to
40 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
41 * the Software, and to permit persons to whom the Software is furnished to do so,
42 * subject to the following conditions:
43 *
44 * The above copyright notice and this permission notice shall be included in all
45 * copies or substantial portions of the Software.
46 *
47 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
49 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
50 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
51 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
52 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
53 *
54 * http://aws.amazon.com/freertos
55 * http://www.FreeRTOS.org
56 */
57
58 #include "FreeRTOS.h"
59 #include "task.h"
60 #include "timers.h"
61 #include "semphr.h"
62
63 /* FreeRTOS+TCP includes. */
64 #include "FreeRTOS_IP.h"
65 #include "FreeRTOS_Sockets.h"
66 #include "FreeRTOS_IP_Private.h"
67 #include "FreeRTOS_Routing.h"
68 #include "NetworkBufferManagement.h"
69
70 #include "Zynq/x_emacpsif.h"
71 #include "Zynq/x_topology.h"
72 #include "xstatus.h"
73
74 #include "xparameters.h"
75 #include "xparameters_ps.h"
76 #include "xil_exception.h"
77 #include "xil_mmu.h"
78
79 #include "uncached_memory.h"
80
81 /* Two defines used to set or clear the EMAC interrupt */
82 #define INTC_BASE_ADDR XPAR_SCUGIC_CPU_BASEADDR
83 #define INTC_DIST_BASE_ADDR XPAR_SCUGIC_DIST_BASEADDR
84
85
86
87 #if ( ipconfigPACKET_FILLER_SIZE != 2 )
88 #error Please define ipconfigPACKET_FILLER_SIZE as the value '2'
89 #endif
90 #define TX_OFFSET ipconfigPACKET_FILLER_SIZE
91
92 #define dmaRX_TX_BUFFER_SIZE 1536
93
94 /* Defined in NetworkInterface.c */
95 extern TaskHandle_t xEMACTaskHandles[ XPAR_XEMACPS_NUM_INSTANCES ];
96
97 /*
98 * pxDMA_tx_buffers: these are character arrays, each one is big enough to hold 1 MTU.
99 * The actual TX buffers are located in uncached RAM.
100 */
101 static unsigned char * pxDMA_tx_buffers[ XPAR_XEMACPS_NUM_INSTANCES ][ ipconfigNIC_N_TX_DESC ];
102
103 /*
104 * pxDMA_rx_buffers: these are pointers to 'NetworkBufferDescriptor_t'.
105 * Once a message has been received by the EMAC, the descriptor can be passed
106 * immediately to the IP-task.
107 */
108 static NetworkBufferDescriptor_t * pxDMA_rx_buffers[ XPAR_XEMACPS_NUM_INSTANCES ][ ipconfigNIC_N_RX_DESC ];
109
110 /*
111 * The FreeRTOS+TCP port is using a fixed 'topology', which is declared in
112 * ./portable/NetworkInterface/Zynq/NetworkInterface.c
113 */
114 extern struct xtopology_t xXTopologies[ XPAR_XEMACPS_NUM_INSTANCES ];
115
116 static SemaphoreHandle_t xTXDescriptorSemaphores[ XPAR_XEMACPS_NUM_INSTANCES ];
117
118 BaseType_t xMayAcceptPacket( uint8_t * pucEthernetBuffer );
119
120 static void prvPassEthMessages( NetworkBufferDescriptor_t * pxDescriptor );
121
122 /*
123 * The FreeRTOS+TCP port does not make use of "src/xemacps_bdring.c".
124 * In stead 'struct xemacpsif_s' has a "head" and a "tail" index.
125 * "head" is the next index to be written, used.
126 * "tail" is the next index to be read, freed.
127 */
128
is_tx_space_available(xemacpsif_s * xemacpsif)129 int is_tx_space_available( xemacpsif_s * xemacpsif )
130 {
131 size_t uxCount;
132 BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
133
134 if( xTXDescriptorSemaphores[ xEMACIndex ] != NULL )
135 {
136 uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphores[ xEMACIndex ] );
137 }
138 else
139 {
140 uxCount = ( UBaseType_t ) 0u;
141 }
142
143 return uxCount;
144 }
145
emacps_check_tx(xemacpsif_s * xemacpsif)146 void emacps_check_tx( xemacpsif_s * xemacpsif )
147 {
148 int tail = xemacpsif->txTail;
149 int head = xemacpsif->txHead;
150 BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
151 size_t uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphores[ xEMACIndex ] );
152
153 /* uxCount is the number of TX descriptors that are in use by the DMA. */
154 /* When done, "TXBUF_USED" will be set. */
155
156 while( ( uxCount > 0 ) && ( ( xemacpsif->txSegments[ tail ].flags & XEMACPS_TXBUF_USED_MASK ) != 0 ) )
157 {
158 if( ( tail == head ) && ( uxCount != ipconfigNIC_N_TX_DESC ) )
159 {
160 break;
161 }
162
163 {
164 void * pvBuffer = pxDMA_tx_buffers[ xEMACIndex ][ tail ];
165 NetworkBufferDescriptor_t * pxBuffer;
166
167 if( pvBuffer != NULL )
168 {
169 pxDMA_tx_buffers[ xEMACIndex ][ tail ] = NULL;
170 pxBuffer = pxPacketBuffer_to_NetworkBuffer( pvBuffer );
171
172 if( pxBuffer != NULL )
173 {
174 vReleaseNetworkBufferAndDescriptor( pxBuffer );
175 }
176 else
177 {
178 FreeRTOS_printf( ( "emacps_check_tx: Can not find network buffer\n" ) );
179 }
180 }
181 }
182
183 /* Clear all but the "used" and "wrap" bits. */
184 if( tail < ipconfigNIC_N_TX_DESC - 1 )
185 {
186 xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK;
187 }
188 else
189 {
190 xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK;
191 }
192
193 uxCount--;
194 /* Tell the counting semaphore that one more TX descriptor is available. */
195 xSemaphoreGive( xTXDescriptorSemaphores[ xEMACIndex ] );
196
197 if( ++tail == ipconfigNIC_N_TX_DESC )
198 {
199 tail = 0;
200 }
201
202 xemacpsif->txTail = tail;
203 }
204 }
205
emacps_send_handler(void * arg)206 void emacps_send_handler( void * arg )
207 {
208 xemacpsif_s * xemacpsif;
209 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
210 BaseType_t xEMACIndex;
211
212 xemacpsif = ( xemacpsif_s * ) arg;
213 xEMACIndex = xemacpsif->emacps.Config.DeviceId;
214
215 /* This function is called from an ISR. The Xilinx ISR-handler has already
216 * cleared the TXCOMPL and TXSR_USEDREAD status bits in the XEMACPS_TXSR register.
217 * But it forgets to do a read-back. Do so now to avoid ever-returning ISR's. */
218 ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_TXSR_OFFSET );
219
220 /* In this port for FreeRTOS+TCP, the EMAC interrupts will only set a bit in
221 * "isr_events". The task in NetworkInterface will wake-up and do the necessary work.
222 */
223 xemacpsif->isr_events |= EMAC_IF_TX_EVENT;
224 xemacpsif->txBusy = pdFALSE;
225
226 if( xEMACTaskHandles[ xEMACIndex ] != NULL )
227 {
228 vTaskNotifyGiveFromISR( xEMACTaskHandles[ xEMACIndex ], &xHigherPriorityTaskWoken );
229 }
230
231 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
232 }
233
xValidLength(BaseType_t xLength)234 static BaseType_t xValidLength( BaseType_t xLength )
235 {
236 BaseType_t xReturn;
237
238 if( ( xLength >= ( BaseType_t ) sizeof( struct xARP_PACKET ) ) && ( ( ( uint32_t ) xLength ) <= dmaRX_TX_BUFFER_SIZE ) )
239 {
240 xReturn = pdTRUE;
241 }
242 else
243 {
244 xReturn = pdFALSE;
245 }
246
247 return xReturn;
248 }
249
emacps_send_message(xemacpsif_s * xemacpsif,NetworkBufferDescriptor_t * pxBuffer,int iReleaseAfterSend)250 XStatus emacps_send_message( xemacpsif_s * xemacpsif,
251 NetworkBufferDescriptor_t * pxBuffer,
252 int iReleaseAfterSend )
253 {
254 int txHead = xemacpsif->txHead;
255 int iHasSent = 0;
256 uint32_t ulBaseAddress = xemacpsif->emacps.Config.BaseAddress;
257 BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
258 TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 5000U );
259
260 /* This driver wants to own all network buffers which are to be transmitted. */
261 configASSERT( iReleaseAfterSend != pdFALSE );
262
263 /* Open a do {} while ( 0 ) loop to be able to call break. */
264 do
265 {
266 uint32_t ulFlags = 0;
267
268 if( xValidLength( pxBuffer->xDataLength ) != pdTRUE )
269 {
270 break;
271 }
272
273 if( xTXDescriptorSemaphores[ xEMACIndex ] == NULL )
274 {
275 break;
276 }
277
278 if( xSemaphoreTake( xTXDescriptorSemaphores[ xEMACIndex ], xBlockTimeTicks ) != pdPASS )
279 {
280 FreeRTOS_printf( ( "emacps_send_message: Time-out waiting for TX buffer\n" ) );
281 break;
282 }
283
284 /* Pass the pointer (and its ownership) directly to DMA. */
285 pxDMA_tx_buffers[ xEMACIndex ][ txHead ] = pxBuffer->pucEthernetBuffer;
286
287 if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
288 {
289 Xil_DCacheFlushRange( ( INTPTR ) pxBuffer->pucEthernetBuffer, ( u32 ) pxBuffer->xDataLength );
290 }
291
292 /* Buffer has been transferred, do not release it. */
293 iReleaseAfterSend = pdFALSE;
294
295 /* Packets will be sent one-by-one, so for each packet
296 * the TXBUF_LAST bit will be set. */
297 ulFlags |= XEMACPS_TXBUF_LAST_MASK;
298 ulFlags |= ( pxBuffer->xDataLength & XEMACPS_TXBUF_LEN_MASK );
299
300 if( txHead == ( ipconfigNIC_N_TX_DESC - 1 ) )
301 {
302 ulFlags |= XEMACPS_TXBUF_WRAP_MASK;
303 }
304
305 /* Copy the address of the buffer and set the flags. */
306 xemacpsif->txSegments[ txHead ].address = ( uint32_t ) pxDMA_tx_buffers[ xEMACIndex ][ txHead ];
307
308 if( xemacpsif->txSegments[ txHead ].address )
309 {
310 }
311
312 xemacpsif->txSegments[ txHead ].flags = ulFlags;
313
314 if( xemacpsif->txSegments[ txHead ].flags )
315 {
316 }
317
318 iHasSent = pdTRUE;
319
320 txHead++;
321
322 if( txHead == ipconfigNIC_N_TX_DESC )
323 {
324 txHead = 0;
325 }
326
327 /* Update the TX-head index. These variable are declared volatile so they will be
328 * accessed as little as possible. */
329 xemacpsif->txHead = txHead;
330
331 /* Data Synchronization Barrier */
332 dsb();
333
334 if( iHasSent == pdTRUE )
335 {
336 /* Make STARTTX high */
337 uint32_t ulValue = XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET );
338 /* Start transmit */
339 xemacpsif->txBusy = pdTRUE;
340 XEmacPs_WriteReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET, ( ulValue | XEMACPS_NWCTRL_STARTTX_MASK ) );
341 /* Read back the register to make sure the data is flushed. */
342 ( void ) XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET );
343 }
344
345 dsb();
346 } while( ipFALSE_BOOL );
347
348 if( iReleaseAfterSend != pdFALSE )
349 {
350 vReleaseNetworkBufferAndDescriptor( pxBuffer );
351 pxBuffer = NULL;
352 }
353
354 return 0;
355 }
356
emacps_recv_handler(void * arg)357 void emacps_recv_handler( void * arg )
358 {
359 xemacpsif_s * xemacpsif;
360 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
361 BaseType_t xEMACIndex;
362
363 xemacpsif = ( xemacpsif_s * ) arg;
364 xemacpsif->isr_events |= EMAC_IF_RX_EVENT;
365 xEMACIndex = xemacpsif->emacps.Config.DeviceId;
366
367 /* The driver has already cleared the FRAMERX, BUFFNA and error bits
368 * in the XEMACPS_RXSR register,
369 * But it forgets to do a read-back. Do so now. */
370 ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET );
371
372 if( xEMACTaskHandles[ xEMACIndex ] != NULL )
373 {
374 vTaskNotifyGiveFromISR( xEMACTaskHandles[ xEMACIndex ], &xHigherPriorityTaskWoken );
375 }
376
377 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
378 }
379 /*-----------------------------------------------------------*/
380
prvPassEthMessages(NetworkBufferDescriptor_t * pxDescriptor)381 static void prvPassEthMessages( NetworkBufferDescriptor_t * pxDescriptor )
382 {
383 IPStackEvent_t xRxEvent;
384
385 xRxEvent.eEventType = eNetworkRxEvent;
386 xRxEvent.pvData = ( void * ) pxDescriptor;
387
388 if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 1000 ) != pdPASS )
389 {
390 /* The buffer could not be sent to the IP-task so it must be released again.
391 * This is a deferred handler taskr, not a real interrupt, so it is ok to
392 * use the task level function here. */
393 #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
394 {
395 do
396 {
397 NetworkBufferDescriptor_t * pxNext = pxDescriptor->pxNextBuffer;
398 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
399 pxDescriptor = pxNext;
400 } while( pxDescriptor != NULL );
401 }
402 #else
403 {
404 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
405 }
406 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
407 iptraceETHERNET_RX_EVENT_LOST();
408 FreeRTOS_printf( ( "prvPassEthMessages: Can not queue return packet!\n" ) );
409 }
410 }
411 /*-----------------------------------------------------------*/
412
xMayAcceptPacket(uint8_t * pucEthernetBuffer)413 BaseType_t xMayAcceptPacket( uint8_t * pucEthernetBuffer )
414 {
415 const ProtocolPacket_t * pxProtPacket = ( const ProtocolPacket_t * ) pucEthernetBuffer;
416
417 switch( pxProtPacket->xTCPPacket.xEthernetHeader.usFrameType )
418 {
419 case ipARP_FRAME_TYPE:
420 /* Check it later. */
421 return pdTRUE;
422
423 case ipIPv6_FRAME_TYPE:
424 /* Check it later. */
425 return pdTRUE;
426
427 case ipIPv4_FRAME_TYPE:
428 /* Check it here. */
429 break;
430
431 default:
432 /* Refuse the packet. */
433 return pdFALSE;
434 }
435
436 #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 )
437 {
438 const IPHeader_t * pxIPHeader = &( pxProtPacket->xTCPPacket.xIPHeader );
439
440 /* Ensure that the incoming packet is not fragmented (only outgoing packets
441 * can be fragmented) as these are the only handled IP frames currently. */
442 if( ( pxIPHeader->usFragmentOffset & FreeRTOS_ntohs( ipFRAGMENT_OFFSET_BIT_MASK ) ) != 0U )
443 {
444 return pdFALSE;
445 }
446
447 /* HT: Might want to make the following configurable because
448 * most IP messages have a standard length of 20 bytes */
449
450 /* 0x45 means: IPv4 with an IP header of 5 x 4 = 20 bytes
451 * 0x47 means: IPv4 with an IP header of 7 x 4 = 28 bytes */
452 if( ( pxIPHeader->ucVersionHeaderLength < 0x45 ) || ( pxIPHeader->ucVersionHeaderLength > 0x4F ) )
453 {
454 return pdFALSE;
455 }
456
457 if( pxIPHeader->ucProtocol == ipPROTOCOL_UDP )
458 {
459 uint16_t usSourcePort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usSourcePort );
460 uint16_t usDestinationPort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort );
461
462 if( ( xPortHasUDPSocket( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort ) == pdFALSE )
463 #if ipconfigUSE_LLMNR == 1
464 && ( usDestinationPort != ipLLMNR_PORT ) &&
465 ( usSourcePort != ipLLMNR_PORT )
466 #endif
467 #if ipconfigUSE_NBNS == 1
468 && ( usDestinationPort != ipNBNS_PORT ) &&
469 ( usSourcePort != ipNBNS_PORT )
470 #endif
471 #if ipconfigUSE_DNS == 1
472 && ( usSourcePort != ipDNS_PORT )
473 #endif
474 )
475 {
476 /* Drop this packet, not for this device. */
477 /* FreeRTOS_printf( ( "Drop: UDP port %d -> %d\n", usSourcePort, usDestinationPort ) ); */
478 return pdFALSE;
479 }
480 }
481 }
482 #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */
483 return pdTRUE;
484 }
485 /*-----------------------------------------------------------*/
486
emacps_check_rx(xemacpsif_s * xemacpsif,NetworkInterface_t * pxInterface)487 int emacps_check_rx( xemacpsif_s * xemacpsif,
488 NetworkInterface_t * pxInterface )
489 {
490 NetworkBufferDescriptor_t * pxBuffer, * pxNewBuffer;
491 int rx_bytes;
492 volatile int msgCount = 0;
493 int rxHead = xemacpsif->rxHead;
494 BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
495 BaseType_t xAccepted;
496
497 #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
498 NetworkBufferDescriptor_t * pxFirstDescriptor = NULL;
499 NetworkBufferDescriptor_t * pxLastDescriptor = NULL;
500 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
501
502 /* There seems to be an issue (SI# 692601), see comments below. */
503 resetrx_on_no_rxdata( xemacpsif );
504
505 /* This FreeRTOS+TCP driver shall be compiled with the option
506 * "ipconfigUSE_LINKED_RX_MESSAGES" enabled. It allows the driver to send a
507 * chain of RX messages within one message to the IP-task. */
508 for( ; ; )
509 {
510 if( ( ( xemacpsif->rxSegments[ rxHead ].address & XEMACPS_RXBUF_NEW_MASK ) == 0 ) ||
511 ( pxDMA_rx_buffers[ xEMACIndex ][ rxHead ] == NULL ) )
512 {
513 break;
514 }
515
516 pxBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ xEMACIndex ][ rxHead ];
517 xAccepted = xMayAcceptPacket( pxBuffer->pucEthernetBuffer );
518
519 if( xAccepted == pdFALSE )
520 {
521 pxNewBuffer = NULL;
522 }
523 else
524 {
525 pxNewBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 );
526
527 if( pxNewBuffer == NULL )
528 {
529 /* A packet has been received, but there is no replacement for this Network Buffer.
530 * The packet will be dropped, and it Network Buffer will stay in place. */
531 FreeRTOS_printf( ( "emacps_check_rx: unable to allocate a Network Buffer\n" ) );
532 }
533 }
534
535 if( pxNewBuffer == NULL )
536 {
537 pxNewBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ xEMACIndex ][ rxHead ];
538 }
539 else
540 {
541 pxBuffer->pxInterface = pxInterface;
542 pxBuffer->pxEndPoint = FreeRTOS_MatchingEndpoint( pxInterface, pxBuffer->pucEthernetBuffer );
543 /* Just avoiding to use or refer to the same buffer again */
544 pxDMA_rx_buffers[ xEMACIndex ][ rxHead ] = pxNewBuffer;
545
546 /*
547 * Adjust the buffer size to the actual number of bytes received.
548 */
549 rx_bytes = xemacpsif->rxSegments[ rxHead ].flags & XEMACPS_RXBUF_LEN_MASK;
550
551 pxBuffer->xDataLength = rx_bytes;
552
553 if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
554 {
555 Xil_DCacheInvalidateRange( ( ( uint32_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( unsigned ) rx_bytes );
556 }
557
558 /* store it in the receive queue, where it'll be processed by a
559 * different handler. */
560 iptraceNETWORK_INTERFACE_RECEIVE();
561 #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
562 {
563 pxBuffer->pxNextBuffer = NULL;
564
565 if( pxFirstDescriptor == NULL )
566 {
567 /* Becomes the first message */
568 pxFirstDescriptor = pxBuffer;
569 }
570 else if( pxLastDescriptor != NULL )
571 {
572 /* Add to the tail */
573 pxLastDescriptor->pxNextBuffer = pxBuffer;
574 }
575
576 pxLastDescriptor = pxBuffer;
577 }
578 #else /* if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) */
579 {
580 prvPassEthMessages( pxBuffer );
581 }
582 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
583
584 msgCount++;
585 }
586
587 {
588 if( ucIsCachedMemory( pxNewBuffer->pucEthernetBuffer ) != 0 )
589 {
590 Xil_DCacheInvalidateRange( ( ( uint32_t ) pxNewBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( uint32_t ) dmaRX_TX_BUFFER_SIZE );
591 }
592
593 {
594 uint32_t addr = ( ( uint32_t ) pxNewBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK;
595
596 if( rxHead == ( ipconfigNIC_N_RX_DESC - 1 ) )
597 {
598 addr |= XEMACPS_RXBUF_WRAP_MASK;
599 }
600
601 /* Clearing 'XEMACPS_RXBUF_NEW_MASK' 0x00000001 *< Used bit.. */
602 xemacpsif->rxSegments[ rxHead ].flags = 0;
603 xemacpsif->rxSegments[ rxHead ].address = addr;
604 /* Make sure that the value has reached the peripheral by reading it back. */
605 ( void ) xemacpsif->rxSegments[ rxHead ].address;
606 }
607 }
608
609 rxHead++;
610
611 if( rxHead == ipconfigNIC_N_RX_DESC )
612 {
613 rxHead = 0;
614 }
615
616 xemacpsif->rxHead = rxHead;
617 }
618
619 #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
620 {
621 if( pxFirstDescriptor != NULL )
622 {
623 prvPassEthMessages( pxFirstDescriptor );
624 }
625 }
626 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
627
628 return msgCount;
629 }
630
clean_dma_txdescs(xemacpsif_s * xemacpsif)631 void clean_dma_txdescs( xemacpsif_s * xemacpsif )
632 {
633 int index;
634 unsigned char * ucTxBuffer;
635 BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
636
637 /* Clear all TX descriptors and assign uncached memory to each descriptor.
638 * "tx_space" points to the first available TX buffer. */
639 ucTxBuffer = xemacpsif->tx_space;
640
641 for( index = 0; index < ipconfigNIC_N_TX_DESC; index++ )
642 {
643 xemacpsif->txSegments[ index ].address = ( uint32_t ) ucTxBuffer;
644 xemacpsif->txSegments[ index ].flags = XEMACPS_TXBUF_USED_MASK;
645 pxDMA_tx_buffers[ xEMACIndex ][ index ] = ( unsigned char * ) NULL;
646 ucTxBuffer += xemacpsif->uTxUnitSize;
647 }
648
649 xemacpsif->txSegments[ ipconfigNIC_N_TX_DESC - 1 ].flags =
650 XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK;
651 }
652
init_dma(xemacpsif_s * xemacpsif)653 XStatus init_dma( xemacpsif_s * xemacpsif )
654 {
655 NetworkBufferDescriptor_t * pxBuffer;
656 BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
657
658 int iIndex;
659 UBaseType_t xRxSize;
660 UBaseType_t xTxSize;
661 struct xtopology_t * xtopologyp = &( xXTopologies[ xEMACIndex ] );
662
663 xRxSize = ipconfigNIC_N_RX_DESC * sizeof( xemacpsif->rxSegments[ 0 ] );
664
665 xTxSize = ipconfigNIC_N_TX_DESC * sizeof( xemacpsif->txSegments[ 0 ] );
666
667 xemacpsif->uTxUnitSize = dmaRX_TX_BUFFER_SIZE;
668
669 /*
670 * We allocate 65536 bytes for RX BDs which can accommodate a
671 * maximum of 8192 BDs which is much more than any application
672 * will ever need.
673 */
674 xemacpsif->rxSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xRxSize ) );
675 xemacpsif->txSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xTxSize ) );
676 xemacpsif->tx_space = ( unsigned char * ) ( pucGetUncachedMemory( ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize ) );
677
678 /* These variables will be used in XEmacPs_Start (see src/xemacps.c). */
679 xemacpsif->emacps.RxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->rxSegments;
680 xemacpsif->emacps.TxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->txSegments;
681
682 if( xTXDescriptorSemaphores[ xEMACIndex ] == NULL )
683 {
684 xTXDescriptorSemaphores[ xEMACIndex ] = xSemaphoreCreateCounting( ( UBaseType_t ) ipconfigNIC_N_TX_DESC, ( UBaseType_t ) ipconfigNIC_N_TX_DESC );
685 configASSERT( xTXDescriptorSemaphores[ xEMACIndex ] );
686 }
687
688 /*
689 * Allocate RX descriptors, 1 RxBD at a time.
690 */
691 for( iIndex = 0; iIndex < ipconfigNIC_N_RX_DESC; iIndex++ )
692 {
693 pxBuffer = pxDMA_rx_buffers[ xEMACIndex ][ iIndex ];
694
695 if( pxBuffer == NULL )
696 {
697 pxBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 );
698
699 if( pxBuffer == NULL )
700 {
701 FreeRTOS_printf( ( "Unable to allocate a network buffer in recv_handler\n" ) );
702 return -1;
703 }
704 }
705
706 xemacpsif->rxSegments[ iIndex ].flags = 0U;
707 xemacpsif->rxSegments[ iIndex ].address = ( ( uint32_t ) pxBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK;
708
709 pxDMA_rx_buffers[ xEMACIndex ][ iIndex ] = pxBuffer;
710
711 /* Make sure this memory is not in cache for now. */
712 if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
713 {
714 Xil_DCacheInvalidateRange( ( ( uint32_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE,
715 ( unsigned ) dmaRX_TX_BUFFER_SIZE );
716 }
717 }
718
719 xemacpsif->rxSegments[ ipconfigNIC_N_RX_DESC - 1 ].address |= XEMACPS_RXBUF_WRAP_MASK;
720
721 memset( xemacpsif->tx_space, '\0', ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize );
722
723 clean_dma_txdescs( xemacpsif );
724
725 {
726 uint32_t value;
727 value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET );
728
729 /* 1xxxx: Attempt to use INCR16 AHB bursts */
730 value = ( value & ~( XEMACPS_DMACR_BLENGTH_MASK ) ) | XEMACPS_DMACR_INCR16_AHB_BURST;
731 #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
732 value |= XEMACPS_DMACR_TCPCKSUM_MASK;
733 #else
734 #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
735 {
736 #warning Are you sure the EMAC should not calculate outgoing checksums?
737 }
738 #endif
739
740 value &= ~XEMACPS_DMACR_TCPCKSUM_MASK;
741 #endif
742 XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET, value );
743 }
744 {
745 uint32_t value;
746 value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET );
747
748 /* Network buffers are 32-bit aligned + 2 bytes (because ipconfigPACKET_FILLER_SIZE = 2 ).
749 * Now tell the EMAC that received messages should be stored at "address + 2". */
750 value = ( value & ~XEMACPS_NWCFG_RXOFFS_MASK ) | 0x8000;
751
752 #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM != 0 )
753 value |= XEMACPS_NWCFG_RXCHKSUMEN_MASK;
754 #else
755 #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
756 {
757 #warning Are you sure the EMAC should not calculate incoming checksums?
758 }
759 #endif
760
761 value &= ~( ( uint32_t ) XEMACPS_NWCFG_RXCHKSUMEN_MASK );
762 #endif
763 XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET, value );
764 }
765
766 /*
767 * Connect the device driver handler that will be called when an
768 * interrupt for the device occurs, the handler defined above performs
769 * the specific interrupt processing for the device.
770 */
771 XScuGic_RegisterHandler( INTC_BASE_ADDR, xtopologyp->scugic_emac_intr,
772 ( Xil_ExceptionHandler ) XEmacPs_IntrHandler,
773 ( void * ) &xemacpsif->emacps );
774
775 /*
776 * Enable the interrupt for emacps.
777 */
778 EmacEnableIntr( xEMACIndex );
779
780 return 0;
781 }
782
783 /*
784 * resetrx_on_no_rxdata():
785 *
786 * It is called at regular intervals through the API xemacpsif_resetrx_on_no_rxdata
787 * called by the user.
788 * The EmacPs has a HW bug (SI# 692601) on the Rx path for heavy Rx traffic.
789 * Under heavy Rx traffic because of the HW bug there are times when the Rx path
790 * becomes unresponsive. The workaround for it is to check for the Rx path for
791 * traffic (by reading the stats registers regularly). If the stats register
792 * does not increment for sometime (proving no Rx traffic), the function resets
793 * the Rx data path.
794 *
795 */
796
resetrx_on_no_rxdata(xemacpsif_s * xemacpsif)797 void resetrx_on_no_rxdata( xemacpsif_s * xemacpsif )
798 {
799 uint32_t regctrl;
800 uint32_t tempcntr;
801
802 tempcntr = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXCNT_OFFSET );
803
804 if( ( tempcntr == 0 ) && ( xemacpsif->last_rx_frms_cntr == 0 ) )
805 {
806 regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress,
807 XEMACPS_NWCTRL_OFFSET );
808 regctrl &= ( ~XEMACPS_NWCTRL_RXEN_MASK );
809 XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress,
810 XEMACPS_NWCTRL_OFFSET, regctrl );
811 regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET );
812 regctrl |= ( XEMACPS_NWCTRL_RXEN_MASK );
813 XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, regctrl );
814 }
815
816 xemacpsif->last_rx_frms_cntr = tempcntr;
817 }
818
EmacDisableIntr(int xEMACIndex)819 void EmacDisableIntr( int xEMACIndex )
820 {
821 XScuGic_DisableIntr( INTC_DIST_BASE_ADDR, xXTopologies[ xEMACIndex ].scugic_emac_intr );
822 }
823
EmacEnableIntr(int xEMACIndex)824 void EmacEnableIntr( int xEMACIndex )
825 {
826 XScuGic_EnableIntr( INTC_DIST_BASE_ADDR, xXTopologies[ xEMACIndex ].scugic_emac_intr );
827 }
828