1 /**
2 * @file: NetworkInterface.c
3 * @author: jscott <jscott@hotstart.com>
4 * @date: Feb 1, 2022
5 * @copyright: Hotstart 2022 Hotstart Thermal Management. All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * @brief:Network Interface driver for the Texas Instruments TM4C line of microcontrollers.
25 *
26 * This driver was written and tested with the TM4C1294NCPDT, which includes a built-in MAC and
27 * PHY. The expectation is that this driver should function correctly across all the MAC/PHY
28 * integrated parts of the TM4C129X parts.
29 */
30
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdbool.h>
34 #include <stdlib.h>
35
36 #include "inc/hw_ints.h"
37 #include "inc/hw_emac.h"
38 #include "inc/hw_memmap.h"
39 #include "inc/hw_nvic.h"
40
41 #include "driverlib/flash.h"
42 #include "driverlib/interrupt.h"
43 #include "driverlib/gpio.h"
44 #include "driverlib/rom_map.h"
45 #include "driverlib/sysctl.h"
46 #include "driverlib/systick.h"
47 #include "driverlib/emac.h"
48
49 #include "FreeRTOS.h"
50 #include "task.h"
51 #include "queue.h"
52
53 #include "FreeRTOS_IP.h"
54 #include "FreeRTOS_Sockets.h"
55 #include "FreeRTOS_IP_Private.h"
56 #include "NetworkBufferManagement.h"
57 #include "NetworkInterface.h"
58 #include "phyHandling.h"
59
60 #define BUFFER_SIZE ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
61 #define BUFFER_SIZE_ROUNDED_UP ( ( BUFFER_SIZE + 7 ) & ~0x7UL )
62 #define PHY_PHYS_ADDR 0
63
64 #ifndef niEMAC_SYSCONFIG_HZ
65 #define niEMAC_SYSCONFIG_HZ configCPU_CLOCK_HZ
66 #endif
67
68 #ifndef niEMAC_TX_DMA_DESC_COUNT
69 #define niEMAC_TX_DMA_DESC_COUNT 8
70 #endif
71
72 #ifndef niEMAC_RX_DMA_DESC_COUNT
73 #define niEMAC_RX_DMA_DESC_COUNT 8
74 #endif
75
76 #if ipconfigUSE_LINKED_RX_MESSAGES
77 #error Linked RX Messages are not supported by this driver
78 #endif
79
80 /* Default the size of the stack used by the EMAC deferred handler task to twice
81 * the size of the stack used by the idle task - but allow this to be overridden in
82 * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */
83 #ifndef configEMAC_TASK_STACK_SIZE
84 #define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE )
85 #endif
86
87 #ifndef niEMAC_HANDLER_TASK_PRIORITY
88 #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1
89 #endif
90
91 #if !defined( ipconfigETHERNET_AN_ENABLE )
92 /* Enable auto-negotiation */
93 #define ipconfigETHERNET_AN_ENABLE 1
94 #endif
95
96 #if !defined( ipconfigETHERNET_USE_100MB )
97 #define ipconfigETHERNET_USE_100MB 1
98 #endif
99
100 #if !defined( ipconfigETHERNET_USE_FULL_DUPLEX )
101 #define ipconfigETHERNET_USE_FULL_DUPLEX 1
102 #endif
103
104 typedef struct
105 {
106 uint32_t number_descriptors;
107 uint32_t write;
108 uint32_t read;
109 } tDescriptorList;
110
111 typedef enum
112 {
113 eMACInit, /* Must initialise MAC. */
114 eMACPass, /* Initialisation was successful. */
115 eMACFailed, /* Initialisation failed. */
116 } eMAC_INIT_STATUS_TYPE;
117
118 typedef enum
119 {
120 eMACInterruptNone = 0, /* No interrupts need servicing */
121 eMACInterruptRx = ( 1 << 0 ), /* Service RX interrupt */
122 eMACInterruptTx = ( 1 << 1 ), /* Service TX interrupt */
123 } eMAC_INTERRUPT_STATUS_TYPE;
124
125 static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit;
126
127 static volatile eMAC_INTERRUPT_STATUS_TYPE xMacInterruptStatus = eMACInterruptNone;
128
129 static tEMACDMADescriptor _tx_descriptors[ niEMAC_TX_DMA_DESC_COUNT ];
130 static tEMACDMADescriptor _rx_descriptors[ niEMAC_RX_DMA_DESC_COUNT ];
131
132 static tDescriptorList _tx_descriptor_list = { .number_descriptors = niEMAC_TX_DMA_DESC_COUNT, 0 };
133 static tDescriptorList _rx_descriptor_list = { .number_descriptors = niEMAC_RX_DMA_DESC_COUNT, 0 };
134
135 static uint8_t _network_buffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ BUFFER_SIZE_ROUNDED_UP ];
136 #pragma DATA_ALIGN(_network_buffers, 4)
137
138 static EthernetPhy_t xPhyObject;
139
140 static TaskHandle_t _deferred_task_handle = NULL;
141
142 const PhyProperties_t xPHYProperties =
143 {
144 #if ( ipconfigETHERNET_AN_ENABLE != 0 )
145 .ucSpeed = PHY_SPEED_AUTO,
146 .ucDuplex = PHY_DUPLEX_AUTO,
147 #else
148 #if ( ipconfigETHERNET_USE_100MB != 0 )
149 .ucSpeed = PHY_SPEED_100,
150 #else
151 .ucSpeed = PHY_SPEED_10,
152 #endif
153
154 #if ( ipconfigETHERNET_USE_FULL_DUPLEX != 0 )
155 .ucDuplex = PHY_DUPLEX_FULL,
156 #else
157 .ucDuplex = PHY_DUPLEX_HALF,
158 #endif
159 #endif /* if ( ipconfigETHERNET_AN_ENABLE != 0 ) */
160 };
161
162 /**
163 * Reads the Ethernet MAC from user Flash.
164 * @param mac_address_bytes[out] The byte array which will hold the MAC address
165 * @return pdPASS on success, pdFAIL if the MAC is invalid from user Flash
166 */
167 static BaseType_t _ethernet_mac_get( uint8_t * mac_address_bytes );
168
169 /**
170 * Initialize DMA descriptors
171 */
172 static void _dma_descriptors_init( void );
173
174 /**
175 * Frees previously sent network buffers
176 */
177 static void _process_transmit_complete( void );
178
179 /**
180 * Processes received packets and forwards those acceptable to the network stack
181 */
182 static BaseType_t _process_received_packet( void );
183
184 /**
185 * Processes PHY interrupts.
186 */
187 static void _process_phy_interrupts( void );
188
189 /**
190 * Thread to forward received packets from the ISR to the network stack
191 * @param parameters Not used
192 */
193 static void _deferred_task( void * parameters );
194
195 /**
196 * Phy read implementation for the TM4C
197 * @param xAddress
198 * @param xRegister
199 * @param pulValue
200 * @return
201 */
202 static BaseType_t xTM4C_PhyRead( BaseType_t xAddress,
203 BaseType_t xRegister,
204 uint32_t * pulValue );
205
206 /**
207 * Phy write implementation for the TM4C
208 * @param xAddress
209 * @param xRegister
210 * @param ulValue
211 * @return
212 */
213 static BaseType_t xTM4C_PhyWrite( BaseType_t xAddress,
214 BaseType_t xRegister,
215 uint32_t ulValue );
216
217 /**
218 * Probe the PHY
219 */
220 static void vMACBProbePhy( void );
221
xNetworkInterfaceInitialise(void)222 BaseType_t xNetworkInterfaceInitialise( void )
223 {
224 uint8_t mac_address_bytes[ 6 ];
225 uint16_t ui16Val;
226 BaseType_t xResult = pdFAIL;
227
228 if( eMACInit == xMacInitStatus )
229 {
230 /* Create the RX packet forwarding task */
231 if( pdFAIL == xTaskCreate( _deferred_task, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &_deferred_task_handle ) )
232 {
233 xMacInitStatus = eMACFailed;
234 }
235 else
236 {
237 /* Read the MAC from user Flash */
238 if( pdPASS != _ethernet_mac_get( &mac_address_bytes[ 0 ] ) )
239 {
240 xMacInitStatus = eMACFailed;
241 }
242 else
243 {
244 MAP_SysCtlPeripheralReset( SYSCTL_PERIPH_EMAC0 );
245
246 while( !MAP_SysCtlPeripheralReady( SYSCTL_PERIPH_EMAC0 ) )
247 {
248 }
249
250 MAP_SysCtlPeripheralReset( SYSCTL_PERIPH_EPHY0 );
251
252 while( !MAP_SysCtlPeripheralReady( SYSCTL_PERIPH_EPHY0 ) )
253 {
254 }
255
256 MAP_EMACInit( EMAC0_BASE, niEMAC_SYSCONFIG_HZ,
257 EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, 4,
258 4, 0 );
259
260 MAP_EMACConfigSet(
261 EMAC0_BASE,
262 (
263 EMAC_CONFIG_100MBPS |
264 EMAC_CONFIG_FULL_DUPLEX |
265 EMAC_CONFIG_CHECKSUM_OFFLOAD |
266 EMAC_CONFIG_7BYTE_PREAMBLE |
267 EMAC_CONFIG_IF_GAP_96BITS |
268 EMAC_CONFIG_USE_MACADDR0 |
269 EMAC_CONFIG_SA_FROM_DESCRIPTOR |
270 EMAC_CONFIG_BO_LIMIT_1024 |
271 EMAC_CONFIG_STRIP_CRC
272 ),
273 (
274 EMAC_MODE_RX_STORE_FORWARD |
275 EMAC_MODE_TX_STORE_FORWARD |
276 EMAC_MODE_RX_THRESHOLD_64_BYTES |
277 EMAC_MODE_TX_THRESHOLD_64_BYTES ),
278 0 );
279
280
281 /* Clear any stray MISR1 PHY interrupts that may be set. */
282 ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1 );
283 /* Enable link status change interrupts */
284 ui16Val |=
285 ( EPHY_MISR1_LINKSTATEN |
286 EPHY_MISR1_SPEEDEN |
287 EPHY_MISR1_DUPLEXMEN |
288 EPHY_MISR1_ANCEN
289 );
290 MAP_EMACPHYWrite( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1, ui16Val );
291
292 /* Clear any stray MISR2 PHY interrupts that may be set. */
293 ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR2 );
294
295 /* Configure and enable PHY interrupts */
296 ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_SCR );
297 ui16Val |= ( EPHY_SCR_INTEN_EXT | EPHY_SCR_INTOE_EXT );
298 MAP_EMACPHYWrite( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_SCR, ui16Val );
299
300 /* Read the PHY interrupt status to clear any stray events. */
301 ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1 );
302
303 /* Set MAC filtering options. We receive all broadcast and mui32ticast */
304 /* packets along with those addressed specifically for us. */
305 MAP_EMACFrameFilterSet( EMAC0_BASE, ( EMAC_FRMFILTER_HASH_AND_PERFECT |
306 EMAC_FRMFILTER_PASS_MULTICAST ) );
307
308 /* Set the MAC address */
309 MAP_EMACAddrSet( EMAC0_BASE, 0, &mac_address_bytes[ 0 ] );
310
311 /* Clears any previously asserted interrupts */
312 MAP_EMACIntClear( EMAC0_BASE, EMACIntStatus( EMAC0_BASE, false ) );
313
314 /* Initialize the DMA descriptors */
315 _dma_descriptors_init();
316
317 /* Enable TX/RX */
318 MAP_EMACTxEnable( EMAC0_BASE );
319 MAP_EMACRxEnable( EMAC0_BASE );
320
321 /* Set the interrupt to a lower priority than the OS scheduler interrupts */
322 MAP_IntPrioritySet( INT_EMAC0, ( 6 << ( 8 - configPRIO_BITS ) ) );
323
324 /* Probe the PHY with the stack driver */
325 vMACBProbePhy();
326
327 xMacInitStatus = eMACPass;
328 }
329 }
330 }
331
332 if( eMACPass == xMacInitStatus )
333 {
334 /* Wait for the link status to come up before enabling interrupts */
335 if( xPhyObject.ulLinkStatusMask != 0U )
336 {
337 /* Enable the Ethernet RX and TX interrupt source. */
338 MAP_EMACIntEnable( EMAC0_BASE, ( EMAC_INT_RECEIVE | EMAC_INT_TRANSMIT |
339 EMAC_INT_TX_STOPPED | EMAC_INT_RX_NO_BUFFER |
340 EMAC_INT_RX_STOPPED | EMAC_INT_PHY ) );
341
342 /* Enable EMAC interrupts */
343 MAP_IntEnable( INT_EMAC0 );
344
345 xResult = pdPASS;
346 }
347 }
348
349 return xResult;
350 }
351
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxNetworkBuffer,BaseType_t xReleaseAfterSend)352 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer,
353 BaseType_t xReleaseAfterSend )
354 {
355 BaseType_t success = pdTRUE;
356 tEMACDMADescriptor * dma_descriptor;
357
358 /* As this driver is strictly zero-copy, assert that the stack does not call this function with */
359 /* xReleaseAfterSend as false */
360 configASSERT( 0 != xReleaseAfterSend );
361
362 dma_descriptor = &_tx_descriptors[ _tx_descriptor_list.write ];
363
364 /* If the DMA controller still owns the descriptor, all DMA descriptors are in use, bail out */
365 if( 0U == ( dma_descriptor->ui32CtrlStatus & DES0_RX_CTRL_OWN ) )
366 {
367 /* Assign the buffer to the DMA descriptor */
368 dma_descriptor->pvBuffer1 = pxNetworkBuffer->pucEthernetBuffer;
369
370 /* Inform the DMA of the size of the packet */
371 dma_descriptor->ui32Count = ( pxNetworkBuffer->xDataLength & DES1_TX_CTRL_BUFF1_SIZE_M ) << DES1_TX_CTRL_BUFF1_SIZE_S;
372
373 /* Inform the DMA that this is the first and last segment of the packet, calculate the checksums, the descriptors are */
374 /* chained, and to use interrupts */
375 dma_descriptor->ui32CtrlStatus = DES0_TX_CTRL_FIRST_SEG | DES0_TX_CTRL_IP_ALL_CKHSUMS | DES0_TX_CTRL_CHAINED
376 | DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_REPLACE_CRC;
377
378 /* Advance the index in the list */
379 _tx_descriptor_list.write++;
380
381 /* Wrap around if required */
382 if( _tx_descriptor_list.write == niEMAC_TX_DMA_DESC_COUNT )
383 {
384 _tx_descriptor_list.write = 0;
385 }
386
387 /* Give the DMA descriptor to the DMA controller */
388 dma_descriptor->ui32CtrlStatus |= DES0_TX_CTRL_OWN;
389
390 /* Inform the DMA it has a new descriptor */
391 MAP_EMACTxDMAPollDemand( EMAC0_BASE );
392
393 iptraceNETWORK_INTERFACE_TRANSMIT();
394 }
395 else
396 {
397 /* Release the stack descriptor and buffer to prevent memory leaks. */
398 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
399
400 success = pdFALSE;
401 }
402
403 return success;
404 }
405
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])406 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
407 {
408 BaseType_t i;
409
410 for( i = 0; i < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; i++ )
411 {
412 /* Assign buffers to each descriptor */
413 pxNetworkBuffers[ i ].pucEthernetBuffer = &_network_buffers[ i ][ ipBUFFER_PADDING ];
414
415 /* Set the 'hidden' reference to the descriptor for use in DMA interrupts */
416 *( ( uint32_t * ) &_network_buffers[ i ][ 0 ] ) = ( uint32_t ) &( ( pxNetworkBuffers[ i ] ) );
417 }
418 }
419
_ethernet_mac_get(uint8_t * mac_address_bytes)420 static BaseType_t _ethernet_mac_get( uint8_t * mac_address_bytes )
421 {
422 BaseType_t success = pdPASS;
423 uint32_t mac_address_words[ 2 ] = { 0 };
424
425 /* Attempt to read the MAC address */
426 MAP_FlashUserGet( &mac_address_words[ 0 ], &mac_address_words[ 1 ] );
427
428 /* If the MAC is not set, fail */
429 if( ( 0xFFFFFFFF == mac_address_words[ 0 ] ) || ( 0xFFFFFFFF == mac_address_words[ 1 ] ) )
430 {
431 success = pdFAIL;
432 }
433 else
434 {
435 /* Otherwise return the MAC address in a usable format for the driver */
436 *( mac_address_bytes + 0 ) = ( mac_address_words[ 0 ] >> 0 ) & 0xFF;
437 *( mac_address_bytes + 1 ) = ( mac_address_words[ 0 ] >> 8 ) & 0xFF;
438 *( mac_address_bytes + 2 ) = ( mac_address_words[ 0 ] >> 16 ) & 0xFF;
439 *( mac_address_bytes + 3 ) = ( mac_address_words[ 1 ] >> 0 ) & 0xFF;
440 *( mac_address_bytes + 4 ) = ( mac_address_words[ 1 ] >> 8 ) & 0xFF;
441 *( mac_address_bytes + 5 ) = ( mac_address_words[ 1 ] >> 16 ) & 0xFF;
442 }
443
444 return success;
445 }
446
_dma_descriptors_init(void)447 static void _dma_descriptors_init( void )
448 {
449 uint32_t i;
450 size_t buffer_size_requested;
451 NetworkBufferDescriptor_t * stack_descriptor;
452
453 /* Initialize the TX DMA descriptors */
454 for( i = 0; i < niEMAC_TX_DMA_DESC_COUNT; i++ )
455 {
456 /* Clear the length of the packet */
457 _tx_descriptors[ i ].ui32Count = 0;
458
459 /* Clear the reference to the buffer */
460 _tx_descriptors[ i ].pvBuffer1 = NULL;
461
462 /* Set the next link in the DMA descriptor chain, either the next in the chain or the first descriptor in the event */
463 /* that this is the last descriptor */
464 _tx_descriptors[ i ].DES3.pLink = (
465 ( i == ( niEMAC_TX_DMA_DESC_COUNT - 1 ) ) ?
466 &_tx_descriptors[ 0 ] : &_tx_descriptors[ i + 1 ] );
467 _tx_descriptors[ i ].ui32CtrlStatus = DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_CHAINED
468 | DES0_TX_CTRL_IP_ALL_CKHSUMS;
469 }
470
471 /* Set the TX descriptor index */
472 _tx_descriptor_list.write = 0;
473 _tx_descriptor_list.read = 0;
474
475 for( i = 0; i < niEMAC_RX_DMA_DESC_COUNT; i++ )
476 {
477 stack_descriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 );
478
479 configASSERT( NULL != stack_descriptor );
480
481 /* Get a buffer from the stack and assign it to the DMA Descriptor */
482 _rx_descriptors[ i ].pvBuffer1 = stack_descriptor->pucEthernetBuffer;
483
484 /* Inform the DMA controller that the descriptors are chained and the size of the buffer */
485 _rx_descriptors[ i ].ui32Count = DES1_RX_CTRL_CHAINED | ( ( buffer_size_requested << DES1_TX_CTRL_BUFF1_SIZE_S ) & DES1_TX_CTRL_BUFF1_SIZE_M );
486
487 /* Give the DMA descriptor to the DMA controller */
488 _rx_descriptors[ i ].ui32CtrlStatus = DES0_RX_CTRL_OWN;
489
490 /* Set the next link the DMA descriptor chain */
491 _rx_descriptors[ i ].DES3.pLink = ( ( i == ( niEMAC_RX_DMA_DESC_COUNT - 1 ) ) ? &_rx_descriptors[ 0 ] : &_rx_descriptors[ i + 1 ] );
492 }
493
494 /* Set the RX descriptor index */
495 _rx_descriptor_list.write = 0;
496
497 /* Set the head of the DMA descriptor list in the EMAC peripheral */
498 MAP_EMACTxDMADescriptorListSet( EMAC0_BASE, &_tx_descriptors[ 0 ] );
499 MAP_EMACRxDMADescriptorListSet( EMAC0_BASE, &_rx_descriptors[ 0 ] );
500 }
501
freertos_tcp_ethernet_int(void)502 void freertos_tcp_ethernet_int( void )
503 {
504 uint32_t status;
505 BaseType_t higher_priority_task_woken = pdFALSE;
506
507 /* Read the interrupt status */
508 status = EMACIntStatus( EMAC0_BASE, true );
509
510 /* Handle power management interrupts */
511 if( status & EMAC_INT_POWER_MGMNT )
512 {
513 MAP_EMACTxEnable( EMAC0_BASE );
514 MAP_EMACRxEnable( EMAC0_BASE );
515
516 MAP_EMACPowerManagementStatusGet( EMAC0_BASE );
517
518 status &= ~( EMAC_INT_POWER_MGMNT );
519 }
520
521 if( status )
522 {
523 MAP_EMACIntClear( EMAC0_BASE, status );
524 }
525
526 /* Handle PHY interrupts */
527 if( EMAC_INT_PHY & status )
528 {
529 _process_phy_interrupts();
530 }
531
532 /* Handle Transmit Complete interrupts */
533 if( EMAC_INT_TRANSMIT & status )
534 {
535 xMacInterruptStatus |= eMACInterruptTx;
536 }
537
538 /* Handle Receive interrupts */
539 if( ( EMAC_INT_RECEIVE | EMAC_INT_RX_NO_BUFFER | EMAC_INT_RX_STOPPED ) & status )
540 {
541 xMacInterruptStatus |= eMACInterruptRx;
542 }
543
544 /* If interrupts of concern were found, wake the task if present */
545 if( ( 0 != xMacInterruptStatus ) && ( NULL != _deferred_task_handle ) )
546 {
547 vTaskNotifyGiveFromISR( _deferred_task_handle, &higher_priority_task_woken );
548
549 portYIELD_FROM_ISR( higher_priority_task_woken );
550 }
551 }
552
_process_transmit_complete(void)553 static void _process_transmit_complete( void )
554 {
555 uint32_t i;
556 tEMACDMADescriptor * dma_descriptor;
557 NetworkBufferDescriptor_t * stack_descriptor;
558
559 for( i = 0; ( ( i < _tx_descriptor_list.number_descriptors ) && ( _tx_descriptor_list.read != _tx_descriptor_list.write ) ); i++ )
560 {
561 /* Get a reference to the current DMA descriptor */
562 dma_descriptor = &_tx_descriptors[ _tx_descriptor_list.read ];
563
564 /* If the descriptor is still owned by the DMA controller, exit */
565 if( dma_descriptor->ui32CtrlStatus & DES0_TX_CTRL_OWN )
566 {
567 break;
568 }
569
570 /* Get the 'hidden' reference to the stack descriptor from the buffer */
571 stack_descriptor = pxPacketBuffer_to_NetworkBuffer( dma_descriptor->pvBuffer1 );
572
573 configASSERT( NULL != stack_descriptor );
574
575 /* Release the stack descriptor */
576 vReleaseNetworkBufferAndDescriptor( stack_descriptor );
577
578 _tx_descriptor_list.read++;
579
580 if( _tx_descriptor_list.read == _tx_descriptor_list.number_descriptors )
581 {
582 _tx_descriptor_list.read = 0;
583 }
584 }
585 }
586
_process_received_packet(void)587 static BaseType_t _process_received_packet( void )
588 {
589 NetworkBufferDescriptor_t * new_stack_descriptor;
590 NetworkBufferDescriptor_t * cur_stack_descriptor;
591 tEMACDMADescriptor * dma_descriptor;
592 uint32_t i;
593 IPStackEvent_t event;
594 BaseType_t result = pdTRUE;
595 const TickType_t max_block_time = pdMS_TO_MIN_TICKS( 50 );
596
597 /* Go through the list of RX DMA descriptors */
598 for( i = 0; i < niEMAC_RX_DMA_DESC_COUNT; i++ )
599 {
600 /* Get a reference to the descriptor */
601 dma_descriptor = &_rx_descriptors[ _rx_descriptor_list.write ];
602
603 /* Make sure the buffer is non-null */
604 configASSERT( NULL != dma_descriptor->pvBuffer1 );
605
606 /* If the descriptor is still in use by DMA, stop processing here */
607 if( DES0_RX_CTRL_OWN == ( dma_descriptor->ui32CtrlStatus & DES0_RX_CTRL_OWN ) )
608 {
609 break;
610 }
611
612 /* If there is NOT an error in the frame */
613 if( 0U == ( dma_descriptor->ui32CtrlStatus & DES0_RX_STAT_ERR ) )
614 {
615 /* Get a new empty descriptor */
616 new_stack_descriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, max_block_time );
617
618 /* If a descriptor was provided, else this packet is dropped */
619 if( NULL != new_stack_descriptor )
620 {
621 /* Get a reference to the current stack descriptor held by the DMA descriptor */
622 cur_stack_descriptor = pxPacketBuffer_to_NetworkBuffer( dma_descriptor->pvBuffer1 );
623
624 /* Set the length of the buffer on the current descriptor */
625 cur_stack_descriptor->xDataLength = ( dma_descriptor->ui32CtrlStatus & DES0_RX_STAT_FRAME_LENGTH_M ) >> DES0_RX_STAT_FRAME_LENGTH_S;
626
627 /* Assign the new stack descriptor to the DMA descriptor */
628 dma_descriptor->pvBuffer1 = new_stack_descriptor->pucEthernetBuffer;
629
630 /* Ask the stack if it wants to process the frame. */
631 if( eProcessBuffer == eConsiderFrameForProcessing( cur_stack_descriptor->pucEthernetBuffer ) )
632 {
633 /* Setup the event */
634 event.eEventType = eNetworkRxEvent;
635 event.pvData = cur_stack_descriptor;
636
637 /* Forward the event */
638 if( pdFALSE == xSendEventStructToIPTask( &event, 0 ) )
639 {
640 /* Release the buffer if an error was encountered */
641 vReleaseNetworkBufferAndDescriptor( cur_stack_descriptor );
642
643 iptraceETHERNET_RX_EVENT_LOST();
644 }
645 else
646 {
647 iptraceNETWORK_INTERFACE_RECEIVE();
648
649 result = pdTRUE;
650 }
651 }
652 else
653 {
654 /* Free the descriptor */
655 vReleaseNetworkBufferAndDescriptor( cur_stack_descriptor );
656 }
657 } /* end if descriptor is available */
658 else
659 {
660 /* No stack descriptor was available for the next RX DMA descriptor so this packet */
661 /* is dropped */
662
663 /* Mark the RX event as lost */
664 iptraceETHERNET_RX_EVENT_LOST();
665 }
666 } /* end if frame had error. In this case, give the buffer back to the DMA for the next RX */
667
668 /* Set up the DMA descriptor for the next receive transaction */
669 dma_descriptor->ui32Count = DES1_RX_CTRL_CHAINED | ipTOTAL_ETHERNET_FRAME_SIZE;
670 dma_descriptor->ui32CtrlStatus = DES0_RX_CTRL_OWN;
671
672 _rx_descriptor_list.write++;
673
674 if( _rx_descriptor_list.write == _rx_descriptor_list.number_descriptors )
675 {
676 _rx_descriptor_list.write = 0;
677 }
678 }
679
680 return result;
681 }
682
683 /**
684 * This deferred interrupt handler process changes from the PHY auto-negotiation to configure the
685 * MAC as appropriate.
686 */
_process_phy_interrupts(void)687 static void _process_phy_interrupts( void )
688 {
689 uint16_t value;
690 uint16_t status;
691 uint32_t configuration;
692 uint32_t mode;
693 uint32_t max_frame_size;
694
695 /* Read the PHY interrupts status */
696 value = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1 );
697 status = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_STS );
698
699 if( value & ( EPHY_MISR1_SPEED | EPHY_MISR1_DUPLEXM | EPHY_MISR1_ANC ) )
700 {
701 /* If the speed or duplex has changed */
702
703 MAP_EMACConfigGet( EMAC0_BASE, &configuration, &mode, &max_frame_size );
704
705 if( status & EPHY_STS_SPEED )
706 {
707 configuration &= ~EMAC_CONFIG_100MBPS;
708 }
709 else
710 {
711 configuration |= EMAC_CONFIG_100MBPS;
712 }
713
714 if( status & EPHY_STS_DUPLEX )
715 {
716 configuration |= EMAC_CONFIG_FULL_DUPLEX;
717 }
718 else
719 {
720 configuration &= ~EMAC_CONFIG_FULL_DUPLEX;
721 }
722
723 MAP_EMACConfigSet( EMAC0_BASE, configuration, mode, max_frame_size );
724 }
725 }
726
_deferred_task(void * parameters)727 static void _deferred_task( void * parameters )
728 {
729 BaseType_t had_reception;
730 IPStackEvent_t link_down_event;
731 const TickType_t max_block_time = pdMS_TO_TICKS( 100 );
732
733 /* Ignore parameters */
734 ( void ) parameters;
735
736 for( ; ; )
737 {
738 had_reception = pdFALSE;
739
740 ulTaskNotifyTake( pdTRUE, max_block_time );
741
742 if( eMACInterruptTx == ( xMacInterruptStatus & eMACInterruptTx ) )
743 {
744 xMacInterruptStatus &= ~( eMACInterruptTx );
745
746 _process_transmit_complete();
747 }
748
749 if( eMACInterruptRx == ( xMacInterruptStatus & eMACInterruptRx ) )
750 {
751 xMacInterruptStatus &= ~( eMACInterruptRx );
752
753 had_reception = _process_received_packet();
754 }
755
756 if( pdTRUE == xPhyCheckLinkStatus( &xPhyObject, had_reception ) )
757 {
758 /* The link has gone done */
759 if( 0 == xPhyObject.ulLinkStatusMask )
760 {
761 link_down_event.eEventType = eNetworkDownEvent;
762 link_down_event.pvData = NULL;
763
764 xSendEventStructToIPTask( &link_down_event, 0 );
765 }
766 }
767 }
768 }
769
vMACBProbePhy(void)770 static void vMACBProbePhy( void )
771 {
772 vPhyInitialise( &xPhyObject, xTM4C_PhyRead, xTM4C_PhyWrite );
773 xPhyDiscover( &xPhyObject );
774 xPhyConfigure( &xPhyObject, &xPHYProperties );
775 }
776
xTM4C_PhyRead(BaseType_t xAddress,BaseType_t xRegister,uint32_t * pulValue)777 static BaseType_t xTM4C_PhyRead( BaseType_t xAddress,
778 BaseType_t xRegister,
779 uint32_t * pulValue )
780 {
781 *pulValue = MAP_EMACPHYRead( EMAC0_BASE, ( uint8_t ) xAddress, ( uint8_t ) xRegister );
782
783 return 0;
784 }
785
xTM4C_PhyWrite(BaseType_t xAddress,BaseType_t xRegister,uint32_t ulValue)786 static BaseType_t xTM4C_PhyWrite( BaseType_t xAddress,
787 BaseType_t xRegister,
788 uint32_t ulValue )
789 {
790 MAP_EMACPHYWrite( EMAC0_BASE, ( uint8_t ) xAddress, ( uint8_t ) xRegister, ulValue );
791
792 return 0;
793 }
794