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