1 /*
2  * FreeRTOS+TCP V3.1.0
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Driver code:
8  * Copyright (C) Nicholas J. Kinar <n.kinar@usask.ca>, Centre for Hydrology, University of Saskatchewan
9  *
10  * MSP432 Driverlib (C) 2017-2019 Texas Instruments Incorporated <https://www.ti.com/>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of
13  * this software and associated documentation files (the "Software"), to deal in
14  * the Software without restriction, including without limitation the rights to
15  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
16  * the Software, and to permit persons to whom the Software is furnished to do so,
17  * subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in all
20  * copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
24  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
25  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
26  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28  *
29  * http://aws.amazon.com/freertos
30  * http://www.FreeRTOS.org
31  */
32 
33 #include <string.h>
34 #include <stdint.h>
35 #include <ti/devices/msp432e4/driverlib/driverlib.h>
36 #include <ti/devices/msp432e4/driverlib/emac.h>
37 
38 #include "FreeRTOS.h"
39 #include "FreeRTOS_IP.h"
40 #include "FreeRTOS_IP_private.h"
41 #include "FreeRTOSIPConfigDefaults.h"
42 #include "task.h"
43 #include "NetworkBufferManagement.h"
44 
45 #include "NetworkInterface.h"
46 
47 /*
48  * NOTES:
49  * This is a driver for the internal MAC of the MSP432E401Y microcontroller from Texas Instruments.
50  *
51  * (1) See below for defines that configure and override FreeRTOS defines so that the driver can be used.
52  * (2) The driver provides link up and down detection that also takes into consideration remote faults
53  * and Ethernet cable plug/unplug events during data transmission.
54  * (3) The EMAC hardware is automatically re-started if there is an internal error.
55  * (4) The MAC address can be set from outside of the driver or read from internal flash.
56  * (5) The EMAC hardware can be turned on or off from external tasks.
57  * (6) The EMAC hardware is set to 10BASE-T Half Duplex to ensure that the FreeRTOS+TCP stack does not receive
58  * data too quickly. This is because the 120 MHz processor cannot respond fast enough to ping floods or
59  * other events.  The hardware is thereby set up so that it can respond in a robust fashion.
60  * (7) Supports tickless interrupts.
61  * (8) To turn on and off the EMAC, it is best to do a hard microcontroller reset.
62  *
63  * Recommended settings in FreeRTOSIPConfig.h:
64  #define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES 1
65  #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM  1
66  #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM  1
67  #define ipconfigIP_TASK_STACK_SIZE_WORDS    ( configMINIMAL_STACK_SIZE * 100 )
68  #define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS      60
69  #define configTASK_NOTIFICATION_ARRAY_ENTRIES 2
70  *
71  #define ipconfigHAS_DEBUG_PRINTF    0
72  #define ipconfigHAS_PRINTF          0
73  *
74  * Recommended settings in FreeRTOSConfig.h:
75  #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
76  #define configUSE_PREEMPTION            1
77  #define configUSE_TIME_SLICING          0
78  #define configCPU_CLOCK_HZ              ( ( unsigned long ) 120000000 )
79  #define configMINIMAL_STACK_SIZE        ( ( unsigned short ) 256 )
80  #define configTOTAL_HEAP_SIZE           ( ( size_t ) ( 0x28000 ) )
81  #define ipconfigTCP_WIN_SEG_COUNT       240
82  *
83  * REFERENCES:
84  * [1] FreeRTOS Forum
85  * [2] Texas Instruments Driverlib code examples
86  */
87 
88 #define RTOS_NET_UP_DOWN_TASK_NAME     "NetUpDownS"
89 #define RTOS_NET_RX_TASK_NAME          "NetRX"
90 #define RTOS_NET_TX_TASK_NAME          "NetTX"
91 #define RTOS_NET_CHECK_TASK_SIZE       configMINIMAL_STACK_SIZE
92 #define RTOS_RX_POLL_MAC_TASK_SIZE     3 * configMINIMAL_STACK_SIZE
93 #define RTOS_TX_POLL_MAC_TASK_SIZE     3 * configMINIMAL_STACK_SIZE
94 #define NUM_TX_DESCRIPTORS             8U
95 #define NUM_RX_DESCRIPTORS             8U
96 
97 #define ETH_DOWN_DELAY_MS              1000U
98 #define ETH_MAX_TIMEOUT_CYCLES         12000000U
99 #define ETH_STARTUP_TIMEOUT            ETH_MAX_TIMEOUT_CYCLES
100 #define CHECK_LINK_UP_DOWN_DELAY_MS    1000U
101 
102 #define ETH_RX_QUEUE_LEN               NUM_TX_DESCRIPTORS
103 #define ETH_TX_QUEUE_LEN               NUM_RX_DESCRIPTORS
104 
105 /* TX Queue positions */
106 #define TX_QUEPOS_FIRST_EVENT          0
107 #define TX_QUEPOS_SECOND_EVENT         1
108 
109 /* Minimum packet length is required to ensure that all of the bytes are transmitted
110  * even when there are issues with the network (i.e. a network cable pulled out during TX or RX) */
111 #define ETHERNET_MIN_PACKET_BYTES      60
112 
113 /* Ensure that the RTOS settings work well for this driver */
114 #if configTASK_NOTIFICATION_ARRAY_ENTRIES < 2
115     #undef configTASK_NOTIFICATION_ARRAY_ENTRIES
116     #define configTASK_NOTIFICATION_ARRAY_ENTRIES     2
117 #endif
118 #ifndef ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM
119     #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM    1
120 #endif
121 #ifndef ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM
122     #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM    1
123 #endif
124 #ifdef ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM
125     #undef ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM
126     #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM    1
127 #endif
128 #ifdef ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM
129     #undef ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM
130     #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM    1
131 #endif
132 #ifndef ipconfigETHERNET_MINIMUM_PACKET_BYTES
133     #define ipconfigETHERNET_MINIMUM_PACKET_BYTES     ETHERNET_MIN_PACKET_BYTES
134 #endif
135 #ifdef ipconfigETHERNET_MINIMUM_PACKET_BYTES
136     #undef ipconfigETHERNET_MINIMUM_PACKET_BYTES
137     #define ipconfigETHERNET_MINIMUM_PACKET_BYTES    ETHERNET_MIN_PACKET_BYTES
138 #endif
139 #if ipconfigHAS_DEBUG_PRINTF == 1
140     #warning The network interface may not work properly if ipconfigHAS_DEBUG_PRINTF 1.  Please set ipconfigHAS_DEBUG_PRINTF 0 for production use.
141 #endif
142 #ifndef __MSP432E401Y__
143     #define __MSP432E401Y__
144 #endif
145 
146 static BaseType_t loadMACInternal();
147 static BaseType_t setupEMAC();
148 static void offEMAC();
149 static void initDescriptors( uint32_t ul32Base );
150 static uint32_t packetTransmit( uint8_t * pui8Buf,
151                                 uint32_t ul32BufLen );
152 static void ethernetIntHandler( void );
153 static uint32_t processReceivedPacket( void );
154 static void applicationProcessFrameRX( uint32_t ul32FrameLen,
155                                        uint8_t * uc8Buf,
156                                        uint32_t ulindex );
157 static void prvCheckLinkUpOrDownNetStateTask( void * pvParameters );
158 static void prvEMACDeferredInterruptHandlerTaskRX( void * pvParameters );
159 static BaseType_t isEMACLinkUp();
160 static void DMAFreeDescriptorRX( uint32_t ulindx );
161 static void prvEMACDeferredInterfaceOutputTaskTX( void * pvParameters );
162 static void vSetNetworkInterfaceConfig( const struct InternalNetworkInterfaceMSP432EConfig config );
163 
164 /* EMAC interrupts */
165 #define EMAC_INTERRUPTS    EMAC_INT_RECEIVE | EMAC_INT_TRANSMIT | EMAC_INT_BUS_ERROR | EMAC_INT_TX_STOPPED | EMAC_INT_RX_STOPPED
166 
167 /* DMA descriptors */
168 tEMACDMADescriptor g_psRxDescriptor[ NUM_TX_DESCRIPTORS ];
169 tEMACDMADescriptor g_psTxDescriptor[ NUM_RX_DESCRIPTORS ];
170 uint32_t ulg_ui32RxDescIndex;
171 uint32_t ulg_ui32TxDescIndex;
172 
173 /* Define the maximum length of a packet buffer. 1536 is ideal because it
174  * allows for a perfect alignment. */
175 #define ETH_MAX_BUFFER_SIZE    1536
176 #define ETH_RX_BUFFER_SIZE     ETH_MAX_BUFFER_SIZE
177 #define ETH_TX_BUFFER_SIZE     ETH_MAX_BUFFER_SIZE
178 uint8_t ucg_ppui8RxBuffer[ NUM_RX_DESCRIPTORS ][ ETH_RX_BUFFER_SIZE ];
179 
180 /* Task handles */
181 TaskHandle_t xTaskToNotifyEthernetRX = NULL;
182 TaskHandle_t xTaskToNotifyEthernetTX = NULL;
183 TaskHandle_t xHandleCheckLinkUpOrDown = NULL;
184 TaskHandle_t xHandleCheckNet = NULL;
185 
186 /* Queue handles */
187 QueueHandle_t xQueueRX;
188 QueueHandle_t xQueueTX;
189 
190 /* MAC address */
191 uint8_t ucpui8MACAddr[ ipMAC_ADDRESS_LENGTH_BYTES ];
192 
193 /* State variable that indicates whether the network is up or down */
194 static BaseType_t networkUP = pdFALSE;
195 /* Check to see if the device has been setup */
196 static BaseType_t hasBeenSetup = pdFALSE;
197 /* Check to see if the network needs to be reset */
198 static BaseType_t resetNetwork = pdFALSE;
199 
200 /* RX data input buffer */
201 struct NetworkInterfaceDataIn
202 {
203     uint8_t * ucbuff;    /* buffer to the data */
204     uint32_t ulbuff_siz; /* buffer size */
205     uint32_t ulindx;     /* index of buffer in DMA descriptor */
206 };
207 
208 /* TX data output buffer */
209 struct NetworkInterfaceDataOut
210 {
211     NetworkBufferDescriptor_t * pxDescriptor;
212     BaseType_t xReleaseAfterSend;
213 };
214 
215 /* Local config struct */
216 static struct InternalNetworkInterfaceMSP432EConfig configLocal;
217 
218 
219 /* Call this function to check if the network interface is up.
220  * The function can be called from code external to this file.
221  */
vPublicCheckNetworkInterfaceUp()222 BaseType_t vPublicCheckNetworkInterfaceUp()
223 {
224     return networkUP;
225 } /* end */
226 
227 
loadMACInternal()228 static BaseType_t loadMACInternal()
229 {
230     uint32_t ui32User0, ui32User1;
231 
232     /* Read the MAC address from internal flash. Bit[23:0] are stored in user register0, and Bit[47:24] are stored in user register1.
233      * The MAC address can be loaded from an external IC or can be loaded from the internal registers if the registers are not zero.
234      * The evaluation kit hardware uses reading from internal flash variables but this can also be used for production as well
235      * if the internal flash is programmed on the assembly line. */
236     FlashUserGet( &ui32User0, &ui32User1 );
237 
238     if( ( ( FlashUserGet( &ui32User0, &ui32User1 ) ) != 0 ) ||
239         ( ui32User0 == 0xffffffff ) ||
240         ( ui32User1 == 0xffffffff ) )
241     {
242         return pdFALSE;
243     }
244 
245     configLocal.ucMACAddr[ 0 ] = ( ( ui32User0 >> 0 ) & 0xffU );
246     configLocal.ucMACAddr[ 1 ] = ( ( ui32User0 >> 8 ) & 0xffU );
247     configLocal.ucMACAddr[ 2 ] = ( ( ui32User0 >> 16 ) & 0xffU );
248     configLocal.ucMACAddr[ 3 ] = ( ( ui32User1 >> 0 ) & 0xffU );
249     configLocal.ucMACAddr[ 4 ] = ( ( ui32User1 >> 8 ) & 0xffU );
250     configLocal.ucMACAddr[ 5 ] = ( ( ui32User1 >> 16 ) & 0xffU );
251     return pdTRUE;
252 }
253 
254 
255 /* This function sets up the EMAC. Not to be called directly from outside of this file. */
setupEMAC()256 static BaseType_t setupEMAC()
257 {
258     uint32_t ul32Loop;
259     BaseType_t rv;
260     BaseType_t interruptsMasked = pdFALSE;
261 
262     if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
263     {
264         taskENTER_CRITICAL();
265         interruptsMasked = pdTRUE;
266     }
267 
268     if( configLocal.setMACAddrInternal == pdTRUE )
269     {
270         rv = loadMACInternal();
271 
272         if( rv == pdFALSE )
273         {
274             return pdFALSE;
275         }
276     }
277 
278     /* enable and reset the internal MAC */
279     SysCtlPeripheralPowerOn( SYSCTL_PERIPH_EMAC0 );
280     SysCtlPeripheralPowerOn( SYSCTL_PERIPH_EPHY0 );
281     SysCtlPeripheralEnable( SYSCTL_PERIPH_EMAC0 );
282     SysCtlPeripheralEnable( SYSCTL_PERIPH_EPHY0 );
283     SysCtlPeripheralReset( SYSCTL_PERIPH_EMAC0 );
284     SysCtlPeripheralReset( SYSCTL_PERIPH_EPHY0 );
285 
286     /* The peripheral should always start, but there is a wait with timeout here to prevent an infinite loop*/
287     ul32Loop = 0;
288 
289     while( !SysCtlPeripheralReady( SYSCTL_PERIPH_EMAC0 ) && ( ul32Loop < ETH_STARTUP_TIMEOUT ) )
290     {
291         SysCtlDelay( 1 );
292         ul32Loop += 1;
293     }
294 
295     if( ul32Loop == ETH_STARTUP_TIMEOUT )
296     {
297         return pdFALSE;
298     }
299 
300     /* configure the internal PHY */
301     EMACPHYConfigSet( EMAC0_BASE,
302                       ( EMAC_PHY_TYPE_INTERNAL |
303                         EMAC_PHY_INT_MDIX_EN |
304                         EMAC_PHY_AN_10B_T_HALF_DUPLEX ) );
305 
306     /* reset the MAC to latch the configuration */
307     EMACReset( EMAC0_BASE );
308 
309     /* wait */
310     SysCtlDelay( ETH_STARTUP_TIMEOUT );
311 
312     /* init the EMAC */
313     EMACInit( EMAC0_BASE, configCPU_CLOCK_HZ,
314               EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, 4, 4,
315               0 );
316 
317     /*
318      *  Since the checksum is computed in the hardware using EMAC_CONFIG_CHECKSUM_OFFLOAD,
319      *  there is also a need to turn on defines in FreeRTOS+TCP as well. This is done automatically
320      *  using the defines in this file.
321      *
322      #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM  1
323      #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM  1
324      */
325     EMACConfigSet( EMAC0_BASE,
326                    ( EMAC_CONFIG_HALF_DUPLEX |
327                      EMAC_CONFIG_CHECKSUM_OFFLOAD |
328                      EMAC_CONFIG_IF_GAP_96BITS |
329                      EMAC_CONFIG_USE_MACADDR0 |
330                      EMAC_CONFIG_BO_LIMIT_1024 ),
331                    ( EMAC_MODE_RX_STORE_FORWARD | EMAC_MODE_TX_STORE_FORWARD
332                    ), ETH_MAX_BUFFER_SIZE );
333 
334     /* DMA descriptors init */
335     initDescriptors( EMAC0_BASE );
336 
337     /* program MAC address from the cached address */
338     EMACAddrSet( EMAC0_BASE, 0, configLocal.ucMACAddr );
339 
340     /* Set address filtering */
341     EMACAddrFilterSet( EMAC0_BASE, 0, EMAC_FILTER_ADDR_ENABLE | EMAC_FILTER_SOURCE_ADDR );
342 
343     /* Set MAC filtering options */
344     EMACFrameFilterSet( EMAC0_BASE, EMAC_FRMFILTER_SADDR | EMAC_FRMFILTER_PASS_NO_CTRL );
345 
346     /* indicate that the receive descriptors are available to the DMA to start the receive processing */
347     for( ul32Loop = 0; ul32Loop < NUM_RX_DESCRIPTORS; ul32Loop++ )
348     {
349         g_psRxDescriptor[ ul32Loop ].ui32CtrlStatus |= DES0_RX_CTRL_OWN;
350     }
351 
352     /* Enable the Ethernet MAC transmitter and receiver */
353     EMACTxEnable( EMAC0_BASE );
354     EMACRxEnable( EMAC0_BASE );
355 
356     /* Enable the Ethernet interrupt */
357     IntPrioritySet( INT_EMAC0, 0x20 ); /* (0x01 << 5) to set the priority into the last three bits for Cortex M4F */
358 
359     /* register the interrupt handler for the Ethernet MAC */
360     EMACIntRegister( EMAC0_BASE, ethernetIntHandler );
361 
362     /* enable the interrupts for the EMAC */
363     EMACIntClear( EMAC0_BASE, EMACIntStatus( EMAC0_BASE, pdFALSE ) );
364     IntEnable( INT_EMAC0 );
365     EMACIntEnable( EMAC0_BASE, EMAC_INTERRUPTS );
366 
367     /* exit the critical section */
368     if( interruptsMasked == pdTRUE )
369     {
370         taskEXIT_CRITICAL();
371     }
372 
373     return pdTRUE;
374 }
375 
376 
377 /* This function only turns off the Ethernet EMAC.  This function does not turn off any
378  * of the processing tasks and therefore should not be called directly from external tasks. */
offEMAC()379 static void offEMAC()
380 {
381     uint32_t ulstatus, ulcnt;
382     BaseType_t interruptsMasked;
383 
384     interruptsMasked = pdFALSE;
385 
386     if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
387     {
388         taskENTER_CRITICAL();
389         interruptsMasked = pdTRUE;
390     }
391 
392     networkUP = pdFALSE;
393 
394     ulcnt = 0;
395     ulstatus = EMACStatusGet( EMAC0_BASE );
396 
397     while( ulstatus )
398     {
399         ulstatus = EMACStatusGet( EMAC0_BASE );
400         ulcnt++;
401 
402         if( ulcnt == ETH_STARTUP_TIMEOUT )
403         {
404             if( interruptsMasked == pdTRUE )
405             {
406                 taskEXIT_CRITICAL();
407             }
408 
409             return;
410         }
411     }
412 
413     IntDisable( INT_EMAC0 );
414     SysCtlPeripheralDisable( SYSCTL_PERIPH_EMAC0 );
415     SysCtlPeripheralDisable( SYSCTL_PERIPH_EPHY0 );
416     SysCtlPeripheralPowerOff( SYSCTL_PERIPH_EMAC0 );
417     SysCtlPeripheralPowerOff( SYSCTL_PERIPH_EPHY0 );
418 
419     if( interruptsMasked == pdTRUE )
420     {
421         taskEXIT_CRITICAL();
422     }
423 }
424 
425 
426 /* Interrupt handler */
ethernetIntHandler(void)427 static void ethernetIntHandler( void )
428 {
429     uint32_t ui32Temp;
430 
431     ui32Temp = EMACIntStatus( EMAC0_BASE, pdTRUE );
432 
433     if( ui32Temp & EMAC_INT_RECEIVE )
434     {
435         /* RX */
436         processReceivedPacket();
437     }
438 
439     if( ui32Temp & EMAC_INT_TRANSMIT )
440     {
441         BaseType_t xHigherPriorityTaskWokenTX = pdFALSE;
442         /* TX */
443         vTaskNotifyGiveIndexedFromISR( xTaskToNotifyEthernetTX, TX_QUEPOS_SECOND_EVENT, &xHigherPriorityTaskWokenTX );
444         portYIELD_FROM_ISR( xHigherPriorityTaskWokenTX );
445     }
446 
447     if( ( ui32Temp & EMAC_INT_BUS_ERROR ) || ( ui32Temp & EMAC_INT_TX_STOPPED ) || ( ui32Temp & EMAC_INT_RX_STOPPED ) )
448     {
449         /* Reset the network since something has gone wrong */
450         resetNetwork = pdTRUE;
451     }
452 
453     /* clear the interrupt */
454     EMACIntClear( EMAC0_BASE, ui32Temp );
455     /* as per Application Note SLAA739 - June 2017 */
456     #if configUSE_TICKLESS_IDLE == 1
457         SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;
458         __DSB();
459     #endif
460 }
461 
462 
463 /* Init the DMA descriptors */
initDescriptors(uint32_t ul32Base)464 static void initDescriptors( uint32_t ul32Base )
465 {
466     uint32_t ul32Loop;
467 
468     for( ul32Loop = 0U; ul32Loop < NUM_TX_DESCRIPTORS; ul32Loop++ )
469     {
470         g_psTxDescriptor[ ul32Loop ].ui32Count = DES1_TX_CTRL_SADDR_INSERT;
471         g_psTxDescriptor[ ul32Loop ].DES3.pLink =
472             ( ul32Loop == ( NUM_TX_DESCRIPTORS - 1 ) ) ?
473             g_psTxDescriptor : &g_psTxDescriptor[ ul32Loop + 1 ];
474         g_psTxDescriptor[ ul32Loop ].ui32CtrlStatus =
475             ( DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG |
476               DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_CHAINED |
477               DES0_TX_CTRL_IP_ALL_CKHSUMS );
478     }
479 
480     for( ul32Loop = 0; ul32Loop < NUM_RX_DESCRIPTORS; ul32Loop++ )
481     {
482         g_psRxDescriptor[ ul32Loop ].ui32CtrlStatus = 0;
483         g_psRxDescriptor[ ul32Loop ].ui32Count =
484             ( DES1_RX_CTRL_CHAINED |
485               ( ETH_RX_BUFFER_SIZE << DES1_RX_CTRL_BUFF1_SIZE_S ) );
486         g_psRxDescriptor[ ul32Loop ].pvBuffer1 = ucg_ppui8RxBuffer[ ul32Loop ];
487         g_psRxDescriptor[ ul32Loop ].DES3.pLink =
488             ( ul32Loop == ( NUM_RX_DESCRIPTORS - 1 ) ) ?
489             g_psRxDescriptor : &g_psRxDescriptor[ ul32Loop + 1 ];
490     }
491 
492     EMACRxDMADescriptorListSet( ul32Base, g_psRxDescriptor );
493     EMACTxDMADescriptorListSet( ul32Base, g_psTxDescriptor );
494 
495     ulg_ui32RxDescIndex = 0;
496     ulg_ui32TxDescIndex = NUM_TX_DESCRIPTORS - 1;
497 }
498 
499 
500 /* Transmit a packet - call this function from the network stack
501  *  pui8Buf = the buffer
502  *  i32BufLen = length of the buffer
503  */
packetTransmit(uint8_t * pui8Buf,uint32_t ul32BufLen)504 static uint32_t packetTransmit( uint8_t * pui8Buf,
505                                 uint32_t ul32BufLen )
506 {
507     uint8_t bufferTX[ ETH_TX_BUFFER_SIZE ];
508 
509     ulg_ui32TxDescIndex++;
510 
511     if( ulg_ui32TxDescIndex == NUM_TX_DESCRIPTORS )
512     {
513         ulg_ui32TxDescIndex = 0;
514     }
515 
516     /* The copy needs to be done here since directly assigning a pointer does not seem to work
517      * and causes the transmitter to stop.  Some testing indicates that that this can be quicker than
518      *  simply assigning the pointer for smaller packets. */
519     memcpy( bufferTX, pui8Buf, ul32BufLen );
520 
521     g_psTxDescriptor[ ulg_ui32TxDescIndex ].ui32Count = ul32BufLen;
522     g_psTxDescriptor[ ulg_ui32TxDescIndex ].pvBuffer1 = bufferTX;
523     g_psTxDescriptor[ ulg_ui32TxDescIndex ].ui32CtrlStatus =
524         ( DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG |
525           DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_IP_ALL_CKHSUMS |
526           DES0_TX_CTRL_CHAINED | DES0_TX_CTRL_OWN );
527 
528     EMACTxDMAPollDemand( EMAC0_BASE );
529     return( ul32BufLen );
530 }
531 
532 
533 /* Function to process the received packet */
processReceivedPacket(void)534 static uint32_t processReceivedPacket( void )
535 {
536     uint32_t ul32FrameLen;
537 
538     ul32FrameLen = 0;
539 
540     if( !( g_psRxDescriptor[ ulg_ui32RxDescIndex ].ui32CtrlStatus & DES0_RX_CTRL_OWN ) )
541     {
542         /* Does it have a valid frame? */
543         if( !( g_psRxDescriptor[ ulg_ui32RxDescIndex ].ui32CtrlStatus &
544                DES0_RX_STAT_ERR ) )
545         {
546             if( g_psRxDescriptor[ ulg_ui32RxDescIndex ].ui32CtrlStatus &
547                 DES0_RX_STAT_LAST_DESC )
548             {
549                 /* get the frame length */
550                 ul32FrameLen =
551                     ( ( g_psRxDescriptor[ ulg_ui32RxDescIndex ].ui32CtrlStatus &
552                         DES0_RX_STAT_FRAME_LENGTH_M ) >>
553                       DES0_RX_STAT_FRAME_LENGTH_S );
554 
555                 /* call the function that sends the data to the task */
556                 applicationProcessFrameRX( ul32FrameLen,
557                                            g_psRxDescriptor[ ulg_ui32RxDescIndex ].pvBuffer1, ulg_ui32RxDescIndex );
558             }
559         }
560 
561         /* Move on to the next RX packet */
562         ulg_ui32RxDescIndex++;
563 
564         if( ulg_ui32RxDescIndex == NUM_RX_DESCRIPTORS )
565         {
566             ulg_ui32RxDescIndex = 0;
567         }
568     } /* end if */
569 
570     /* return the frame length */
571     return ul32FrameLen;
572 }
573 
574 
575 /* This function passes the framelength and the RX buffer into the FreeRTOS task.
576  * The function is called from the ISR and therefore must use portYIELD_FROM_ISR() */
applicationProcessFrameRX(uint32_t ul32FrameLen,uint8_t * uc8Buf,uint32_t ulindex)577 static void applicationProcessFrameRX( uint32_t ul32FrameLen,
578                                        uint8_t * uc8Buf,
579                                        uint32_t ulindex )
580 {
581     BaseType_t rv;
582     struct NetworkInterfaceDataIn NIDataIn;
583     BaseType_t xHigherPriorityTaskWokenRX = pdFALSE;
584 
585     NIDataIn.ucbuff = uc8Buf;
586     NIDataIn.ulbuff_siz = ul32FrameLen;
587     NIDataIn.ulindx = ulindex;
588 
589     /* send the data into the queue */
590     rv = xQueueSendFromISR( xQueueRX, &NIDataIn, &xHigherPriorityTaskWokenRX );
591 
592     if( rv == pdTRUE )
593     {
594         vTaskNotifyGiveFromISR( xTaskToNotifyEthernetRX, &xHigherPriorityTaskWokenRX );
595     }
596     else
597     {
598         DMAFreeDescriptorRX( ulindex );
599     }
600 
601     portYIELD_FROM_ISR( xHigherPriorityTaskWokenRX );
602 }
603 
604 /* Function to free the RX descriptor */
DMAFreeDescriptorRX(uint32_t ulindx)605 static void DMAFreeDescriptorRX( uint32_t ulindx )
606 {
607     g_psRxDescriptor[ ulindx ].ui32CtrlStatus = DES0_RX_CTRL_OWN;
608 }
609 
610 /*
611  * -------------------------------------------------------------
612  * FREERTOS+TCP FUNCTIONS
613  * -------------------------------------------------------------
614  */
615 
616 /* Call this function to populate the driver defaults struct with defaults */
vGetInternalNetworkInterfaceMSP432EConfigDefaults(struct InternalNetworkInterfaceMSP432EConfig * config)617 void vGetInternalNetworkInterfaceMSP432EConfigDefaults( struct InternalNetworkInterfaceMSP432EConfig * config )
618 {
619     uint32_t k;
620 
621     config->turnOnEMAC = pdTRUE;
622     config->setMACAddrInternal = pdFALSE;
623 
624     for( k = 0; k < ipMAC_ADDRESS_LENGTH_BYTES; k++ )
625     {
626         config->ucMACAddr[ k ] = 0xFF;
627     }
628 }
629 
630 
vSetNetworkInterfaceConfig(const struct InternalNetworkInterfaceMSP432EConfig config)631 static void vSetNetworkInterfaceConfig( const struct InternalNetworkInterfaceMSP432EConfig config )
632 {
633     configLocal = config;
634 }
635 
636 
637 /* Call this function to setup the network */
vPublicSetupEMACNetwork(const struct InternalNetworkInterfaceMSP432EConfig config)638 BaseType_t vPublicSetupEMACNetwork( const struct InternalNetworkInterfaceMSP432EConfig config )
639 {
640     BaseType_t rv;
641 
642     rv = pdFALSE;
643     BaseType_t tv;
644 
645     /* setup the MAC address and turn on the EMAC if required */
646     vSetNetworkInterfaceConfig( config );
647 
648     if( config.turnOnEMAC )
649     {
650         rv = setupEMAC();
651     }
652 
653     if( rv == pdFALSE )
654     {
655         return pdFALSE;
656     }
657 
658     /* ensure that the code can only be run once to create the tasks */
659     if( hasBeenSetup == pdTRUE )
660     {
661         return pdTRUE;
662     }
663 
664     /* create the queues */
665     xQueueRX = xQueueCreate( ETH_RX_QUEUE_LEN, sizeof( struct NetworkInterfaceDataIn ) );
666 
667     if( xQueueRX == NULL )
668     {
669         return pdFALSE;
670     }
671 
672     xQueueTX = xQueueCreate( ETH_TX_QUEUE_LEN, sizeof( struct NetworkInterfaceDataOut ) );
673 
674     if( xQueueTX == NULL )
675     {
676         return pdFALSE;
677     }
678 
679     /* RX task */
680     tv = xTaskCreate( prvEMACDeferredInterruptHandlerTaskRX,
681                       RTOS_NET_RX_TASK_NAME,
682                       RTOS_RX_POLL_MAC_TASK_SIZE,
683                       NULL,
684                       configMAX_PRIORITIES - 1,
685                       &xTaskToNotifyEthernetRX );
686 
687     if( tv != pdPASS )
688     {
689         return pdFALSE;
690     }
691 
692     /* TX task */
693     tv = xTaskCreate( prvEMACDeferredInterfaceOutputTaskTX,
694                       RTOS_NET_TX_TASK_NAME,
695                       RTOS_TX_POLL_MAC_TASK_SIZE,
696                       NULL,
697                       configMAX_PRIORITIES - 1,
698                       &xTaskToNotifyEthernetTX );
699 
700     if( tv != pdPASS )
701     {
702         return pdFALSE;
703     }
704 
705     /* Link Up/Down/Network task */
706     tv = xTaskCreate( prvCheckLinkUpOrDownNetStateTask,
707                       RTOS_NET_UP_DOWN_TASK_NAME,
708                       RTOS_NET_CHECK_TASK_SIZE,
709                       NULL,
710                       tskIDLE_PRIORITY,
711                       &xHandleCheckLinkUpOrDown );
712 
713     if( tv != pdPASS )
714     {
715         return pdFALSE;
716     }
717 
718     /* latch the setup state */
719     hasBeenSetup = pdTRUE;
720 
721     /* the setup has succeeded */
722     return pdTRUE;
723 }
724 
725 
726 /* FreeRTOS task that handles the RX interrupt */
prvEMACDeferredInterruptHandlerTaskRX(void * pvParameters)727 static void prvEMACDeferredInterruptHandlerTaskRX( void * pvParameters )
728 {
729     NetworkBufferDescriptor_t * pxDescriptor = NULL;
730     IPStackEvent_t xRxEvent;
731     struct NetworkInterfaceDataIn NIDataReceived;
732     uint32_t xBytesReceived;
733 
734     for( ; ; )
735     {
736         ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
737         xQueueReceive( xQueueRX, &NIDataReceived, 0 );
738 
739         if( eConsiderFrameForProcessing( NIDataReceived.ucbuff ) == eProcessBuffer )
740         {
741             xBytesReceived = NIDataReceived.ulbuff_siz - ipSIZE_OF_ETH_CRC_BYTES; /* do not include the CRC bytes in the received size */
742             pxDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 );
743 
744             if( pxDescriptor != NULL )
745             {
746                 memcpy( pxDescriptor->pucEthernetBuffer, NIDataReceived.ucbuff, NIDataReceived.ulbuff_siz );
747                 pxDescriptor->xDataLength = xBytesReceived;
748                 xRxEvent.eEventType = eNetworkRxEvent;
749                 xRxEvent.pvData = ( void * ) pxDescriptor;
750 
751                 if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdTRUE )
752                 {
753                     /* The buffer does not need to be released here since this will be done internally by the network stack */
754                     iptraceNETWORK_INTERFACE_RECEIVE();
755                 }
756                 else
757                 {
758                     /* The buffer needs to be released since we cannot send to the network stack */
759                     vReleaseNetworkBufferAndDescriptor( pxDescriptor );
760                     iptraceETHERNET_RX_EVENT_LOST();
761                 }
762             }
763         }
764 
765         /* Release the DMA descriptor at a specific index */
766         taskENTER_CRITICAL();
767         DMAFreeDescriptorRX( NIDataReceived.ulindx );
768         taskEXIT_CRITICAL();
769     } /* end for */
770 }
771 
772 
773 /* Network initialization is already done outside of this function, so this function will only
774  * report if the network is initialized by returning a flag. Called directly by FreeRTOS. */
xNetworkInterfaceInitialise(void)775 BaseType_t xNetworkInterfaceInitialise( void )
776 {
777     if( networkUP == pdTRUE )
778     {
779         return pdTRUE;
780     }
781 
782     return pdFALSE;
783 }
784 
785 
786 /*  Task to output the data to the network interface. This allows the network stack to continue
787  *  processing while the data is able to be sent. */
prvEMACDeferredInterfaceOutputTaskTX(void * pvParameters)788 static void prvEMACDeferredInterfaceOutputTaskTX( void * pvParameters )
789 {
790     struct NetworkInterfaceDataOut NIDataOutput;
791 
792     for( ; ; )
793     {
794         ulTaskNotifyTakeIndexed( TX_QUEPOS_FIRST_EVENT, pdTRUE, portMAX_DELAY );
795         xQueueReceive( xQueueTX, &NIDataOutput, 0 );
796 
797         /* For ICMP packets, the checksum must be zero if the network hardware computes the checksum or the buffer will not be properly sent
798          * For this driver it is required that ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 */
799         ProtocolPacket_t * pxPacket;
800         pxPacket = ( ProtocolPacket_t * ) ( NIDataOutput.pxDescriptor->pucEthernetBuffer );
801 
802         if( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
803         {
804             pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t ) 0u;
805         }
806 
807         taskENTER_CRITICAL();
808         packetTransmit( NIDataOutput.pxDescriptor->pucEthernetBuffer, ( uint32_t ) NIDataOutput.pxDescriptor->xDataLength );
809         taskEXIT_CRITICAL();
810 
811         /* wait until transmit */
812         ulTaskNotifyTakeIndexed( TX_QUEPOS_SECOND_EVENT, pdTRUE, portMAX_DELAY );
813         iptraceNETWORK_INTERFACE_TRANSMIT();
814 
815         if( NIDataOutput.xReleaseAfterSend )
816         {
817             vReleaseNetworkBufferAndDescriptor( NIDataOutput.pxDescriptor );
818         }
819     }
820 }
821 
822 
823 /*  Function to output packets to the network.
824  *  Called directly by FreeRTOS. This function should not block and therefore passes
825  *  all output data to a queue.  If the function blocks the network stack can become unresponsive
826  *  so all of the output work is thereby done by another task. */
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxDescriptor,BaseType_t xReleaseAfterSend)827 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor,
828                                     BaseType_t xReleaseAfterSend )
829 {
830     struct NetworkInterfaceDataOut NIDataOutput;
831     BaseType_t txResp;
832     BaseType_t returnValue = pdFALSE;
833     BaseType_t xDescriptorUsed = pdFALSE;
834 
835     /* When the following assert is hit, please make sure that
836      * ipconfigZERO_COPY_TX_DRIVER is defined as '1' in your
837      * FreeRTOSIPConfig.h header file. */
838     configASSERT( xReleaseAfterSend != pdFALSE );
839 
840     if( networkUP != pdFALSE )
841     {
842         NIDataOutput.pxDescriptor = pxDescriptor;
843         NIDataOutput.xReleaseAfterSend = xReleaseAfterSend;
844         txResp = xQueueSend( xQueueTX, &NIDataOutput, 0 );
845 
846         if( txResp == pdTRUE )
847         {
848             xTaskNotifyGiveIndexed( xTaskToNotifyEthernetTX, 0 );
849             returnValue = pdTRUE;
850             xDescriptorUsed = pdTRUE;
851         }
852     }
853     else
854     {
855         /* The PHY has no Link Status, packet shall be dropped. */
856     }
857 
858     if( xDescriptorUsed == pdFALSE )
859     {
860         vReleaseNetworkBufferAndDescriptor( pxDescriptor );
861     }
862 
863     return returnValue;
864 }
865 
866 
867 /*
868  * -----------------------------------------------------------------------------------------------------
869  * Network interface UP or DOWN tasks and associated functions
870  * -----------------------------------------------------------------------------------------------------
871  */
872 
873 /*  Function to check and see if the network is up or down by polling a register in the Ethernet driver.
874  *  Apparently there is no other way to do this other than polling.  The register is checked for link status,
875  *  but also for remote faults and this takes into consideration a pulled Ethernet cable during RX and TX.
876  */
isEMACLinkUp()877 static BaseType_t isEMACLinkUp()
878 {
879     uint8_t uci8PHYAddr = 0; /* refers to the internal PHY */
880     uint16_t check;
881     BaseType_t returnValue = pdFALSE;
882 
883     check = EMACPHYRead( EMAC0_BASE, uci8PHYAddr, EPHY_BMSR );
884 
885     if( ( ( check & EPHY_BMSR_LINKSTAT ) != 0 ) && !( check & EPHY_BMSR_RFAULT ) )
886     {
887         returnValue = pdTRUE; /* link is up */
888     }
889 
890     return returnValue; /* return link status  */
891 }
892 
893 
894 /* Call this network function to physically turn on the EMAC from an internal task. */
vPrivateTurnOnEMAC()895 static BaseType_t vPrivateTurnOnEMAC()
896 {
897     if( hasBeenSetup == pdFALSE )
898     {
899         return pdFALSE;
900     }
901 
902     setupEMAC();
903 
904     if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
905     {
906         FreeRTOS_NetworkDown();
907         vTaskResume( xHandleCheckLinkUpOrDown );
908         vTaskResume( xHandleCheckNet );
909         vTaskResume( xTaskToNotifyEthernetRX );
910         vTaskResume( xTaskToNotifyEthernetTX );
911     }
912 
913     return pdTRUE;
914 }
915 
916 
917 /*  Call this function to physically turn off the EMAC from an external task.
918  *  It is recommended to check the network stack for open sockets before calling this task,
919  *  and only call the task after all sockets are closed and there are no connected clients.
920  *  In an operational sense, the best way to turn off the EMAC is to do a hard reset. */
vPrivateTurnOffEMAC()921 static BaseType_t vPrivateTurnOffEMAC()
922 {
923     if( hasBeenSetup == pdFALSE )
924     {
925         return pdFALSE; /* make sure that the MAC has been setup */
926     }
927 
928     if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
929     {
930         vTaskSuspend( xHandleCheckLinkUpOrDown );
931         vTaskSuspend( xHandleCheckNet );
932         vTaskSuspend( xTaskToNotifyEthernetRX );
933         vTaskSuspend( xTaskToNotifyEthernetTX );
934     }
935 
936     networkUP = pdFALSE;
937 
938     if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
939     {
940         FreeRTOS_NetworkDown();                           /* Indicate to FreeRTOS that the network is down */
941         vTaskDelay( pdMS_TO_TICKS( ETH_DOWN_DELAY_MS ) ); /* Wait until FreeRTOS has finished processing */
942     }
943 
944     offEMAC(); /* Turn off the physical hardware */
945     return pdTRUE;
946 }
947 
948 
949 /* A task to check and see if the link is up or down by polling an EMAC register */
prvCheckLinkUpOrDownNetStateTask(void * pvParameters)950 static void prvCheckLinkUpOrDownNetStateTask( void * pvParameters )
951 {
952     BaseType_t checkLinkStatus = pdFALSE;
953 
954     for( ; ; )
955     {
956         checkLinkStatus = isEMACLinkUp();
957 
958         if( ( checkLinkStatus == pdTRUE ) && ( networkUP == pdFALSE ) )
959         {
960             networkUP = pdTRUE;
961         }
962         else if( ( checkLinkStatus == pdFALSE ) && ( networkUP == pdTRUE ) )
963         {
964             /*   FreeRTOS will poll xNetworkInterfaceInitialise() to check if the network is up.
965              *   So after FreeRTOS_NetworkDown() is called, there is no corresponding FreeRTOS_NetworkUp() function...
966              */
967             networkUP = pdFALSE;
968             FreeRTOS_NetworkDown();
969         }
970 
971         if( resetNetwork == pdTRUE )
972         {
973             vPrivateTurnOffEMAC();
974             vPrivateTurnOnEMAC();
975             resetNetwork = pdFALSE;
976         }
977     }
978 }
979 
980 
981 
982 /* Call this function to obtain the MAC address used by the driver */
vPublicGetMACAddr(uint8_t uc8MACAddrGet[ipMAC_ADDRESS_LENGTH_BYTES])983 void vPublicGetMACAddr( uint8_t uc8MACAddrGet[ ipMAC_ADDRESS_LENGTH_BYTES ] )
984 {
985     memcpy( uc8MACAddrGet, configLocal.ucMACAddr, ipMAC_ADDRESS_LENGTH_BYTES );
986 }
987 
988 
xGetPhyLinkStatus(void)989 BaseType_t xGetPhyLinkStatus( void )
990 {
991     return networkUP;
992 }
993 
994 
995 /*-----------------------------------------------------------------------------------------------------
996  * For BufferAllocation_1.c (see the FreeRTOS documentation for further details and examples)
997  * ----------------------------------------------------------------------------------------------------- */
998 #define BUFFER_SIZE_ALLOC1               ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
999 #define BUFFER_SIZE_ALLOC1_ROUNDED_UP    ( ( BUFFER_SIZE_ALLOC1 + 7 ) & ~0x07UL )
1000 static uint8_t ucBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ BUFFER_SIZE_ALLOC1_ROUNDED_UP ];
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])1001 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
1002 {
1003     BaseType_t x;
1004 
1005     for( x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ )
1006     {
1007         /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
1008          * beginning of the allocated buffer. */
1009         pxNetworkBuffers[ x ].pucEthernetBuffer = &( ucBuffers[ x ][ ipBUFFER_PADDING ] );
1010 
1011         /* The following line is also required, but will not be required in
1012          * future versions. */
1013         *( ( uint32_t * ) &ucBuffers[ x ][ 0 ] ) = ( uint32_t ) &( pxNetworkBuffers[ x ] );
1014     }
1015 }
1016