mirror of
https://github.com/FreeRTOS/FreeRTOS-Plus-TCP
synced 2025-10-21 15:10:39 +08:00
794 lines
27 KiB
C
794 lines
27 KiB
C
/**
|
|
* @file: NetworkInterface.c
|
|
* @author: jscott <jscott@hotstart.com>
|
|
* @date: Feb 1, 2022
|
|
* @copyright: Hotstart 2022 Hotstart Thermal Management. All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
* the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* @brief:Network Interface driver for the Texas Instruments TM4C line of microcontrollers.
|
|
*
|
|
* This driver was written and tested with the TM4C1294NCPDT, which includes a built-in MAC and
|
|
* PHY. The expectation is that this driver should function correctly across all the MAC/PHY
|
|
* integrated parts of the TM4C129X parts.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "inc/hw_ints.h"
|
|
#include "inc/hw_emac.h"
|
|
#include "inc/hw_memmap.h"
|
|
#include "inc/hw_nvic.h"
|
|
|
|
#include "driverlib/flash.h"
|
|
#include "driverlib/interrupt.h"
|
|
#include "driverlib/gpio.h"
|
|
#include "driverlib/rom_map.h"
|
|
#include "driverlib/sysctl.h"
|
|
#include "driverlib/systick.h"
|
|
#include "driverlib/emac.h"
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "queue.h"
|
|
|
|
#include "FreeRTOS_IP.h"
|
|
#include "FreeRTOS_Sockets.h"
|
|
#include "FreeRTOS_IP_Private.h"
|
|
#include "NetworkBufferManagement.h"
|
|
#include "NetworkInterface.h"
|
|
#include "phyHandling.h"
|
|
|
|
#define BUFFER_SIZE ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
|
|
#define BUFFER_SIZE_ROUNDED_UP ( ( BUFFER_SIZE + 7 ) & ~0x7UL )
|
|
#define PHY_PHYS_ADDR 0
|
|
|
|
#ifndef niEMAC_SYSCONFIG_HZ
|
|
#define niEMAC_SYSCONFIG_HZ configCPU_CLOCK_HZ
|
|
#endif
|
|
|
|
#ifndef niEMAC_TX_DMA_DESC_COUNT
|
|
#define niEMAC_TX_DMA_DESC_COUNT 8
|
|
#endif
|
|
|
|
#ifndef niEMAC_RX_DMA_DESC_COUNT
|
|
#define niEMAC_RX_DMA_DESC_COUNT 8
|
|
#endif
|
|
|
|
#if ipconfigUSE_LINKED_RX_MESSAGES
|
|
#error Linked RX Messages are not supported by this driver
|
|
#endif
|
|
|
|
/* Default the size of the stack used by the EMAC deferred handler task to twice
|
|
* the size of the stack used by the idle task - but allow this to be overridden in
|
|
* FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */
|
|
#ifndef configEMAC_TASK_STACK_SIZE
|
|
#define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE )
|
|
#endif
|
|
|
|
#ifndef niEMAC_HANDLER_TASK_PRIORITY
|
|
#define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1
|
|
#endif
|
|
|
|
#if !defined( ipconfigETHERNET_AN_ENABLE )
|
|
/* Enable auto-negotiation */
|
|
#define ipconfigETHERNET_AN_ENABLE 1
|
|
#endif
|
|
|
|
#if !defined( ipconfigETHERNET_USE_100MB )
|
|
#define ipconfigETHERNET_USE_100MB 1
|
|
#endif
|
|
|
|
#if !defined( ipconfigETHERNET_USE_FULL_DUPLEX )
|
|
#define ipconfigETHERNET_USE_FULL_DUPLEX 1
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
uint32_t number_descriptors;
|
|
uint32_t write;
|
|
uint32_t read;
|
|
} tDescriptorList;
|
|
|
|
typedef enum
|
|
{
|
|
eMACInit, /* Must initialise MAC. */
|
|
eMACPass, /* Initialisation was successful. */
|
|
eMACFailed, /* Initialisation failed. */
|
|
} eMAC_INIT_STATUS_TYPE;
|
|
|
|
typedef enum
|
|
{
|
|
eMACInterruptNone = 0, /* No interrupts need servicing */
|
|
eMACInterruptRx = ( 1 << 0 ), /* Service RX interrupt */
|
|
eMACInterruptTx = ( 1 << 1 ), /* Service TX interrupt */
|
|
} eMAC_INTERRUPT_STATUS_TYPE;
|
|
|
|
static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit;
|
|
|
|
static volatile eMAC_INTERRUPT_STATUS_TYPE xMacInterruptStatus = eMACInterruptNone;
|
|
|
|
static tEMACDMADescriptor _tx_descriptors[ niEMAC_TX_DMA_DESC_COUNT ];
|
|
static tEMACDMADescriptor _rx_descriptors[ niEMAC_RX_DMA_DESC_COUNT ];
|
|
|
|
static tDescriptorList _tx_descriptor_list = { .number_descriptors = niEMAC_TX_DMA_DESC_COUNT, 0 };
|
|
static tDescriptorList _rx_descriptor_list = { .number_descriptors = niEMAC_RX_DMA_DESC_COUNT, 0 };
|
|
|
|
static uint8_t _network_buffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ BUFFER_SIZE_ROUNDED_UP ];
|
|
#pragma DATA_ALIGN(_network_buffers, 4)
|
|
|
|
static EthernetPhy_t xPhyObject;
|
|
|
|
static TaskHandle_t _deferred_task_handle = NULL;
|
|
|
|
const PhyProperties_t xPHYProperties =
|
|
{
|
|
#if ( ipconfigETHERNET_AN_ENABLE != 0 )
|
|
.ucSpeed = PHY_SPEED_AUTO,
|
|
.ucDuplex = PHY_DUPLEX_AUTO,
|
|
#else
|
|
#if ( ipconfigETHERNET_USE_100MB != 0 )
|
|
.ucSpeed = PHY_SPEED_100,
|
|
#else
|
|
.ucSpeed = PHY_SPEED_10,
|
|
#endif
|
|
|
|
#if ( ipconfigETHERNET_USE_FULL_DUPLEX != 0 )
|
|
.ucDuplex = PHY_DUPLEX_FULL,
|
|
#else
|
|
.ucDuplex = PHY_DUPLEX_HALF,
|
|
#endif
|
|
#endif /* if ( ipconfigETHERNET_AN_ENABLE != 0 ) */
|
|
};
|
|
|
|
/**
|
|
* Reads the Ethernet MAC from user Flash.
|
|
* @param mac_address_bytes[out] The byte array which will hold the MAC address
|
|
* @return pdPASS on success, pdFAIL if the MAC is invalid from user Flash
|
|
*/
|
|
static BaseType_t _ethernet_mac_get( uint8_t * mac_address_bytes );
|
|
|
|
/**
|
|
* Initialize DMA descriptors
|
|
*/
|
|
static void _dma_descriptors_init( void );
|
|
|
|
/**
|
|
* Frees previously sent network buffers
|
|
*/
|
|
static void _process_transmit_complete( void );
|
|
|
|
/**
|
|
* Processes received packets and forwards those acceptable to the network stack
|
|
*/
|
|
static BaseType_t _process_received_packet( void );
|
|
|
|
/**
|
|
* Processes PHY interrupts.
|
|
*/
|
|
static void _process_phy_interrupts( void );
|
|
|
|
/**
|
|
* Thread to forward received packets from the ISR to the network stack
|
|
* @param parameters Not used
|
|
*/
|
|
static void _deferred_task( void * parameters );
|
|
|
|
/**
|
|
* Phy read implementation for the TM4C
|
|
* @param xAddress
|
|
* @param xRegister
|
|
* @param pulValue
|
|
* @return
|
|
*/
|
|
static BaseType_t xTM4C_PhyRead( BaseType_t xAddress,
|
|
BaseType_t xRegister,
|
|
uint32_t * pulValue );
|
|
|
|
/**
|
|
* Phy write implementation for the TM4C
|
|
* @param xAddress
|
|
* @param xRegister
|
|
* @param ulValue
|
|
* @return
|
|
*/
|
|
static BaseType_t xTM4C_PhyWrite( BaseType_t xAddress,
|
|
BaseType_t xRegister,
|
|
uint32_t ulValue );
|
|
|
|
/**
|
|
* Probe the PHY
|
|
*/
|
|
static void vMACBProbePhy( void );
|
|
|
|
BaseType_t xNetworkInterfaceInitialise( void )
|
|
{
|
|
uint8_t mac_address_bytes[ 6 ];
|
|
uint16_t ui16Val;
|
|
BaseType_t xResult = pdFAIL;
|
|
|
|
if( eMACInit == xMacInitStatus )
|
|
{
|
|
/* Create the RX packet forwarding task */
|
|
if( pdFAIL == xTaskCreate( _deferred_task, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &_deferred_task_handle ) )
|
|
{
|
|
xMacInitStatus = eMACFailed;
|
|
}
|
|
else
|
|
{
|
|
/* Read the MAC from user Flash */
|
|
if( pdPASS != _ethernet_mac_get( &mac_address_bytes[ 0 ] ) )
|
|
{
|
|
xMacInitStatus = eMACFailed;
|
|
}
|
|
else
|
|
{
|
|
MAP_SysCtlPeripheralReset( SYSCTL_PERIPH_EMAC0 );
|
|
|
|
while( !MAP_SysCtlPeripheralReady( SYSCTL_PERIPH_EMAC0 ) )
|
|
{
|
|
}
|
|
|
|
MAP_SysCtlPeripheralReset( SYSCTL_PERIPH_EPHY0 );
|
|
|
|
while( !MAP_SysCtlPeripheralReady( SYSCTL_PERIPH_EPHY0 ) )
|
|
{
|
|
}
|
|
|
|
MAP_EMACInit( EMAC0_BASE, niEMAC_SYSCONFIG_HZ,
|
|
EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, 4,
|
|
4, 0 );
|
|
|
|
MAP_EMACConfigSet(
|
|
EMAC0_BASE,
|
|
(
|
|
EMAC_CONFIG_100MBPS |
|
|
EMAC_CONFIG_FULL_DUPLEX |
|
|
EMAC_CONFIG_CHECKSUM_OFFLOAD |
|
|
EMAC_CONFIG_7BYTE_PREAMBLE |
|
|
EMAC_CONFIG_IF_GAP_96BITS |
|
|
EMAC_CONFIG_USE_MACADDR0 |
|
|
EMAC_CONFIG_SA_FROM_DESCRIPTOR |
|
|
EMAC_CONFIG_BO_LIMIT_1024 |
|
|
EMAC_CONFIG_STRIP_CRC
|
|
),
|
|
(
|
|
EMAC_MODE_RX_STORE_FORWARD |
|
|
EMAC_MODE_TX_STORE_FORWARD |
|
|
EMAC_MODE_RX_THRESHOLD_64_BYTES |
|
|
EMAC_MODE_TX_THRESHOLD_64_BYTES ),
|
|
0 );
|
|
|
|
|
|
/* Clear any stray MISR1 PHY interrupts that may be set. */
|
|
ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1 );
|
|
/* Enable link status change interrupts */
|
|
ui16Val |=
|
|
( EPHY_MISR1_LINKSTATEN |
|
|
EPHY_MISR1_SPEEDEN |
|
|
EPHY_MISR1_DUPLEXMEN |
|
|
EPHY_MISR1_ANCEN
|
|
);
|
|
MAP_EMACPHYWrite( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1, ui16Val );
|
|
|
|
/* Clear any stray MISR2 PHY interrupts that may be set. */
|
|
ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR2 );
|
|
|
|
/* Configure and enable PHY interrupts */
|
|
ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_SCR );
|
|
ui16Val |= ( EPHY_SCR_INTEN_EXT | EPHY_SCR_INTOE_EXT );
|
|
MAP_EMACPHYWrite( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_SCR, ui16Val );
|
|
|
|
/* Read the PHY interrupt status to clear any stray events. */
|
|
ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1 );
|
|
|
|
/* Set MAC filtering options. We receive all broadcast and mui32ticast */
|
|
/* packets along with those addressed specifically for us. */
|
|
MAP_EMACFrameFilterSet( EMAC0_BASE, ( EMAC_FRMFILTER_HASH_AND_PERFECT |
|
|
EMAC_FRMFILTER_PASS_MULTICAST ) );
|
|
|
|
/* Set the MAC address */
|
|
MAP_EMACAddrSet( EMAC0_BASE, 0, &mac_address_bytes[ 0 ] );
|
|
|
|
/* Clears any previously asserted interrupts */
|
|
MAP_EMACIntClear( EMAC0_BASE, EMACIntStatus( EMAC0_BASE, false ) );
|
|
|
|
/* Initialize the DMA descriptors */
|
|
_dma_descriptors_init();
|
|
|
|
/* Enable TX/RX */
|
|
MAP_EMACTxEnable( EMAC0_BASE );
|
|
MAP_EMACRxEnable( EMAC0_BASE );
|
|
|
|
/* Set the interrupt to a lower priority than the OS scheduler interrupts */
|
|
MAP_IntPrioritySet( INT_EMAC0, ( 6 << ( 8 - configPRIO_BITS ) ) );
|
|
|
|
/* Probe the PHY with the stack driver */
|
|
vMACBProbePhy();
|
|
|
|
xMacInitStatus = eMACPass;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( eMACPass == xMacInitStatus )
|
|
{
|
|
/* Wait for the link status to come up before enabling interrupts */
|
|
if( xPhyObject.ulLinkStatusMask != 0U )
|
|
{
|
|
/* Enable the Ethernet RX and TX interrupt source. */
|
|
MAP_EMACIntEnable( EMAC0_BASE, ( EMAC_INT_RECEIVE | EMAC_INT_TRANSMIT |
|
|
EMAC_INT_TX_STOPPED | EMAC_INT_RX_NO_BUFFER |
|
|
EMAC_INT_RX_STOPPED | EMAC_INT_PHY ) );
|
|
|
|
/* Enable EMAC interrupts */
|
|
MAP_IntEnable( INT_EMAC0 );
|
|
|
|
xResult = pdPASS;
|
|
}
|
|
}
|
|
|
|
return xResult;
|
|
}
|
|
|
|
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer,
|
|
BaseType_t xReleaseAfterSend )
|
|
{
|
|
BaseType_t success = pdTRUE;
|
|
tEMACDMADescriptor * dma_descriptor;
|
|
|
|
/* As this driver is strictly zero-copy, assert that the stack does not call this function with */
|
|
/* xReleaseAfterSend as false */
|
|
configASSERT( 0 != xReleaseAfterSend );
|
|
|
|
dma_descriptor = &_tx_descriptors[ _tx_descriptor_list.write ];
|
|
|
|
/* If the DMA controller still owns the descriptor, all DMA descriptors are in use, bail out */
|
|
if( 0U == ( dma_descriptor->ui32CtrlStatus & DES0_RX_CTRL_OWN ) )
|
|
{
|
|
/* Assign the buffer to the DMA descriptor */
|
|
dma_descriptor->pvBuffer1 = pxNetworkBuffer->pucEthernetBuffer;
|
|
|
|
/* Inform the DMA of the size of the packet */
|
|
dma_descriptor->ui32Count = ( pxNetworkBuffer->xDataLength & DES1_TX_CTRL_BUFF1_SIZE_M ) << DES1_TX_CTRL_BUFF1_SIZE_S;
|
|
|
|
/* Inform the DMA that this is the first and last segment of the packet, calculate the checksums, the descriptors are */
|
|
/* chained, and to use interrupts */
|
|
dma_descriptor->ui32CtrlStatus = DES0_TX_CTRL_FIRST_SEG | DES0_TX_CTRL_IP_ALL_CKHSUMS | DES0_TX_CTRL_CHAINED
|
|
| DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_REPLACE_CRC;
|
|
|
|
/* Advance the index in the list */
|
|
_tx_descriptor_list.write++;
|
|
|
|
/* Wrap around if required */
|
|
if( _tx_descriptor_list.write == niEMAC_TX_DMA_DESC_COUNT )
|
|
{
|
|
_tx_descriptor_list.write = 0;
|
|
}
|
|
|
|
/* Give the DMA descriptor to the DMA controller */
|
|
dma_descriptor->ui32CtrlStatus |= DES0_TX_CTRL_OWN;
|
|
|
|
/* Inform the DMA it has a new descriptor */
|
|
MAP_EMACTxDMAPollDemand( EMAC0_BASE );
|
|
|
|
iptraceNETWORK_INTERFACE_TRANSMIT();
|
|
}
|
|
else
|
|
{
|
|
/* Release the stack descriptor and buffer to prevent memory leaks. */
|
|
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
|
|
|
|
success = pdFALSE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
|
|
{
|
|
BaseType_t i;
|
|
|
|
for( i = 0; i < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; i++ )
|
|
{
|
|
/* Assign buffers to each descriptor */
|
|
pxNetworkBuffers[ i ].pucEthernetBuffer = &_network_buffers[ i ][ ipBUFFER_PADDING ];
|
|
|
|
/* Set the 'hidden' reference to the descriptor for use in DMA interrupts */
|
|
*( ( uint32_t * ) &_network_buffers[ i ][ 0 ] ) = ( uint32_t ) &( ( pxNetworkBuffers[ i ] ) );
|
|
}
|
|
}
|
|
|
|
static BaseType_t _ethernet_mac_get( uint8_t * mac_address_bytes )
|
|
{
|
|
BaseType_t success = pdPASS;
|
|
uint32_t mac_address_words[ 2 ] = { 0 };
|
|
|
|
/* Attempt to read the MAC address */
|
|
MAP_FlashUserGet( &mac_address_words[ 0 ], &mac_address_words[ 1 ] );
|
|
|
|
/* If the MAC is not set, fail */
|
|
if( ( 0xFFFFFFFF == mac_address_words[ 0 ] ) || ( 0xFFFFFFFF == mac_address_words[ 1 ] ) )
|
|
{
|
|
success = pdFAIL;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise return the MAC address in a usable format for the driver */
|
|
*( mac_address_bytes + 0 ) = ( mac_address_words[ 0 ] >> 0 ) & 0xFF;
|
|
*( mac_address_bytes + 1 ) = ( mac_address_words[ 0 ] >> 8 ) & 0xFF;
|
|
*( mac_address_bytes + 2 ) = ( mac_address_words[ 0 ] >> 16 ) & 0xFF;
|
|
*( mac_address_bytes + 3 ) = ( mac_address_words[ 1 ] >> 0 ) & 0xFF;
|
|
*( mac_address_bytes + 4 ) = ( mac_address_words[ 1 ] >> 8 ) & 0xFF;
|
|
*( mac_address_bytes + 5 ) = ( mac_address_words[ 1 ] >> 16 ) & 0xFF;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static void _dma_descriptors_init( void )
|
|
{
|
|
uint32_t i;
|
|
size_t buffer_size_requested;
|
|
NetworkBufferDescriptor_t * stack_descriptor;
|
|
|
|
/* Initialize the TX DMA descriptors */
|
|
for( i = 0; i < niEMAC_TX_DMA_DESC_COUNT; i++ )
|
|
{
|
|
/* Clear the length of the packet */
|
|
_tx_descriptors[ i ].ui32Count = 0;
|
|
|
|
/* Clear the reference to the buffer */
|
|
_tx_descriptors[ i ].pvBuffer1 = NULL;
|
|
|
|
/* Set the next link in the DMA descriptor chain, either the next in the chain or the first descriptor in the event */
|
|
/* that this is the last descriptor */
|
|
_tx_descriptors[ i ].DES3.pLink = (
|
|
( i == ( niEMAC_TX_DMA_DESC_COUNT - 1 ) ) ?
|
|
&_tx_descriptors[ 0 ] : &_tx_descriptors[ i + 1 ] );
|
|
_tx_descriptors[ i ].ui32CtrlStatus = DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_CHAINED
|
|
| DES0_TX_CTRL_IP_ALL_CKHSUMS;
|
|
}
|
|
|
|
/* Set the TX descriptor index */
|
|
_tx_descriptor_list.write = 0;
|
|
_tx_descriptor_list.read = 0;
|
|
|
|
for( i = 0; i < niEMAC_RX_DMA_DESC_COUNT; i++ )
|
|
{
|
|
stack_descriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 );
|
|
|
|
configASSERT( NULL != stack_descriptor );
|
|
|
|
/* Get a buffer from the stack and assign it to the DMA Descriptor */
|
|
_rx_descriptors[ i ].pvBuffer1 = stack_descriptor->pucEthernetBuffer;
|
|
|
|
/* Inform the DMA controller that the descriptors are chained and the size of the buffer */
|
|
_rx_descriptors[ i ].ui32Count = DES1_RX_CTRL_CHAINED | ( ( buffer_size_requested << DES1_TX_CTRL_BUFF1_SIZE_S ) & DES1_TX_CTRL_BUFF1_SIZE_M );
|
|
|
|
/* Give the DMA descriptor to the DMA controller */
|
|
_rx_descriptors[ i ].ui32CtrlStatus = DES0_RX_CTRL_OWN;
|
|
|
|
/* Set the next link the DMA descriptor chain */
|
|
_rx_descriptors[ i ].DES3.pLink = ( ( i == ( niEMAC_RX_DMA_DESC_COUNT - 1 ) ) ? &_rx_descriptors[ 0 ] : &_rx_descriptors[ i + 1 ] );
|
|
}
|
|
|
|
/* Set the RX descriptor index */
|
|
_rx_descriptor_list.write = 0;
|
|
|
|
/* Set the head of the DMA descriptor list in the EMAC peripheral */
|
|
MAP_EMACTxDMADescriptorListSet( EMAC0_BASE, &_tx_descriptors[ 0 ] );
|
|
MAP_EMACRxDMADescriptorListSet( EMAC0_BASE, &_rx_descriptors[ 0 ] );
|
|
}
|
|
|
|
void freertos_tcp_ethernet_int( void )
|
|
{
|
|
uint32_t status;
|
|
BaseType_t higher_priority_task_woken = pdFALSE;
|
|
|
|
/* Read the interrupt status */
|
|
status = EMACIntStatus( EMAC0_BASE, true );
|
|
|
|
/* Handle power management interrupts */
|
|
if( status & EMAC_INT_POWER_MGMNT )
|
|
{
|
|
MAP_EMACTxEnable( EMAC0_BASE );
|
|
MAP_EMACRxEnable( EMAC0_BASE );
|
|
|
|
MAP_EMACPowerManagementStatusGet( EMAC0_BASE );
|
|
|
|
status &= ~( EMAC_INT_POWER_MGMNT );
|
|
}
|
|
|
|
if( status )
|
|
{
|
|
MAP_EMACIntClear( EMAC0_BASE, status );
|
|
}
|
|
|
|
/* Handle PHY interrupts */
|
|
if( EMAC_INT_PHY & status )
|
|
{
|
|
_process_phy_interrupts();
|
|
}
|
|
|
|
/* Handle Transmit Complete interrupts */
|
|
if( EMAC_INT_TRANSMIT & status )
|
|
{
|
|
xMacInterruptStatus |= eMACInterruptTx;
|
|
}
|
|
|
|
/* Handle Receive interrupts */
|
|
if( ( EMAC_INT_RECEIVE | EMAC_INT_RX_NO_BUFFER | EMAC_INT_RX_STOPPED ) & status )
|
|
{
|
|
xMacInterruptStatus |= eMACInterruptRx;
|
|
}
|
|
|
|
/* If interrupts of concern were found, wake the task if present */
|
|
if( ( 0 != xMacInterruptStatus ) && ( NULL != _deferred_task_handle ) )
|
|
{
|
|
vTaskNotifyGiveFromISR( _deferred_task_handle, &higher_priority_task_woken );
|
|
|
|
portYIELD_FROM_ISR( higher_priority_task_woken );
|
|
}
|
|
}
|
|
|
|
static void _process_transmit_complete( void )
|
|
{
|
|
uint32_t i;
|
|
tEMACDMADescriptor * dma_descriptor;
|
|
NetworkBufferDescriptor_t * stack_descriptor;
|
|
|
|
for( i = 0; ( ( i < _tx_descriptor_list.number_descriptors ) && ( _tx_descriptor_list.read != _tx_descriptor_list.write ) ); i++ )
|
|
{
|
|
/* Get a reference to the current DMA descriptor */
|
|
dma_descriptor = &_tx_descriptors[ _tx_descriptor_list.read ];
|
|
|
|
/* If the descriptor is still owned by the DMA controller, exit */
|
|
if( dma_descriptor->ui32CtrlStatus & DES0_TX_CTRL_OWN )
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Get the 'hidden' reference to the stack descriptor from the buffer */
|
|
stack_descriptor = pxPacketBuffer_to_NetworkBuffer( dma_descriptor->pvBuffer1 );
|
|
|
|
configASSERT( NULL != stack_descriptor );
|
|
|
|
/* Release the stack descriptor */
|
|
vReleaseNetworkBufferAndDescriptor( stack_descriptor );
|
|
|
|
_tx_descriptor_list.read++;
|
|
|
|
if( _tx_descriptor_list.read == _tx_descriptor_list.number_descriptors )
|
|
{
|
|
_tx_descriptor_list.read = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static BaseType_t _process_received_packet( void )
|
|
{
|
|
NetworkBufferDescriptor_t * new_stack_descriptor;
|
|
NetworkBufferDescriptor_t * cur_stack_descriptor;
|
|
tEMACDMADescriptor * dma_descriptor;
|
|
uint32_t i;
|
|
IPStackEvent_t event;
|
|
BaseType_t result = pdTRUE;
|
|
const TickType_t max_block_time = pdMS_TO_MIN_TICKS( 50 );
|
|
|
|
/* Go through the list of RX DMA descriptors */
|
|
for( i = 0; i < niEMAC_RX_DMA_DESC_COUNT; i++ )
|
|
{
|
|
/* Get a reference to the descriptor */
|
|
dma_descriptor = &_rx_descriptors[ _rx_descriptor_list.write ];
|
|
|
|
/* Make sure the buffer is non-null */
|
|
configASSERT( NULL != dma_descriptor->pvBuffer1 );
|
|
|
|
/* If the descriptor is still in use by DMA, stop processing here */
|
|
if( DES0_RX_CTRL_OWN == ( dma_descriptor->ui32CtrlStatus & DES0_RX_CTRL_OWN ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* If there is NOT an error in the frame */
|
|
if( 0U == ( dma_descriptor->ui32CtrlStatus & DES0_RX_STAT_ERR ) )
|
|
{
|
|
/* Get a new empty descriptor */
|
|
new_stack_descriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, max_block_time );
|
|
|
|
/* If a descriptor was provided, else this packet is dropped */
|
|
if( NULL != new_stack_descriptor )
|
|
{
|
|
/* Get a reference to the current stack descriptor held by the DMA descriptor */
|
|
cur_stack_descriptor = pxPacketBuffer_to_NetworkBuffer( dma_descriptor->pvBuffer1 );
|
|
|
|
/* Set the length of the buffer on the current descriptor */
|
|
cur_stack_descriptor->xDataLength = ( dma_descriptor->ui32CtrlStatus & DES0_RX_STAT_FRAME_LENGTH_M ) >> DES0_RX_STAT_FRAME_LENGTH_S;
|
|
|
|
/* Assign the new stack descriptor to the DMA descriptor */
|
|
dma_descriptor->pvBuffer1 = new_stack_descriptor->pucEthernetBuffer;
|
|
|
|
/* Ask the stack if it wants to process the frame. */
|
|
if( eProcessBuffer == eConsiderFrameForProcessing( cur_stack_descriptor->pucEthernetBuffer ) )
|
|
{
|
|
/* Setup the event */
|
|
event.eEventType = eNetworkRxEvent;
|
|
event.pvData = cur_stack_descriptor;
|
|
|
|
/* Forward the event */
|
|
if( pdFALSE == xSendEventStructToIPTask( &event, 0 ) )
|
|
{
|
|
/* Release the buffer if an error was encountered */
|
|
vReleaseNetworkBufferAndDescriptor( cur_stack_descriptor );
|
|
|
|
iptraceETHERNET_RX_EVENT_LOST();
|
|
}
|
|
else
|
|
{
|
|
iptraceNETWORK_INTERFACE_RECEIVE();
|
|
|
|
result = pdTRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Free the descriptor */
|
|
vReleaseNetworkBufferAndDescriptor( cur_stack_descriptor );
|
|
}
|
|
} /* end if descriptor is available */
|
|
else
|
|
{
|
|
/* No stack descriptor was available for the next RX DMA descriptor so this packet */
|
|
/* is dropped */
|
|
|
|
/* Mark the RX event as lost */
|
|
iptraceETHERNET_RX_EVENT_LOST();
|
|
}
|
|
} /* end if frame had error. In this case, give the buffer back to the DMA for the next RX */
|
|
|
|
/* Set up the DMA descriptor for the next receive transaction */
|
|
dma_descriptor->ui32Count = DES1_RX_CTRL_CHAINED | ipTOTAL_ETHERNET_FRAME_SIZE;
|
|
dma_descriptor->ui32CtrlStatus = DES0_RX_CTRL_OWN;
|
|
|
|
_rx_descriptor_list.write++;
|
|
|
|
if( _rx_descriptor_list.write == _rx_descriptor_list.number_descriptors )
|
|
{
|
|
_rx_descriptor_list.write = 0;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This deferred interrupt handler process changes from the PHY auto-negotiation to configure the
|
|
* MAC as appropriate.
|
|
*/
|
|
static void _process_phy_interrupts( void )
|
|
{
|
|
uint16_t value;
|
|
uint16_t status;
|
|
uint32_t configuration;
|
|
uint32_t mode;
|
|
uint32_t max_frame_size;
|
|
|
|
/* Read the PHY interrupts status */
|
|
value = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1 );
|
|
status = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_STS );
|
|
|
|
if( value & ( EPHY_MISR1_SPEED | EPHY_MISR1_DUPLEXM | EPHY_MISR1_ANC ) )
|
|
{
|
|
/* If the speed or duplex has changed */
|
|
|
|
MAP_EMACConfigGet( EMAC0_BASE, &configuration, &mode, &max_frame_size );
|
|
|
|
if( status & EPHY_STS_SPEED )
|
|
{
|
|
configuration &= ~EMAC_CONFIG_100MBPS;
|
|
}
|
|
else
|
|
{
|
|
configuration |= EMAC_CONFIG_100MBPS;
|
|
}
|
|
|
|
if( status & EPHY_STS_DUPLEX )
|
|
{
|
|
configuration |= EMAC_CONFIG_FULL_DUPLEX;
|
|
}
|
|
else
|
|
{
|
|
configuration &= ~EMAC_CONFIG_FULL_DUPLEX;
|
|
}
|
|
|
|
MAP_EMACConfigSet( EMAC0_BASE, configuration, mode, max_frame_size );
|
|
}
|
|
}
|
|
|
|
static void _deferred_task( void * parameters )
|
|
{
|
|
BaseType_t had_reception;
|
|
IPStackEvent_t link_down_event;
|
|
const TickType_t max_block_time = pdMS_TO_TICKS( 100 );
|
|
|
|
/* Ignore parameters */
|
|
( void ) parameters;
|
|
|
|
for( ; ; )
|
|
{
|
|
had_reception = pdFALSE;
|
|
|
|
ulTaskNotifyTake( pdTRUE, max_block_time );
|
|
|
|
if( eMACInterruptTx == ( xMacInterruptStatus & eMACInterruptTx ) )
|
|
{
|
|
xMacInterruptStatus &= ~( eMACInterruptTx );
|
|
|
|
_process_transmit_complete();
|
|
}
|
|
|
|
if( eMACInterruptRx == ( xMacInterruptStatus & eMACInterruptRx ) )
|
|
{
|
|
xMacInterruptStatus &= ~( eMACInterruptRx );
|
|
|
|
had_reception = _process_received_packet();
|
|
}
|
|
|
|
if( pdTRUE == xPhyCheckLinkStatus( &xPhyObject, had_reception ) )
|
|
{
|
|
/* The link has gone done */
|
|
if( 0 == xPhyObject.ulLinkStatusMask )
|
|
{
|
|
link_down_event.eEventType = eNetworkDownEvent;
|
|
link_down_event.pvData = NULL;
|
|
|
|
xSendEventStructToIPTask( &link_down_event, 0 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void vMACBProbePhy( void )
|
|
{
|
|
vPhyInitialise( &xPhyObject, xTM4C_PhyRead, xTM4C_PhyWrite );
|
|
xPhyDiscover( &xPhyObject );
|
|
xPhyConfigure( &xPhyObject, &xPHYProperties );
|
|
}
|
|
|
|
static BaseType_t xTM4C_PhyRead( BaseType_t xAddress,
|
|
BaseType_t xRegister,
|
|
uint32_t * pulValue )
|
|
{
|
|
*pulValue = MAP_EMACPHYRead( EMAC0_BASE, ( uint8_t ) xAddress, ( uint8_t ) xRegister );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static BaseType_t xTM4C_PhyWrite( BaseType_t xAddress,
|
|
BaseType_t xRegister,
|
|
uint32_t ulValue )
|
|
{
|
|
MAP_EMACPHYWrite( EMAC0_BASE, ( uint8_t ) xAddress, ( uint8_t ) xRegister, ulValue );
|
|
|
|
return 0;
|
|
}
|