mirror of
https://github.com/FreeRTOS/FreeRTOS.git
synced 2025-06-19 14:54:28 +08:00
FreeRTOS+TCP Adding the combined driver for SAM4E and SAME70 v2 (#78)
* Adding a combined +TCP driver for SAM4E and SAME70 * Changes after review from Aniruddha Co-authored-by: Hein Tibosch <hein@htibosch.net> Co-authored-by: Aniruddha Kanhere <60444055+AniruddhaKanhere@users.noreply.github.com>
This commit is contained in:
@ -0,0 +1,928 @@
|
||||
/*
|
||||
FreeRTOS+TCP V2.2.1
|
||||
Copyright (C) 2017 Amazon.com, Inc. or its affiliates. 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.
|
||||
|
||||
http://aws.amazon.com/freertos
|
||||
http://www.FreeRTOS.org
|
||||
*/
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* FreeRTOS includes. */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "queue.h"
|
||||
#include "semphr.h"
|
||||
|
||||
/* FreeRTOS+TCP includes. */
|
||||
#include "FreeRTOS_IP.h"
|
||||
#include "FreeRTOS_Sockets.h"
|
||||
#include "FreeRTOS_IP_Private.h"
|
||||
#include "FreeRTOS_ARP.h"
|
||||
#include "NetworkBufferManagement.h"
|
||||
#include "NetworkInterface.h"
|
||||
|
||||
/* Some files from the Atmel Software Framework */
|
||||
/* gmac_SAM.[ch] is a combination of the gmac.[ch] for both SAM4E and SAME70. */
|
||||
#include "gmac_SAM.h"
|
||||
#include <sysclk.h>
|
||||
#include "phyhandling.h"
|
||||
|
||||
/* This file is included to see if 'CONF_BOARD_ENABLE_CACHE' is defined. */
|
||||
#include "conf_board.h"
|
||||
|
||||
|
||||
/* Interrupt events to process. Currently only the Rx event is processed
|
||||
although code for other events is included to allow for possible future
|
||||
expansion. */
|
||||
#define EMAC_IF_RX_EVENT 1UL
|
||||
#define EMAC_IF_TX_EVENT 2UL
|
||||
#define EMAC_IF_ERR_EVENT 4UL
|
||||
#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )
|
||||
|
||||
/* 1536 bytes is more than needed, 1524 would be enough.
|
||||
But 1536 is a multiple of 32, which gives a great alignment for
|
||||
cached memories. */
|
||||
|
||||
#define NETWORK_BUFFER_SIZE 1536
|
||||
|
||||
#ifndef EMAC_MAX_BLOCK_TIME_MS
|
||||
/* The task 'prvEMACHandlerTask()' will wake-up every 100 ms, to see
|
||||
if something has to be done, mostly checking if the PHY has a
|
||||
change in Link Status. */
|
||||
#define EMAC_MAX_BLOCK_TIME_MS 100ul
|
||||
#endif
|
||||
|
||||
#if( ipconfigZERO_COPY_RX_DRIVER == 0 )
|
||||
#error This driver works optimal if ipconfigZERO_COPY_RX_DRIVER is defined as 1
|
||||
#endif
|
||||
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
|
||||
#error This driver works optimal if ipconfigZERO_COPY_TX_DRIVER is defined as 1
|
||||
#endif
|
||||
|
||||
/* Default the size of the stack used by the EMAC deferred handler task to 4x
|
||||
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 ( 4 * configMINIMAL_STACK_SIZE )
|
||||
#endif
|
||||
|
||||
#ifndef niEMAC_HANDLER_TASK_PRIORITY
|
||||
#define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1
|
||||
#endif
|
||||
|
||||
#if( __DCACHE_PRESENT != 0 ) && defined( CONF_BOARD_ENABLE_CACHE )
|
||||
#include "core_cm7.h"
|
||||
#warning This driver assumes the presence of DCACHE
|
||||
#define NETWORK_BUFFERS_CACHED 1
|
||||
#define CACHE_LINE_SIZE 32
|
||||
#define NETWORK_BUFFER_HEADER_SIZE ( ipconfigPACKET_FILLER_SIZE + 8 )
|
||||
|
||||
static void cache_clean_invalidate()
|
||||
{
|
||||
/* If you application crashes here, make sure that SCB_EnableDCache() has been called. */
|
||||
SCB_CleanInvalidateDCache();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void cache_clean_invalidate_by_addr(uint32_t addr, uint32_t size)
|
||||
{
|
||||
/* SAME70 does not have clean/invalidate per area. */
|
||||
/* SCB_CleanInvalidateDCache_by_Addr( ( uint32_t * )addr, size); */
|
||||
SCB_CleanInvalidateDCache();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void cache_invalidate_by_addr(addr, size) \
|
||||
{
|
||||
/* SAME70 does not have clean/invalidate per area. */
|
||||
/* SCB_InvalidateDCache_by_Addr( ( uint32_t * )addr, size); */
|
||||
SCB_InvalidateDCache();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#else
|
||||
#warning Sure there is no caching?
|
||||
#define cache_clean_invalidate() do {} while( 0 )
|
||||
#define cache_clean_invalidate_by_addr(addr, size) do {} while( 0 )
|
||||
#define cache_invalidate_by_addr(addr, size) do {} while( 0 )
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Update settings in GMAC for speed and duplex.
|
||||
*/
|
||||
static void prvEthernetUpdateConfig( BaseType_t xForce );
|
||||
|
||||
/*
|
||||
* Access functions to the PHY's: read() and write() to be used by
|
||||
* phyHandling.c.
|
||||
*/
|
||||
static BaseType_t xPHY_Read( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue );
|
||||
static BaseType_t xPHY_Write( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue );
|
||||
|
||||
#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 )
|
||||
void vGMACGenerateChecksum( uint8_t *apBuffer, size_t uxLength );
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Called from the ASF GMAC driver.
|
||||
*/
|
||||
void xRxCallback( uint32_t ulStatus );
|
||||
void xTxCallback( uint32_t ulStatus, uint8_t *puc_buffer );
|
||||
|
||||
/*
|
||||
* A deferred interrupt handler task that processes GMAC interrupts.
|
||||
*/
|
||||
static void prvEMACHandlerTask( void *pvParameters );
|
||||
|
||||
/*
|
||||
* Initialise the ASF GMAC driver.
|
||||
*/
|
||||
static BaseType_t prvGMACInit( void );
|
||||
|
||||
/*
|
||||
* Try to obtain an Rx packet from the hardware.
|
||||
*/
|
||||
static uint32_t prvEMACRxPoll( void );
|
||||
|
||||
/*
|
||||
* Handle transmission errors.
|
||||
*/
|
||||
static void hand_tx_errors( void );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Bit map of outstanding ETH interrupt events for processing. Currently only
|
||||
the Rx interrupt is handled, although code is included for other events to
|
||||
enable future expansion. */
|
||||
static volatile uint32_t ulISREvents;
|
||||
|
||||
/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
|
||||
static volatile BaseType_t xGMACSwitchRequired;
|
||||
|
||||
/* LLMNR multicast address. */
|
||||
static const uint8_t llmnr_mac_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC };
|
||||
|
||||
/* The GMAC object as defined by the ASF drivers. */
|
||||
static gmac_device_t gs_gmac_dev;
|
||||
|
||||
/* MAC address to use. */
|
||||
extern const uint8_t ucMACAddress[ 6 ];
|
||||
|
||||
/* Holds the handle of the task used as a deferred interrupt processor. The
|
||||
handle is used so direct notifications can be sent to the task for all EMAC/DMA
|
||||
related interrupts. */
|
||||
TaskHandle_t xEMACTaskHandle = NULL;
|
||||
|
||||
static QueueHandle_t xTxBufferQueue;
|
||||
int tx_release_count[ 4 ];
|
||||
|
||||
/* xTXDescriptorSemaphore is a counting semaphore with
|
||||
a maximum count of GMAC_TX_BUFFERS, which is the number of
|
||||
DMA TX descriptors. */
|
||||
static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
|
||||
|
||||
/* For local use only: describe the PHY's properties: */
|
||||
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 ) && ( ipconfigETHERNET_AUTO_CROSS_ENABLE != 0 )
|
||||
.ucMDI_X = PHY_MDIX_AUTO,
|
||||
#elif( ipconfigETHERNET_CROSSED_LINK != 0 )
|
||||
.ucMDI_X = PHY_MDIX_CROSSED,
|
||||
#else
|
||||
.ucMDI_X = PHY_MDIX_DIRECT,
|
||||
#endif
|
||||
};
|
||||
|
||||
/* All PHY handling code has now been separated from the NetworkInterface.c,
|
||||
see "../Common/phyHandling.c". */
|
||||
static EthernetPhy_t xPhyObject;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* GMAC interrupt handler.
|
||||
*/
|
||||
void GMAC_Handler(void)
|
||||
{
|
||||
xGMACSwitchRequired = pdFALSE;
|
||||
|
||||
/* gmac_handler() may call xRxCallback() which may change
|
||||
the value of xGMACSwitchRequired. */
|
||||
gmac_handler( &gs_gmac_dev );
|
||||
|
||||
if( xGMACSwitchRequired != pdFALSE )
|
||||
{
|
||||
portEND_SWITCHING_ISR( xGMACSwitchRequired );
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void xRxCallback( uint32_t ulStatus )
|
||||
{
|
||||
if( ( ( ulStatus & GMAC_RSR_REC ) != 0 ) && ( xEMACTaskHandle != NULL ) )
|
||||
{
|
||||
/* let the prvEMACHandlerTask know that there was an RX event. */
|
||||
ulISREvents |= EMAC_IF_RX_EVENT;
|
||||
/* Only an RX interrupt can wakeup prvEMACHandlerTask. */
|
||||
vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired );
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void returnTxBuffer(uint8_t *puc_buffer)
|
||||
{
|
||||
/* Called from a non-ISR context. */
|
||||
if( xTxBufferQueue != NULL )
|
||||
{
|
||||
xQueueSend( xTxBufferQueue, &puc_buffer, 0 );
|
||||
xTaskNotifyGive( xEMACTaskHandle );
|
||||
ulISREvents |= EMAC_IF_TX_EVENT;
|
||||
}
|
||||
}
|
||||
|
||||
void xTxCallback( uint32_t ulStatus, uint8_t *puc_buffer )
|
||||
{
|
||||
if( ( xTxBufferQueue != NULL ) && ( xEMACTaskHandle != NULL ) )
|
||||
{
|
||||
/* let the prvEMACHandlerTask know that there was an TX event. */
|
||||
ulISREvents |= EMAC_IF_TX_EVENT;
|
||||
/* Wakeup prvEMACHandlerTask. */
|
||||
vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired );
|
||||
xQueueSendFromISR( xTxBufferQueue, &puc_buffer, ( BaseType_t * ) &xGMACSwitchRequired );
|
||||
tx_release_count[ 2 ]++;
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
|
||||
/*
|
||||
The two standard defines 'GMAC_MAN_RW_TYPE' and 'GMAC_MAN_READ_ONLY'
|
||||
are incorrect.
|
||||
Therefore, use the following:
|
||||
*/
|
||||
|
||||
#define GMAC_MAINTENANCE_READ_ACCESS (2)
|
||||
#define GMAC_MAINTENANCE_WRITE_ACCESS (1)
|
||||
|
||||
static BaseType_t xPHY_Read( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue )
|
||||
{
|
||||
BaseType_t xReturn;
|
||||
UBaseType_t uxWasEnabled;
|
||||
|
||||
/* Wait until bus idle */
|
||||
while ((GMAC->GMAC_NSR & GMAC_NSR_IDLE) == 0);
|
||||
/* Write maintain register */
|
||||
/*
|
||||
* OP: Operation: 10 is read. 01 is write.
|
||||
*/
|
||||
uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u;
|
||||
if( uxWasEnabled == 0u )
|
||||
{
|
||||
/* Enable further GMAC maintenance. */
|
||||
GMAC->GMAC_NCR |= GMAC_NCR_MPE;
|
||||
}
|
||||
GMAC->GMAC_MAN = GMAC_MAN_WTN(GMAC_MAN_CODE_VALUE)
|
||||
| GMAC_MAN_CLTTO
|
||||
| GMAC_MAN_PHYA(xAddress)
|
||||
| GMAC_MAN_REGA(xRegister)
|
||||
| GMAC_MAN_OP(GMAC_MAINTENANCE_READ_ACCESS)
|
||||
| GMAC_MAN_DATA( (uint16_t)0u );
|
||||
|
||||
if (gmac_wait_phy(GMAC, MAC_PHY_RETRY_MAX) == GMAC_TIMEOUT)
|
||||
{
|
||||
*pulValue = (uint32_t)0xffffu;
|
||||
xReturn = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait until bus idle */
|
||||
while ((GMAC->GMAC_NSR & GMAC_NSR_IDLE) == 0);
|
||||
|
||||
/* Return data */
|
||||
*pulValue = (uint32_t)(GMAC->GMAC_MAN & GMAC_MAN_DATA_Msk);
|
||||
|
||||
xReturn = 0;
|
||||
}
|
||||
if( uxWasEnabled == 0u )
|
||||
{
|
||||
/* Disable further GMAC maintenance. */
|
||||
GMAC->GMAC_NCR &= ~GMAC_NCR_MPE;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static BaseType_t xPHY_Write( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue )
|
||||
{
|
||||
BaseType_t xReturn;
|
||||
UBaseType_t uxWasEnabled;
|
||||
|
||||
/* Wait until bus idle */
|
||||
while ((GMAC->GMAC_NSR & GMAC_NSR_IDLE) == 0);
|
||||
/* Write maintain register */
|
||||
uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u;
|
||||
if( uxWasEnabled == 0u )
|
||||
{
|
||||
/* Enable further GMAC maintenance. */
|
||||
GMAC->GMAC_NCR |= GMAC_NCR_MPE;
|
||||
}
|
||||
GMAC->GMAC_MAN = GMAC_MAN_WTN(GMAC_MAN_CODE_VALUE)
|
||||
| GMAC_MAN_CLTTO
|
||||
| GMAC_MAN_PHYA(xAddress)
|
||||
| GMAC_MAN_REGA(xRegister)
|
||||
| GMAC_MAN_OP(GMAC_MAINTENANCE_WRITE_ACCESS)
|
||||
| GMAC_MAN_DATA( (uint16_t)ulValue );
|
||||
|
||||
if (gmac_wait_phy(GMAC, MAC_PHY_RETRY_MAX) == GMAC_TIMEOUT )
|
||||
{
|
||||
xReturn = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
xReturn = 0;
|
||||
}
|
||||
if( uxWasEnabled == 0u )
|
||||
{
|
||||
/* Disable further GMAC maintenance. */
|
||||
GMAC->GMAC_NCR &= ~GMAC_NCR_MPE;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
BaseType_t xNetworkInterfaceInitialise( void )
|
||||
{
|
||||
const TickType_t x5_Seconds = 5000UL;
|
||||
|
||||
if( xEMACTaskHandle == NULL )
|
||||
{
|
||||
prvGMACInit();
|
||||
|
||||
cache_clean_invalidate();
|
||||
|
||||
/* The handler task is created at the highest possible priority to
|
||||
ensure the interrupt handler can return directly to it. */
|
||||
xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle );
|
||||
configASSERT( xEMACTaskHandle );
|
||||
}
|
||||
|
||||
if( xTxBufferQueue == NULL )
|
||||
{
|
||||
xTxBufferQueue = xQueueCreate( GMAC_TX_BUFFERS, sizeof( void * ) );
|
||||
configASSERT( xTxBufferQueue );
|
||||
}
|
||||
|
||||
if( xTXDescriptorSemaphore == NULL )
|
||||
{
|
||||
xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) GMAC_TX_BUFFERS, ( UBaseType_t ) GMAC_TX_BUFFERS );
|
||||
configASSERT( xTXDescriptorSemaphore );
|
||||
}
|
||||
/* When returning non-zero, the stack will become active and
|
||||
start DHCP (in configured) */
|
||||
return xGetPhyLinkStatus();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
BaseType_t xGetPhyLinkStatus( void )
|
||||
{
|
||||
BaseType_t xReturn;
|
||||
|
||||
if( xPhyObject.ulLinkStatusMask != 0 )
|
||||
{
|
||||
xReturn = pdPASS;
|
||||
}
|
||||
else
|
||||
{
|
||||
xReturn = pdFAIL;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/** The GMAC TX errors to handle */
|
||||
#define GMAC_TX_ERRORS (GMAC_TSR_TFC | GMAC_TSR_HRESP)
|
||||
|
||||
static void hand_tx_errors( void )
|
||||
{
|
||||
/* Handle GMAC underrun or AHB errors. */
|
||||
if (gmac_get_tx_status(GMAC) & GMAC_TX_ERRORS) {
|
||||
|
||||
gmac_enable_transmit(GMAC, false);
|
||||
|
||||
/* Reinit TX descriptors. */
|
||||
// gmac_tx_init(ps_gmac_dev);
|
||||
gmac_reset_tx_mem(&gs_gmac_dev);
|
||||
/* Clear error status. */
|
||||
gmac_clear_tx_status(GMAC, GMAC_TX_ERRORS);
|
||||
|
||||
gmac_enable_transmit(GMAC, true);
|
||||
}
|
||||
}
|
||||
|
||||
volatile IPPacket_t *pxSendPacket;
|
||||
|
||||
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend )
|
||||
{
|
||||
/* Do not wait too long for a free TX DMA buffer. */
|
||||
const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u );
|
||||
uint32_t ulTransmitSize;
|
||||
|
||||
ulTransmitSize = pxDescriptor->xDataLength;
|
||||
|
||||
pxSendPacket = (IPPacket_t *)pxDescriptor->pucEthernetBuffer;
|
||||
|
||||
if( ulTransmitSize > NETWORK_BUFFER_SIZE )
|
||||
{
|
||||
ulTransmitSize = NETWORK_BUFFER_SIZE;
|
||||
}
|
||||
/* A do{}while(0) loop is introduced to allow the use of multiple break
|
||||
statement. */
|
||||
do {
|
||||
if( xPhyObject.ulLinkStatusMask == 0ul )
|
||||
{
|
||||
/* Do not attempt to send packets as long as the Link Status is low. */
|
||||
break;
|
||||
}
|
||||
if( xTXDescriptorSemaphore == NULL )
|
||||
{
|
||||
/* Semaphore has not been created yet? */
|
||||
break;
|
||||
}
|
||||
hand_tx_errors();
|
||||
if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
|
||||
{
|
||||
/* Time-out waiting for a free TX descriptor. */
|
||||
tx_release_count[ 3 ]++;
|
||||
break;
|
||||
}
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
|
||||
{
|
||||
/* Confirm that the pxDescriptor may be kept by the driver. */
|
||||
configASSERT( bReleaseAfterSend != pdFALSE );
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_TX_DRIVER */
|
||||
|
||||
#if( NETWORK_BUFFERS_CACHED != 0 )
|
||||
{
|
||||
uint32_t xlength = CACHE_LINE_SIZE * ( ( ulTransmitSize + NETWORK_BUFFER_HEADER_SIZE + CACHE_LINE_SIZE - 1 ) / CACHE_LINE_SIZE );
|
||||
uint32_t xAddress = ( uint32_t )( pxDescriptor->pucEthernetBuffer - NETWORK_BUFFER_HEADER_SIZE );
|
||||
cache_clean_invalidate_by_addr( xAddress, xlength );
|
||||
}
|
||||
#endif
|
||||
|
||||
gmac_dev_write( &gs_gmac_dev, (void *)pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
|
||||
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
|
||||
{
|
||||
/* Confirm that the pxDescriptor may be kept by the driver. */
|
||||
bReleaseAfterSend = pdFALSE;
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_TX_DRIVER */
|
||||
/* Not interested in a call-back after TX. */
|
||||
iptraceNETWORK_INTERFACE_TRANSMIT();
|
||||
} while( ipFALSE_BOOL );
|
||||
|
||||
if( bReleaseAfterSend != pdFALSE )
|
||||
{
|
||||
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
|
||||
}
|
||||
return pdTRUE;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static BaseType_t prvGMACInit( void )
|
||||
{
|
||||
uint32_t ncfgr;
|
||||
|
||||
gmac_options_t gmac_option;
|
||||
|
||||
gmac_enable_management(GMAC, true);
|
||||
/* Enable further GMAC maintenance. */
|
||||
GMAC->GMAC_NCR |= GMAC_NCR_MPE;
|
||||
|
||||
memset( &gmac_option, '\0', sizeof( gmac_option ) );
|
||||
gmac_option.uc_copy_all_frame = 0;
|
||||
gmac_option.uc_no_boardcast = 0;
|
||||
memcpy( gmac_option.uc_mac_addr, ucMACAddress, sizeof( gmac_option.uc_mac_addr ) );
|
||||
|
||||
gs_gmac_dev.p_hw = GMAC;
|
||||
gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option );
|
||||
|
||||
NVIC_SetPriority( GMAC_IRQn, configMAC_INTERRUPT_PRIORITY );
|
||||
NVIC_EnableIRQ( GMAC_IRQn );
|
||||
|
||||
{
|
||||
/* Set MDC clock divider. */
|
||||
gmac_set_mdc_clock( GMAC, sysclk_get_cpu_hz() );
|
||||
|
||||
vPhyInitialise( &xPhyObject, xPHY_Read, xPHY_Write );
|
||||
xPhyDiscover( &xPhyObject );
|
||||
xPhyConfigure( &xPhyObject, &xPHYProperties );
|
||||
|
||||
/* For a reset / reconfigure of the EMAC. */
|
||||
prvEthernetUpdateConfig( pdTRUE );
|
||||
|
||||
/* Select Media Independent Interface type */
|
||||
#if( SAME70 != 0 )
|
||||
{
|
||||
/* Selecting RMII mode. */
|
||||
GMAC->GMAC_UR &= ~GMAC_UR_RMII;
|
||||
}
|
||||
#else
|
||||
{
|
||||
gmac_select_mii_mode(GMAC, ETH_PHY_MODE);
|
||||
}
|
||||
#endif
|
||||
|
||||
gmac_enable_transmit(GMAC, true);
|
||||
gmac_enable_receive(GMAC, true);
|
||||
|
||||
}
|
||||
|
||||
gmac_enable_management(GMAC, true);
|
||||
|
||||
gmac_set_address( GMAC, 1, (uint8_t*)llmnr_mac_address );
|
||||
|
||||
gmac_enable_management(GMAC, false);
|
||||
/* Disable further GMAC maintenance. */
|
||||
GMAC->GMAC_NCR &= ~GMAC_NCR_MPE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvEthernetUpdateConfig( BaseType_t xForce )
|
||||
{
|
||||
FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS mask %02lX Force %d\n",
|
||||
xPhyObject.ulLinkStatusMask,
|
||||
( int )xForce ) );
|
||||
|
||||
if( ( xForce != pdFALSE ) || ( xPhyObject.ulLinkStatusMask != 0 ) )
|
||||
{
|
||||
#if( ipconfigETHERNET_AN_ENABLE != 0 )
|
||||
{
|
||||
UBaseType_t uxWasEnabled;
|
||||
|
||||
/* Restart the auto-negotiation. */
|
||||
uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u;
|
||||
if( uxWasEnabled == 0u )
|
||||
{
|
||||
/* Enable further GMAC maintenance. */
|
||||
GMAC->GMAC_NCR |= GMAC_NCR_MPE;
|
||||
}
|
||||
xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) );
|
||||
|
||||
/* Configure the MAC with the Duplex Mode fixed by the
|
||||
auto-negotiation process. */
|
||||
if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL )
|
||||
{
|
||||
gmac_enable_full_duplex(GMAC, pdTRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
gmac_enable_full_duplex(GMAC, pdFALSE);
|
||||
}
|
||||
|
||||
/* Configure the MAC with the speed fixed by the
|
||||
auto-negotiation process. */
|
||||
if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 )
|
||||
{
|
||||
gmac_set_speed(GMAC, pdFALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
gmac_set_speed(GMAC, pdTRUE);
|
||||
}
|
||||
if( uxWasEnabled == 0u )
|
||||
{
|
||||
/* Enable further GMAC maintenance. */
|
||||
GMAC->GMAC_NCR &= ~GMAC_NCR_MPE;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
if( xPHYProperties.ucDuplex == PHY_DUPLEX_FULL )
|
||||
{
|
||||
xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_FULL;
|
||||
gmac_enable_full_duplex(GMAC, pdTRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_HALF;
|
||||
gmac_enable_full_duplex(GMAC, pdFALSE);
|
||||
}
|
||||
|
||||
if( xPHYProperties.ucSpeed == PHY_SPEED_100 )
|
||||
{
|
||||
xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_100;
|
||||
gmac_set_speed(GMAC, pdTRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_10;
|
||||
gmac_set_speed(GMAC, pdFALSE);
|
||||
}
|
||||
|
||||
xPhyObject.xPhyPreferences.ucMDI_X = PHY_MDIX_AUTO;
|
||||
|
||||
/* Use predefined (fixed) configuration. */
|
||||
xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vGMACGenerateChecksum( uint8_t *pucBuffer, size_t uxLength )
|
||||
{
|
||||
ProtocolPacket_t *xProtPacket = ( ProtocolPacket_t * ) pucBuffer;
|
||||
|
||||
if ( xProtPacket->xTCPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE )
|
||||
{
|
||||
IPHeader_t *pxIPHeader = &( xProtPacket->xTCPPacket.xIPHeader );
|
||||
|
||||
/* Calculate the IP header checksum. */
|
||||
pxIPHeader->usHeaderChecksum = 0x00;
|
||||
pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );
|
||||
pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );
|
||||
|
||||
/* Calculate the TCP checksum for an outgoing packet. */
|
||||
usGenerateProtocolChecksum( pucBuffer, uxLength, pdTRUE );
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static uint32_t prvEMACRxPoll( void )
|
||||
{
|
||||
unsigned char *pucUseBuffer;
|
||||
uint32_t ulReceiveCount, ulResult, ulReturnValue = 0;
|
||||
static NetworkBufferDescriptor_t *pxNextNetworkBufferDescriptor = NULL;
|
||||
const UBaseType_t xMinDescriptorsToLeave = 2UL;
|
||||
const TickType_t xBlockTime = pdMS_TO_TICKS( 100UL );
|
||||
static IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
|
||||
uint8_t *pucDMABuffer = NULL;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
/* If pxNextNetworkBufferDescriptor was not left pointing at a valid
|
||||
descriptor then allocate one now. */
|
||||
if( ( pxNextNetworkBufferDescriptor == NULL ) && ( uxGetNumberOfFreeNetworkBuffers() > xMinDescriptorsToLeave ) )
|
||||
{
|
||||
pxNextNetworkBufferDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xBlockTime );
|
||||
}
|
||||
|
||||
if( pxNextNetworkBufferDescriptor != NULL )
|
||||
{
|
||||
/* Point pucUseBuffer to the buffer pointed to by the descriptor. */
|
||||
pucUseBuffer = ( unsigned char* ) ( pxNextNetworkBufferDescriptor->pucEthernetBuffer - ipconfigPACKET_FILLER_SIZE );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* As long as pxNextNetworkBufferDescriptor is NULL, the incoming
|
||||
messages will be flushed and ignored. */
|
||||
pucUseBuffer = NULL;
|
||||
}
|
||||
|
||||
/* Read the next packet from the hardware into pucUseBuffer. */
|
||||
ulResult = gmac_dev_read( &gs_gmac_dev, pucUseBuffer, ipTOTAL_ETHERNET_FRAME_SIZE, &ulReceiveCount, &pucDMABuffer );
|
||||
|
||||
if( ( ulResult != GMAC_OK ) || ( ulReceiveCount == 0 ) )
|
||||
{
|
||||
/* No data from the hardware. */
|
||||
break;
|
||||
}
|
||||
|
||||
if( pxNextNetworkBufferDescriptor == NULL )
|
||||
{
|
||||
/* Data was read from the hardware, but no descriptor was available
|
||||
for it, so it will be dropped. */
|
||||
iptraceETHERNET_RX_EVENT_LOST();
|
||||
continue;
|
||||
}
|
||||
|
||||
iptraceNETWORK_INTERFACE_RECEIVE();
|
||||
#if( ipconfigZERO_COPY_RX_DRIVER != 0 )
|
||||
{
|
||||
pxNextNetworkBufferDescriptor = pxPacketBuffer_to_NetworkBuffer( pucDMABuffer );
|
||||
if( pxNextNetworkBufferDescriptor == NULL )
|
||||
{
|
||||
/* STrange: can not translate from a DMA buffer to a Network Buffer. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_RX_DRIVER */
|
||||
|
||||
pxNextNetworkBufferDescriptor->xDataLength = ( size_t ) ulReceiveCount;
|
||||
xRxEvent.pvData = ( void * ) pxNextNetworkBufferDescriptor;
|
||||
|
||||
/* Send the descriptor to the IP task for processing. */
|
||||
if( xSendEventStructToIPTask( &xRxEvent, xBlockTime ) != pdTRUE )
|
||||
{
|
||||
/* The buffer could not be sent to the stack so must be released
|
||||
again. */
|
||||
vReleaseNetworkBufferAndDescriptor( pxNextNetworkBufferDescriptor );
|
||||
iptraceETHERNET_RX_EVENT_LOST();
|
||||
FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) );
|
||||
}
|
||||
|
||||
/* Now the buffer has either been passed to the IP-task,
|
||||
or it has been released in the code above. */
|
||||
pxNextNetworkBufferDescriptor = NULL;
|
||||
ulReturnValue++;
|
||||
}
|
||||
|
||||
return ulReturnValue;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
volatile UBaseType_t uxLastMinBufferCount = 0;
|
||||
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
|
||||
volatile UBaseType_t uxLastMinQueueSpace;
|
||||
#endif
|
||||
volatile UBaseType_t uxCurrentSemCount;
|
||||
volatile UBaseType_t uxLowestSemCount;
|
||||
|
||||
void vCheckBuffersAndQueue( void )
|
||||
{
|
||||
static UBaseType_t uxCurrentCount;
|
||||
|
||||
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
|
||||
{
|
||||
uxCurrentCount = uxGetMinimumIPQueueSpace();
|
||||
if( uxLastMinQueueSpace != uxCurrentCount )
|
||||
{
|
||||
/* The logging produced below may be helpful
|
||||
while tuning +TCP: see how many buffers are in use. */
|
||||
uxLastMinQueueSpace = uxCurrentCount;
|
||||
FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
|
||||
}
|
||||
}
|
||||
#endif /* ipconfigCHECK_IP_QUEUE_SPACE */
|
||||
uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
|
||||
if( uxLastMinBufferCount != uxCurrentCount )
|
||||
{
|
||||
/* The logging produced below may be helpful
|
||||
while tuning +TCP: see how many buffers are in use. */
|
||||
uxLastMinBufferCount = uxCurrentCount;
|
||||
FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
|
||||
uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
|
||||
}
|
||||
if( xTXDescriptorSemaphore != NULL )
|
||||
{
|
||||
uxCurrentSemCount = uxSemaphoreGetCount( xTXDescriptorSemaphore );
|
||||
if( uxLowestSemCount > uxCurrentSemCount )
|
||||
{
|
||||
uxLowestSemCount = uxCurrentSemCount;
|
||||
FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
extern uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * NETWORK_BUFFER_SIZE ];
|
||||
void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
|
||||
{
|
||||
uint8_t *ucRAMBuffer = ucNetworkPackets;
|
||||
uint32_t ulIndex;
|
||||
|
||||
for( ulIndex = 0; ulIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ulIndex++ )
|
||||
{
|
||||
pxNetworkBuffers[ ulIndex ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
|
||||
*( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ulIndex ] ) );
|
||||
ucRAMBuffer += NETWORK_BUFFER_SIZE;
|
||||
}
|
||||
cache_clean_invalidate();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvEMACHandlerTask( void *pvParameters )
|
||||
{
|
||||
UBaseType_t uxCount;
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
|
||||
NetworkBufferDescriptor_t *pxBuffer;
|
||||
#endif
|
||||
uint8_t *pucBuffer;
|
||||
BaseType_t xResult = 0;
|
||||
uint32_t xStatus;
|
||||
const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS );
|
||||
|
||||
/* Remove compiler warnings about unused parameters. */
|
||||
( void ) pvParameters;
|
||||
|
||||
configASSERT( xEMACTaskHandle );
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
xResult = 0;
|
||||
vCheckBuffersAndQueue();
|
||||
|
||||
if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 )
|
||||
{
|
||||
/* No events to process now, wait for the next. */
|
||||
ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );
|
||||
}
|
||||
|
||||
if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
|
||||
{
|
||||
ulISREvents &= ~EMAC_IF_RX_EVENT;
|
||||
|
||||
/* Wait for the EMAC interrupt to indicate that another packet has been
|
||||
received. */
|
||||
xResult = prvEMACRxPoll();
|
||||
}
|
||||
|
||||
if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
|
||||
{
|
||||
/* Future extension: code to release TX buffers if zero-copy is used. */
|
||||
ulISREvents &= ~EMAC_IF_TX_EVENT;
|
||||
while( xQueueReceive( xTxBufferQueue, &pucBuffer, 0 ) != pdFALSE )
|
||||
{
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
|
||||
{
|
||||
pxBuffer = pxPacketBuffer_to_NetworkBuffer( pucBuffer );
|
||||
if( pxBuffer != NULL )
|
||||
{
|
||||
vReleaseNetworkBufferAndDescriptor( pxBuffer );
|
||||
tx_release_count[ 0 ]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
tx_release_count[ 1 ]++;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
tx_release_count[ 0 ]++;
|
||||
}
|
||||
#endif
|
||||
uxCount = uxQueueMessagesWaiting( ( QueueHandle_t ) xTXDescriptorSemaphore );
|
||||
if( uxCount < GMAC_TX_BUFFERS )
|
||||
{
|
||||
/* Tell the counting semaphore that one more TX descriptor is available. */
|
||||
xSemaphoreGive( xTXDescriptorSemaphore );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 )
|
||||
{
|
||||
/* Future extension: logging about errors that occurred. */
|
||||
ulISREvents &= ~EMAC_IF_ERR_EVENT;
|
||||
}
|
||||
gmac_enable_management(GMAC, true);
|
||||
if( xPhyCheckLinkStatus( &xPhyObject, xResult ) != 0 )
|
||||
{
|
||||
/* Something has changed to a Link Status, need re-check. */
|
||||
prvEthernetUpdateConfig( pdFALSE );
|
||||
}
|
||||
gmac_enable_management(GMAC, false);
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
@ -0,0 +1,924 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief GMAC (Ethernet MAC) driver for SAM.
|
||||
*
|
||||
* Copyright (c) 2015-2016 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
|
||||
*/
|
||||
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* FreeRTOS includes. */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "semphr.h"
|
||||
|
||||
#include "FreeRTOSIPConfig.h"
|
||||
|
||||
/* FreeRTOS+TCP includes. */
|
||||
#include "FreeRTOS_IP.h"
|
||||
#include "FreeRTOS_Sockets.h"
|
||||
#include "FreeRTOS_IP_Private.h"
|
||||
#include "FreeRTOS_ARP.h"
|
||||
#include "NetworkBufferManagement.h"
|
||||
#include "NetworkInterface.h"
|
||||
|
||||
#include "compiler.h"
|
||||
#include "gmac_SAM.h"
|
||||
|
||||
#if( SAME70 != 0 )
|
||||
/* This file is included to see if 'CONF_BOARD_ENABLE_CACHE' is defined. */
|
||||
#include "conf_board.h"
|
||||
#include "core_cm7.h"
|
||||
#endif
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (int)( sizeof(x) / sizeof(x)[0] )
|
||||
#endif
|
||||
|
||||
#if( GMAC_RX_BUFFERS <= 1 )
|
||||
#error Configuration error
|
||||
#endif
|
||||
|
||||
#if( GMAC_TX_BUFFERS <= 1 )
|
||||
#error Configuration error
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \defgroup gmac_group Ethernet Media Access Controller
|
||||
*
|
||||
* See \ref gmac_quickstart.
|
||||
*
|
||||
* Driver for the GMAC (Ethernet Media Access Controller).
|
||||
* This file contains basic functions for the GMAC, with support for all modes, settings
|
||||
* and clock speeds.
|
||||
*
|
||||
* \section dependencies Dependencies
|
||||
* This driver does not depend on other modules.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define NETWORK_BUFFER_SIZE 1536
|
||||
|
||||
__attribute__ ( ( aligned( 32 ) ) )
|
||||
__attribute__ ( ( section( ".first_data" ) ) )
|
||||
uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * NETWORK_BUFFER_SIZE ];
|
||||
|
||||
/** TX descriptor lists */
|
||||
__attribute__ ((section(".first_data")))
|
||||
COMPILER_ALIGNED(8)
|
||||
static gmac_tx_descriptor_t gs_tx_desc[ GMAC_TX_BUFFERS ];
|
||||
|
||||
#if( SAME70 != 0 )
|
||||
__attribute__ ((section(".first_data")))
|
||||
COMPILER_ALIGNED(8)
|
||||
static gmac_tx_descriptor_t gs_tx_desc_null;
|
||||
#endif
|
||||
|
||||
/** RX descriptors lists */
|
||||
__attribute__ ((section(".first_data")))
|
||||
COMPILER_ALIGNED(8)
|
||||
static gmac_rx_descriptor_t gs_rx_desc[ GMAC_RX_BUFFERS ];
|
||||
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
|
||||
/** Send Buffer. Section 3.6 of AMBA 2.0 spec states that burst should not cross the
|
||||
* 1K Boundaries. Receive buffer manager write operations are burst of 2 words => 3 lsb bits
|
||||
* of the address shall be set to 0.
|
||||
*/
|
||||
__attribute__ ((section(".first_data")))
|
||||
COMPILER_ALIGNED(8)
|
||||
static uint8_t gs_uc_tx_buffer[ GMAC_TX_BUFFERS * GMAC_TX_UNITSIZE ];
|
||||
#endif /* ipconfigZERO_COPY_TX_DRIVER */
|
||||
|
||||
#if( ipconfigZERO_COPY_RX_DRIVER == 0 )
|
||||
/** Receive Buffer */
|
||||
__attribute__ ((section(".first_data")))
|
||||
COMPILER_ALIGNED(8)
|
||||
static uint8_t gs_uc_rx_buffer[ GMAC_RX_BUFFERS * GMAC_RX_UNITSIZE ];
|
||||
#endif /* ipconfigZERO_COPY_RX_DRIVER */
|
||||
|
||||
/** Return count in buffer */
|
||||
#define CIRC_CNT( head, tail, size ) ( ( ( head ) - ( tail ) ) % ( size ) )
|
||||
|
||||
/*
|
||||
* Return space available, from 0 to size-1.
|
||||
* Always leave one free char as a completely full buffer that has (head == tail),
|
||||
* which is the same as empty.
|
||||
*/
|
||||
#define CIRC_SPACE( head, tail, size ) CIRC_CNT( ( tail ), ( ( head ) + 1 ), ( size ) )
|
||||
|
||||
/** Circular buffer is empty ? */
|
||||
#define CIRC_EMPTY( head, tail ) ( ( head ) == ( tail ) )
|
||||
/** Clear circular buffer */
|
||||
#define CIRC_CLEAR( head, tail ) do { ( head ) = 0; ( tail ) = 0; } while( 0 )
|
||||
|
||||
/* Two call-back functions that should be defined in NetworkInterface.c */
|
||||
extern void xRxCallback( uint32_t ulStatus );
|
||||
extern void xTxCallback( uint32_t ulStatus, uint8_t *puc_buffer );
|
||||
extern void returnTxBuffer(uint8_t *puc_buffer);
|
||||
|
||||
|
||||
/** Increment head or tail */
|
||||
static __inline void circ_inc32( int32_t *lHeadOrTail, uint32_t ulSize )
|
||||
{
|
||||
( *lHeadOrTail ) ++;
|
||||
if( ( *lHeadOrTail ) >= ( int32_t )ulSize )
|
||||
{
|
||||
( *lHeadOrTail ) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Wait PHY operation to be completed.
|
||||
*
|
||||
* \param p_gmac HW controller address.
|
||||
* \param ul_retry The retry times, 0 to wait forever until completeness.
|
||||
*
|
||||
* Return GMAC_OK if the operation is completed successfully.
|
||||
*/
|
||||
uint8_t gmac_wait_phy(Gmac* p_gmac, const uint32_t ul_retry)
|
||||
{
|
||||
volatile uint32_t ul_retry_count = 0;
|
||||
const uint32_t xPHYPollDelay = pdMS_TO_TICKS( 1ul );
|
||||
|
||||
while (!gmac_is_phy_idle(p_gmac)) {
|
||||
if (ul_retry == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ul_retry_count++;
|
||||
|
||||
if (ul_retry_count >= ul_retry) {
|
||||
return GMAC_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Block the task to allow other tasks to execute while the PHY
|
||||
is not connected. */
|
||||
vTaskDelay( xPHYPollDelay );
|
||||
}
|
||||
return GMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable transfer, reset registers and descriptor lists.
|
||||
*
|
||||
* \param p_dev Pointer to GMAC driver instance.
|
||||
*
|
||||
*/
|
||||
void gmac_reset_tx_mem(gmac_device_t* p_dev)
|
||||
{
|
||||
Gmac *p_hw = p_dev->p_hw;
|
||||
|
||||
uint32_t ul_index;
|
||||
uint32_t ul_address;
|
||||
|
||||
/* Disable TX */
|
||||
gmac_enable_transmit(p_hw, 0);
|
||||
|
||||
{
|
||||
for( ul_index = 0; ul_index < ARRAY_SIZE(gs_tx_desc); ul_index++ )
|
||||
{
|
||||
uint32_t ulAddr = gs_tx_desc[ul_index].addr;
|
||||
if (ulAddr) {
|
||||
returnTxBuffer ((uint8_t *)ulAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Set up the TX descriptors */
|
||||
CIRC_CLEAR(p_dev->l_tx_head, p_dev->l_tx_tail);
|
||||
for( ul_index = 0; ul_index < GMAC_TX_BUFFERS; ul_index++ )
|
||||
{
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
|
||||
{
|
||||
ul_address = (uint32_t) 0u;
|
||||
}
|
||||
#else
|
||||
{
|
||||
ul_address = (uint32_t) (&(gs_uc_tx_buffer[ul_index * GMAC_TX_UNITSIZE]));
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_TX_DRIVER */
|
||||
gs_tx_desc[ul_index].addr = ul_address;
|
||||
gs_tx_desc[ul_index].status.val = GMAC_TXD_USED;
|
||||
}
|
||||
/* Set the WRAP bit in the last descriptor. */
|
||||
gs_tx_desc[GMAC_TX_BUFFERS - 1].status.val = GMAC_TXD_USED | GMAC_TXD_WRAP;
|
||||
|
||||
/* Set transmit buffer queue */
|
||||
gmac_set_tx_queue(p_hw, (uint32_t) gs_tx_desc);
|
||||
#if( SAME70 != 0 )
|
||||
{
|
||||
gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_1);
|
||||
gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_2);
|
||||
/* Note that SAME70 REV B had 6 priority queues. */
|
||||
gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_3);
|
||||
gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_4);
|
||||
gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_5);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable receiver, reset registers and descriptor list.
|
||||
*
|
||||
* \param p_dev Pointer to GMAC Driver instance.
|
||||
*/
|
||||
static void gmac_reset_rx_mem(gmac_device_t* p_dev)
|
||||
{
|
||||
Gmac *p_hw = p_dev->p_hw;
|
||||
|
||||
uint32_t ul_index;
|
||||
uint32_t ul_address;
|
||||
|
||||
/* Disable RX */
|
||||
gmac_enable_receive(p_hw, 0);
|
||||
|
||||
/* Set up the RX descriptors */
|
||||
p_dev->ul_rx_idx = 0;
|
||||
for( ul_index = 0; ul_index < GMAC_RX_BUFFERS; ul_index++ )
|
||||
{
|
||||
#if( ipconfigZERO_COPY_RX_DRIVER != 0 )
|
||||
{
|
||||
NetworkBufferDescriptor_t *pxNextNetworkBufferDescriptor;
|
||||
|
||||
pxNextNetworkBufferDescriptor = pxGetNetworkBufferWithDescriptor( GMAC_RX_UNITSIZE, 0ul );
|
||||
configASSERT( pxNextNetworkBufferDescriptor != NULL );
|
||||
ul_address = ( uint32_t )( pxNextNetworkBufferDescriptor->pucEthernetBuffer );
|
||||
}
|
||||
#else
|
||||
{
|
||||
ul_address = ( uint32_t ) ( &( gs_uc_rx_buffer[ ul_index * GMAC_RX_UNITSIZE ] ) );
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_RX_DRIVER */
|
||||
gs_rx_desc[ul_index].addr.val = ul_address & GMAC_RXD_ADDR_MASK;
|
||||
gs_rx_desc[ul_index].status.val = 0;
|
||||
}
|
||||
/* Set the WRAP bit in the last descriptor. */
|
||||
gs_rx_desc[GMAC_RX_BUFFERS - 1].addr.bm.b_wrap = 1;
|
||||
|
||||
/* Set receive buffer queue */
|
||||
gmac_set_rx_queue(p_hw, (uint32_t)gs_rx_desc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Initialize the allocated buffer lists for GMAC driver to transfer data.
|
||||
* Must be invoked after gmac_dev_init() but before RX/TX starts.
|
||||
*
|
||||
* \note If input address is not 8-byte aligned, the address is automatically
|
||||
* adjusted and the list size is reduced by one.
|
||||
*
|
||||
* \param p_gmac Pointer to GMAC instance.
|
||||
* \param p_gmac_dev Pointer to GMAC device instance.
|
||||
* \param p_dev_mm Pointer to the GMAC memory management control block.
|
||||
*
|
||||
* \return GMAC_OK or GMAC_PARAM.
|
||||
*/
|
||||
static uint8_t gmac_init_mem(Gmac* p_gmac, gmac_device_t* p_gmac_dev)
|
||||
{
|
||||
/* Assign TX buffers */
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
|
||||
if ((((uint32_t) gs_uc_tx_buffer) & 0x7)
|
||||
|| ((uint32_t) p_dev_mm->p_tx_dscr & 0x7)) {
|
||||
p_dev_mm->ul_tx_size--;
|
||||
}
|
||||
p_gmac_dev->p_tx_buffer =
|
||||
(uint8_t *) (((uint32_t) gs_uc_tx_buffer) & 0xFFFFFFF8);
|
||||
#endif
|
||||
|
||||
/* Reset TX & RX Memory */
|
||||
gmac_reset_rx_mem(p_gmac_dev);
|
||||
gmac_reset_tx_mem(p_gmac_dev);
|
||||
|
||||
/* Enable Rx and Tx, plus the statistics register */
|
||||
gmac_enable_transmit(p_gmac, true);
|
||||
gmac_enable_receive(p_gmac, true);
|
||||
gmac_enable_statistics_write(p_gmac, true);
|
||||
|
||||
/* Set up the interrupts for transmission and errors */
|
||||
gmac_enable_interrupt(p_gmac,
|
||||
GMAC_IER_RLEX | /* Enable retry limit exceeded interrupt. */
|
||||
GMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */
|
||||
GMAC_IER_ROVR | /* Enable receive overrun interrupt. */
|
||||
GMAC_IER_TCOMP | /* Enable transmit complete interrupt. */
|
||||
GMAC_IER_TUR | /* Enable transmit underrun interrupt. */
|
||||
GMAC_IER_TFC | /* Enable transmit buffers exhausted in mid-frame interrupt. */
|
||||
GMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */
|
||||
GMAC_IER_PFNZ | /* Enable pause frame received interrupt. */
|
||||
GMAC_IER_PTZ | /* Enable pause time zero interrupt. */
|
||||
GMAC_IER_RCOMP); /* Enable receive complete interrupt. */
|
||||
|
||||
return GMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initialize the GMAC driver.
|
||||
*
|
||||
* \param p_gmac Pointer to the GMAC instance.
|
||||
* \param p_gmac_dev Pointer to the GMAC device instance.
|
||||
* \param p_opt GMAC configure options.
|
||||
*/
|
||||
void gmac_dev_init(Gmac* p_gmac, gmac_device_t* p_gmac_dev,
|
||||
gmac_options_t* p_opt)
|
||||
{
|
||||
/* Disable TX & RX and more */
|
||||
gmac_network_control(p_gmac, 0);
|
||||
gmac_disable_interrupt(p_gmac, ~0u);
|
||||
|
||||
gmac_clear_statistics(p_gmac);
|
||||
|
||||
/* Clear all status bits in the receive status register. */
|
||||
gmac_clear_rx_status(p_gmac, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA
|
||||
| GMAC_RSR_HNO);
|
||||
|
||||
#ifndef GMAC_TSR_UND
|
||||
/* GMAC_TSR_UND is only defined by SAM4E. */
|
||||
#define GMAC_TSR_UND 0ul
|
||||
#endif
|
||||
/* Clear all status bits in the transmit status register */
|
||||
gmac_clear_tx_status(p_gmac, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE
|
||||
| GMAC_TSR_TFC | GMAC_TSR_TXCOMP | GMAC_TSR_UND);
|
||||
|
||||
/* Clear interrupts */
|
||||
gmac_get_interrupt_status(p_gmac);
|
||||
#if !defined(ETHERNET_CONF_DATA_OFFSET)
|
||||
/* Receive Buffer Offset
|
||||
* Indicates the number of bytes by which the received data
|
||||
* is offset from the start of the receive buffer
|
||||
* which can be handy for alignment reasons */
|
||||
/* Note: FreeRTOS+TCP wants to have this offset set to 2 bytes */
|
||||
#error ETHERNET_CONF_DATA_OFFSET not defined, assuming 0
|
||||
#endif
|
||||
/* Enable the copy of data into the buffers
|
||||
ignore broadcasts, and not copy FCS. */
|
||||
|
||||
gmac_set_config(p_gmac,
|
||||
( gmac_get_config(p_gmac) & ~GMAC_NCFGR_RXBUFO_Msk ) |
|
||||
GMAC_NCFGR_RFCS | /* Remove FCS, frame check sequence (last 4 bytes) */
|
||||
GMAC_NCFGR_PEN | /* Pause Enable */
|
||||
GMAC_NCFGR_RXBUFO( ETHERNET_CONF_DATA_OFFSET ) | /* Set Ethernet Offset */
|
||||
GMAC_RXD_RXCOEN ); /* RXCOEN related function */
|
||||
|
||||
/*
|
||||
* GMAC_DCFGR_TXCOEN: (GMAC_DCFGR) Transmitter Checksum Generation Offload Enable.
|
||||
* Note: SAM4E/SAME70 do have RX checksum offloading
|
||||
* but TX checksum offloading has NOT been implemented,
|
||||
* at least on a SAM4E.
|
||||
* http://community.atmel.com/forum/sam4e-gmac-transmit-checksum-offload-enablesolved
|
||||
*/
|
||||
|
||||
{
|
||||
uint32_t ulValue = gmac_get_dma(p_gmac);
|
||||
|
||||
/* Let the GMAC set TX checksum's. */
|
||||
ulValue |= GMAC_DCFGR_TXCOEN;
|
||||
#if( SAME70 != 0 )
|
||||
{
|
||||
/* Transmitter Packet Buffer Memory Size Select:
|
||||
Use full configured addressable space (4 Kbytes). */
|
||||
ulValue |= GMAC_DCFGR_TXPBMS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Clear the DMA Receive Buffer Size (DRBS) field: */
|
||||
ulValue &= ~( GMAC_DCFGR_DRBS_Msk );
|
||||
/* And set it: */
|
||||
ulValue |= ( GMAC_RX_UNITSIZE / 64 ) << GMAC_DCFGR_DRBS_Pos;
|
||||
|
||||
gmac_set_dma(p_gmac, ulValue);
|
||||
}
|
||||
|
||||
/* Enable/Disable Copy(Receive) All Valid Frames. */
|
||||
gmac_enable_copy_all(p_gmac, p_opt->uc_copy_all_frame);
|
||||
|
||||
/* Disable/Enable broadcast receiving */
|
||||
gmac_disable_broadcast(p_gmac, p_opt->uc_no_boardcast);
|
||||
|
||||
|
||||
/* Initialize memory */
|
||||
gmac_init_mem(p_gmac, p_gmac_dev);
|
||||
|
||||
/* Set Mac Address */
|
||||
gmac_set_address(p_gmac, 0, p_opt->uc_mac_addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Frames can be read from the GMAC in multiple sections.
|
||||
*
|
||||
* Returns > 0 if a complete frame is available
|
||||
* It also it cleans up incomplete older frames
|
||||
*/
|
||||
|
||||
static uint32_t gmac_dev_poll(gmac_device_t* p_gmac_dev)
|
||||
{
|
||||
uint32_t ulReturn = 0;
|
||||
int32_t ulIndex = p_gmac_dev->ul_rx_idx;
|
||||
gmac_rx_descriptor_t *pxHead = &gs_rx_desc[ulIndex];
|
||||
|
||||
// #warning Just for debugging
|
||||
// if((pxHead->addr.val & GMAC_RXD_OWNERSHIP) != 0)
|
||||
// {
|
||||
// NVIC_DisableIRQ( GMAC_IRQn );
|
||||
// }
|
||||
|
||||
#if( ipconfigZERO_COPY_RX_DRIVER == 0 )
|
||||
{
|
||||
/* Discard any incomplete frames */
|
||||
while ((pxHead->addr.val & GMAC_RXD_OWNERSHIP) &&
|
||||
(pxHead->status.val & GMAC_RXD_SOF) == 0)
|
||||
{
|
||||
pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP);
|
||||
circ_inc32 (&ulIndex, GMAC_RX_BUFFERS);
|
||||
pxHead = &gs_rx_desc[ulIndex];
|
||||
p_gmac_dev->ul_rx_idx = ulIndex;
|
||||
#if( GMAC_STATS != 0 )
|
||||
{
|
||||
gmacStats.incompCount++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_RX_DRIVER == 0 */
|
||||
|
||||
while ((pxHead->addr.val & GMAC_RXD_OWNERSHIP) != 0)
|
||||
{
|
||||
#if( ipconfigZERO_COPY_RX_DRIVER == 0 )
|
||||
{
|
||||
if ((pxHead->status.val & GMAC_RXD_EOF) != 0) {
|
||||
/* Here a complete frame has been seen with SOF and EOF */
|
||||
ulReturn = pxHead->status.bm.b_len;
|
||||
break;
|
||||
}
|
||||
circ_inc32 (&ulIndex, GMAC_RX_BUFFERS);
|
||||
pxHead = &gs_rx_desc[ulIndex];
|
||||
if ((pxHead->addr.val & GMAC_RXD_OWNERSHIP) == 0) {
|
||||
/* CPU is not the owner (yet) */
|
||||
break;
|
||||
}
|
||||
if ((pxHead->status.val & GMAC_RXD_SOF) != 0) {
|
||||
/* Strange, we found a new Start Of Frame
|
||||
* discard previous segments */
|
||||
int32_t ulPrev = p_gmac_dev->ul_rx_idx;
|
||||
pxHead = &gs_rx_desc[ulPrev];
|
||||
do {
|
||||
pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP);
|
||||
circ_inc32 (&ulPrev, GMAC_RX_BUFFERS);
|
||||
pxHead = &gs_rx_desc[ulPrev];
|
||||
#if( GMAC_STATS != 0 )
|
||||
{
|
||||
gmacStats.truncCount++;
|
||||
}
|
||||
#endif
|
||||
} while (ulPrev != ulIndex);
|
||||
p_gmac_dev->ul_rx_idx = ulIndex;
|
||||
}
|
||||
}
|
||||
#else /* ipconfigZERO_COPY_RX_DRIVER */
|
||||
{
|
||||
if ((pxHead->status.val & (GMAC_RXD_SOF|GMAC_RXD_EOF)) == (GMAC_RXD_SOF|GMAC_RXD_EOF)) {
|
||||
/* Here a complete frame in a single segment. */
|
||||
ulReturn = pxHead->status.bm.b_len;
|
||||
break;
|
||||
}
|
||||
/* Return the buffer to DMA. */
|
||||
pxHead->addr.bm.b_ownership = 0;
|
||||
|
||||
/* Let ulIndex/pxHead point to the next buffer. */
|
||||
circ_inc32 (&ulIndex, GMAC_RX_BUFFERS);
|
||||
pxHead = &gs_rx_desc[ulIndex];
|
||||
/* And remember this index. */
|
||||
p_gmac_dev->ul_rx_idx = ulIndex;
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_RX_DRIVER */
|
||||
}
|
||||
return ulReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Frames can be read from the GMAC in multiple sections.
|
||||
* Read ul_frame_size bytes from the GMAC receive buffers to pcTo.
|
||||
* p_rcv_size is the size of the entire frame. Generally gmac_read
|
||||
* will be repeatedly called until the sum of all the ul_frame_size equals
|
||||
* the value of p_rcv_size.
|
||||
*
|
||||
* \param p_gmac_dev Pointer to the GMAC device instance.
|
||||
* \param p_frame Address of the frame buffer.
|
||||
* \param ul_frame_size Length of the frame.
|
||||
* \param p_rcv_size Received frame size.
|
||||
*
|
||||
* \return GMAC_OK if receiving frame successfully, otherwise failed.
|
||||
*/
|
||||
uint32_t gmac_dev_read(gmac_device_t* p_gmac_dev, uint8_t* p_frame,
|
||||
uint32_t ul_frame_size, uint32_t* p_rcv_size,
|
||||
uint8_t** pp_recv_frame)
|
||||
{
|
||||
int32_t nextIdx; /* A copy of the Rx-index 'ul_rx_idx' */
|
||||
int32_t bytesLeft = gmac_dev_poll (p_gmac_dev);
|
||||
gmac_rx_descriptor_t *pxHead;
|
||||
|
||||
if (bytesLeft == 0 )
|
||||
{
|
||||
return GMAC_RX_NO_DATA;
|
||||
}
|
||||
|
||||
/* gmac_dev_poll has confirmed that there is a complete frame at
|
||||
* the current position 'ul_rx_idx'
|
||||
*/
|
||||
nextIdx = p_gmac_dev->ul_rx_idx;
|
||||
|
||||
/* Read +2 bytes because buffers are aligned at -2 bytes */
|
||||
bytesLeft = min( bytesLeft + 2, ( int32_t )ul_frame_size );
|
||||
|
||||
#if( __DCACHE_PRESENT != 0 ) && defined( CONF_BOARD_ENABLE_CACHE )
|
||||
SCB_InvalidateDCache();
|
||||
#endif
|
||||
|
||||
#if( ipconfigZERO_COPY_RX_DRIVER == 0 )
|
||||
{
|
||||
|
||||
/* The frame will be copied in 1 or 2 memcpy's */
|
||||
if( ( p_frame != NULL ) && ( bytesLeft != 0 ) )
|
||||
{
|
||||
const uint8_t *source;
|
||||
int32_t left;
|
||||
int32_t toCopy;
|
||||
|
||||
source = gs_uc_rx_buffer + nextIdx * GMAC_RX_UNITSIZE;
|
||||
left = bytesLeft;
|
||||
toCopy = ( GMAC_RX_BUFFERS - nextIdx ) * GMAC_RX_UNITSIZE;
|
||||
if(toCopy > left )
|
||||
{
|
||||
toCopy = left;
|
||||
}
|
||||
memcpy (p_frame, source, toCopy);
|
||||
left -= toCopy;
|
||||
|
||||
if( left != 0ul )
|
||||
{
|
||||
memcpy (p_frame + toCopy, (void*)gs_uc_rx_buffer, left);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else /* ipconfigZERO_COPY_RX_DRIVER */
|
||||
{
|
||||
if( p_frame != NULL )
|
||||
{
|
||||
/* Return a pointer to the earlier DMA buffer. */
|
||||
*( pp_recv_frame ) = ( uint8_t * )
|
||||
( ( ( gs_rx_desc[nextIdx].addr.val ) & ~( 0x03ul ) ) + 2 );
|
||||
/* Set the new DMA-buffer. */
|
||||
gs_rx_desc[nextIdx].addr.bm.addr_dw = ( ( uint32_t )p_frame ) / 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The driver couldn't not allocate a buffer to receive a packet.
|
||||
Leave the current DMA buffer in place. */
|
||||
}
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_RX_DRIVER */
|
||||
|
||||
do
|
||||
{
|
||||
pxHead = &gs_rx_desc[nextIdx];
|
||||
pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP);
|
||||
circ_inc32 (&nextIdx, GMAC_RX_BUFFERS);
|
||||
} while ((pxHead->status.val & GMAC_RXD_EOF) == 0);
|
||||
|
||||
p_gmac_dev->ul_rx_idx = nextIdx;
|
||||
|
||||
*p_rcv_size = bytesLeft;
|
||||
|
||||
// #warning Just for debugging
|
||||
// NVIC_EnableIRQ( GMAC_IRQn );
|
||||
|
||||
return GMAC_OK;
|
||||
}
|
||||
|
||||
extern void vGMACGenerateChecksum( uint8_t *apBuffer, size_t uxLength );
|
||||
|
||||
/**
|
||||
* \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the
|
||||
* GMAC Tx buffers, and then indicates to the GMAC that the buffer is ready.
|
||||
* If lEndOfFrame is true then the data being copied is the end of the frame
|
||||
* and the frame can be transmitted.
|
||||
*
|
||||
* \param p_gmac_dev Pointer to the GMAC device instance.
|
||||
* \param p_buffer Pointer to the data buffer.
|
||||
* \param ul_size Length of the frame.
|
||||
*
|
||||
* \return Length sent.
|
||||
*/
|
||||
uint32_t gmac_dev_write(gmac_device_t* p_gmac_dev, void *p_buffer,
|
||||
uint32_t ul_size)
|
||||
{
|
||||
volatile gmac_tx_descriptor_t *p_tx_td;
|
||||
|
||||
Gmac *p_hw = p_gmac_dev->p_hw;
|
||||
|
||||
|
||||
/* Check parameter */
|
||||
if (ul_size > GMAC_TX_UNITSIZE) {
|
||||
return GMAC_PARAM;
|
||||
}
|
||||
|
||||
/* Pointers to the current transmit descriptor */
|
||||
p_tx_td = &gs_tx_desc[p_gmac_dev->l_tx_head];
|
||||
|
||||
/* If no free TxTd, buffer can't be sent, schedule the wakeup callback */
|
||||
if ((p_tx_td->status.val & GMAC_TXD_USED) == 0)
|
||||
{
|
||||
return GMAC_TX_BUSY;
|
||||
}
|
||||
|
||||
/* Set up/copy data to transmission buffer */
|
||||
if (p_buffer && ul_size) {
|
||||
/* Driver manages the ring buffer */
|
||||
/* Calculating the checksum here is faster than calculating it from the GMAC buffer
|
||||
* because withing p_buffer, it is well aligned */
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
|
||||
{
|
||||
/* Zero-copy... */
|
||||
p_tx_td->addr = ( uint32_t ) p_buffer;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* Or memcopy... */
|
||||
memcpy((void *)p_tx_td->addr, p_buffer, ul_size);
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_TX_DRIVER */
|
||||
vGMACGenerateChecksum( ( uint8_t * ) p_tx_td->addr, ( size_t ) ul_size );
|
||||
}
|
||||
|
||||
//#warning Trying out
|
||||
gmac_start_transmission(p_hw);
|
||||
|
||||
/* Update transmit descriptor status */
|
||||
|
||||
/* The buffer size defined is the length of ethernet frame,
|
||||
so it's always the last buffer of the frame. */
|
||||
if( p_gmac_dev->l_tx_head == ( int32_t )( GMAC_TX_BUFFERS - 1 ) )
|
||||
{
|
||||
/* No need to 'and' with GMAC_TXD_LEN_MASK because ul_size has been checked
|
||||
GMAC_TXD_USED will now be cleared. */
|
||||
p_tx_td->status.val =
|
||||
ul_size | GMAC_TXD_LAST | GMAC_TXD_WRAP;
|
||||
} else {
|
||||
/* GMAC_TXD_USED will now be cleared. */
|
||||
p_tx_td->status.val =
|
||||
ul_size | GMAC_TXD_LAST;
|
||||
}
|
||||
|
||||
circ_inc32( &p_gmac_dev->l_tx_head, GMAC_TX_BUFFERS );
|
||||
|
||||
/* Now start to transmit if it is still not done */
|
||||
gmac_start_transmission(p_hw);
|
||||
|
||||
return GMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get current load of transmit.
|
||||
*
|
||||
* \param p_gmac_dev Pointer to the GMAC device instance.
|
||||
*
|
||||
* \return Current load of transmit.
|
||||
*/
|
||||
uint32_t gmac_dev_get_tx_load(gmac_device_t* p_gmac_dev)
|
||||
{
|
||||
uint16_t us_head = p_gmac_dev->l_tx_head;
|
||||
uint16_t us_tail = p_gmac_dev->l_tx_tail;
|
||||
return CIRC_CNT(us_head, us_tail, GMAC_TX_BUFFERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Register/Clear TX wakeup callback.
|
||||
*
|
||||
* When gmac_dev_write() returns GMAC_TX_BUSY (all transmit descriptor busy), the application
|
||||
* task calls gmac_dev_set_tx_wakeup_callback() to register func_wakeup() callback and
|
||||
* enters suspend state. The callback is in charge to resume the task once
|
||||
* several transmit descriptors have been released. The next time gmac_dev_write() will be called,
|
||||
* it shall be successful.
|
||||
*
|
||||
* This function is usually invoked with NULL callback from the TX wakeup
|
||||
* callback itself, to unregister. Once the callback has resumed the
|
||||
* application task, there is no need to invoke the callback again.
|
||||
*
|
||||
* \param p_gmac_dev Pointer to GMAC device instance.
|
||||
* \param func_wakeup Pointer to wakeup callback function.
|
||||
* \param uc_threshold Number of free transmit descriptor before wakeup callback invoked.
|
||||
*
|
||||
* \return GMAC_OK, GMAC_PARAM on parameter error.
|
||||
*/
|
||||
#if( GMAC_USES_WAKEUP_CALLBACK )
|
||||
uint8_t gmac_dev_set_tx_wakeup_callback(gmac_device_t* p_gmac_dev,
|
||||
gmac_dev_wakeup_cb_t func_wakeup_cb, uint8_t uc_threshold)
|
||||
{
|
||||
if (func_wakeup_cb == NULL) {
|
||||
p_gmac_dev->func_wakeup_cb = NULL;
|
||||
} else {
|
||||
if (uc_threshold <= GMAC_TX_BUFFERS) {
|
||||
p_gmac_dev->func_wakeup_cb = func_wakeup_cb;
|
||||
p_gmac_dev->ul_wakeup_threshold = ( uint32_t )uc_threshold;
|
||||
} else {
|
||||
return GMAC_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
return GMAC_OK;
|
||||
}
|
||||
#endif /* GMAC_USES_WAKEUP_CALLBACK */
|
||||
|
||||
/**
|
||||
* \brief Reset TX & RX queue & statistics.
|
||||
*
|
||||
* \param p_gmac_dev Pointer to GMAC device instance.
|
||||
*/
|
||||
void gmac_dev_reset(gmac_device_t* p_gmac_dev)
|
||||
{
|
||||
Gmac *p_hw = p_gmac_dev->p_hw;
|
||||
|
||||
gmac_reset_rx_mem(p_gmac_dev);
|
||||
gmac_reset_tx_mem(p_gmac_dev);
|
||||
gmac_network_control(p_hw, GMAC_NCR_TXEN | GMAC_NCR_RXEN
|
||||
| GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT);
|
||||
}
|
||||
|
||||
void gmac_dev_halt(Gmac* p_gmac);
|
||||
|
||||
void gmac_dev_halt(Gmac* p_gmac)
|
||||
{
|
||||
gmac_network_control(p_gmac, GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT);
|
||||
gmac_disable_interrupt(p_gmac, ~0u);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief GMAC Interrupt handler.
|
||||
*
|
||||
* \param p_gmac_dev Pointer to GMAC device instance.
|
||||
*/
|
||||
|
||||
#if( GMAC_STATS != 0 )
|
||||
extern int logPrintf( const char *pcFormat, ... );
|
||||
|
||||
void gmac_show_irq_counts ()
|
||||
{
|
||||
int index;
|
||||
for (index = 0; index < ARRAY_SIZE(intPairs); index++) {
|
||||
if (gmacStats.intStatus[intPairs[index].index]) {
|
||||
logPrintf("%s : %6u\n", intPairs[index].name, gmacStats.intStatus[intPairs[index].index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void gmac_handler(gmac_device_t* p_gmac_dev)
|
||||
{
|
||||
Gmac *p_hw = p_gmac_dev->p_hw;
|
||||
|
||||
gmac_tx_descriptor_t *p_tx_td;
|
||||
uint32_t ul_tx_status_flag;
|
||||
#if( GMAC_STATS != 0 )
|
||||
int index;
|
||||
#endif
|
||||
|
||||
uint32_t ul_isr = gmac_get_interrupt_status(p_hw);
|
||||
uint32_t ul_rsr = gmac_get_rx_status(p_hw);
|
||||
uint32_t ul_tsr = gmac_get_tx_status(p_hw);
|
||||
|
||||
#if( GMAC_STATS != 0 )
|
||||
{
|
||||
for (index = 0; index < ARRAY_SIZE(intPairs); index++) {
|
||||
if (ul_isr & intPairs[index].mask)
|
||||
gmacStats.intStatus[intPairs[index].index]++;
|
||||
}
|
||||
}
|
||||
#endif /* GMAC_STATS != 0 */
|
||||
|
||||
/* RX packet */
|
||||
if ((ul_isr & GMAC_ISR_RCOMP) || (ul_rsr & (GMAC_RSR_REC|GMAC_RSR_RXOVR|GMAC_RSR_BNA))) {
|
||||
/* Clear status */
|
||||
gmac_clear_rx_status(p_hw, ul_rsr);
|
||||
|
||||
if (ul_isr & GMAC_ISR_RCOMP)
|
||||
ul_rsr |= GMAC_RSR_REC;
|
||||
/* Invoke callbacks which can be useful to wake op a task */
|
||||
xRxCallback( ul_rsr );
|
||||
}
|
||||
|
||||
/* TX packet */
|
||||
if ((ul_isr & GMAC_ISR_TCOMP) || (ul_tsr & (GMAC_TSR_TXCOMP|GMAC_TSR_COL|GMAC_TSR_RLE|GMAC_TSR_UND))) {
|
||||
|
||||
ul_tx_status_flag = GMAC_TSR_TXCOMP;
|
||||
/* A frame transmitted */
|
||||
|
||||
/* Check RLE */
|
||||
if (ul_tsr & GMAC_TSR_RLE) {
|
||||
/* Status RLE & Number of discarded buffers */
|
||||
ul_tx_status_flag = GMAC_TSR_RLE | CIRC_CNT(p_gmac_dev->l_tx_head,
|
||||
p_gmac_dev->l_tx_tail, GMAC_TX_BUFFERS);
|
||||
gmac_reset_tx_mem(p_gmac_dev);
|
||||
gmac_enable_transmit(p_hw, 1);
|
||||
}
|
||||
/* Clear status */
|
||||
gmac_clear_tx_status(p_hw, ul_tsr);
|
||||
|
||||
if (!CIRC_EMPTY(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail)) {
|
||||
/* Check the buffers */
|
||||
do {
|
||||
p_tx_td = &gs_tx_desc[p_gmac_dev->l_tx_tail];
|
||||
/* Any error? Exit if buffer has not been sent yet */
|
||||
if ((p_tx_td->status.val & GMAC_TXD_USED) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Notify upper layer that a packet has been sent */
|
||||
xTxCallback(ul_tx_status_flag, (void*)p_tx_td->addr); // Function call prvTxCallback
|
||||
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
|
||||
{
|
||||
p_tx_td->addr = 0ul;
|
||||
}
|
||||
#endif /* ipconfigZERO_COPY_TX_DRIVER */
|
||||
|
||||
circ_inc32(&p_gmac_dev->l_tx_tail, GMAC_TX_BUFFERS);
|
||||
} while (CIRC_CNT(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail,
|
||||
GMAC_TX_BUFFERS));
|
||||
}
|
||||
|
||||
if (ul_tsr & GMAC_TSR_RLE) {
|
||||
/* Notify upper layer RLE */
|
||||
xTxCallback(ul_tx_status_flag, NULL);
|
||||
}
|
||||
|
||||
#if( GMAC_USES_WAKEUP_CALLBACK )
|
||||
/* If a wakeup has been scheduled, notify upper layer that it can
|
||||
send other packets, and the sending will be successful. */
|
||||
if ((CIRC_SPACE(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail,
|
||||
GMAC_TX_BUFFERS) >= p_gmac_dev->ul_wakeup_threshold)
|
||||
&& p_gmac_dev->func_wakeup_cb) {
|
||||
p_gmac_dev->func_wakeup_cb();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user