Add missing +TCP code.

This commit is contained in:
Richard Barry 2017-08-17 12:26:43 +00:00
parent 77e95538dc
commit e42a701e99
18 changed files with 11695 additions and 501 deletions

View file

@ -97,7 +97,9 @@ a constant. */
/* Time delay between repeated attempts to initialise the network hardware. */
#define ipINITIALISATION_RETRY_DELAY ( pdMS_TO_TICKS( 3000 ) )
#ifndef ipINITIALISATION_RETRY_DELAY
#define ipINITIALISATION_RETRY_DELAY ( pdMS_TO_TICKS( 3000 ) )
#endif
/* Defines how often the ARP timer callback function is executed. The time is
shorted in the Windows simulator as simulated time is not real time. */
@ -1027,8 +1029,11 @@ BaseType_t xReturn = pdFALSE;
/* Added to prevent ARP flood to gateway. Ensure the
gateway is on the same subnet as the IP address. */
if( xNetworkAddressing.ulGatewayAddress != 0ul )
{
configASSERT( ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) == ( xNetworkAddressing.ulGatewayAddress & xNetworkAddressing.ulNetMask ) );
}
}
#endif /* ipconfigUSE_DHCP == 1 */
/* The MAC address is stored in the start of the default packet

View file

@ -2893,11 +2893,26 @@ void vSocketWakeUpUser( FreeRTOS_Socket_t *pxSocket )
creation, it could still be changed with setsockopt(). */
if( xIsInputStream != pdFALSE )
{
/* Flow control for input streams works with a low- and a high-water mark.
1) If the RX-space becomes less than uxLittleSpace, the flag 'bLowWater' will
be set, and a TCP window update message will be sent to the peer.
2) The data will be read from the socket by recv() and when RX-space becomes
larger than or equal to than 'uxEnoughSpace', a new TCP window update
message will be sent to the peer, and 'bLowWater' will get cleared again.
By default:
uxLittleSpace == 1/5 x uxRxStreamSize
uxEnoughSpace == 4/5 x uxRxStreamSize
How-ever it is very inefficient to make 'uxLittleSpace' smaller than the actual MSS.
*/
uxLength = pxSocket->u.xTCP.uxRxStreamSize;
if( pxSocket->u.xTCP.uxLittleSpace == 0ul )
{
pxSocket->u.xTCP.uxLittleSpace = ( 1ul * pxSocket->u.xTCP.uxRxStreamSize ) / 5u; /*_RB_ Why divide by 5? Can this be changed to a #define? */
if( (pxSocket->u.xTCP.uxLittleSpace < pxSocket->u.xTCP.usCurMSS ) && ( pxSocket->u.xTCP.uxRxStreamSize >= 2 * pxSocket->u.xTCP.usCurMSS ) )
{
pxSocket->u.xTCP.uxLittleSpace = pxSocket->u.xTCP.usCurMSS;
}
}
if( pxSocket->u.xTCP.uxEnoughSpace == 0ul )

View file

@ -744,6 +744,8 @@ NetworkBufferDescriptor_t xTempBuffer;
xTempBuffer.pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket;
xTempBuffer.xDataLength = sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket );
/* A pseudo network buffer can not be released. */
xReleaseAfterSend = pdFALSE;
}
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )

View file

@ -0,0 +1,664 @@
/*
* Handling of Ethernet PHY's
* PHY's communicate with an EMAC either through
* a Media-Independent Interface (MII), or a Reduced Media-Independent Interface (RMII).
* The EMAC can poll for PHY ports on 32 different addresses. Each of the PHY ports
* shall be treated independently.
*
*/
/* 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 "phyHandling.h"
#include "eventLogging.h"
#define phyMIN_PHY_ADDRESS 0
#define phyMAX_PHY_ADDRESS 31
#if defined( PHY_LS_HIGH_CHECK_TIME_MS ) || defined( PHY_LS_LOW_CHECK_TIME_MS )
#warning please use the new defines with 'ipconfig' prefix
#endif
#ifndef ipconfigPHY_LS_HIGH_CHECK_TIME_MS
/* Check if the LinkSStatus in the PHY is still high after 15 seconds of not
receiving packets. */
#define ipconfigPHY_LS_HIGH_CHECK_TIME_MS 15000
#endif
#ifndef ipconfigPHY_LS_LOW_CHECK_TIME_MS
/* Check if the LinkSStatus in the PHY is still low every second. */
#define ipconfigPHY_LS_LOW_CHECK_TIME_MS 1000
#endif
/* Naming and numbering of basic PHY registers. */
#define phyREG_00_BMCR 0x00u /* Basic Mode Control Register. */
#define phyREG_01_BMSR 0x01u /* Basic Mode Status Register. */
#define phyREG_02_PHYSID1 0x02u /* PHYS ID 1 */
#define phyREG_03_PHYSID2 0x03u /* PHYS ID 2 */
#define phyREG_04_ADVERTISE 0x04u /* Advertisement control reg */
/* Naming and numbering of extended PHY registers. */
#define PHYREG_10_PHYSTS 0x10u /* 16 PHY status register Offset */
#define phyREG_19_PHYCR 0x19u /* 25 RW PHY Control Register */
#define phyREG_1F_PHYSPCS 0x1Fu /* 31 RW PHY Special Control Status */
/* Bit fields for 'phyREG_00_BMCR', the 'Basic Mode Control Register'. */
#define phyBMCR_FULL_DUPLEX 0x0100u /* Full duplex. */
#define phyBMCR_AN_RESTART 0x0200u /* Auto negotiation restart. */
#define phyBMCR_AN_ENABLE 0x1000u /* Enable auto negotiation. */
#define phyBMCR_SPEED_100 0x2000u /* Select 100Mbps. */
#define phyBMCR_RESET 0x8000u /* Reset the PHY. */
/* Bit fields for 'phyREG_19_PHYCR', the 'PHY Control Register'. */
#define PHYCR_MDIX_EN 0x8000u /* Enable Auto MDIX. */
#define PHYCR_MDIX_FORCE 0x4000u /* Force MDIX crossed. */
#define phyBMSR_AN_COMPLETE 0x0020u /* Auto-Negotiation process completed */
#define phyBMSR_LINK_STATUS 0x0004u
#define phyPHYSTS_LINK_STATUS 0x0001u /* PHY Link mask */
#define phyPHYSTS_SPEED_STATUS 0x0002u /* PHY Speed mask */
#define phyPHYSTS_DUPLEX_STATUS 0x0004u /* PHY Duplex mask */
/* Bit fields for 'phyREG_1F_PHYSPCS
001 = 10BASE-T half-duplex
101 = 10BASE-T full-duplex
010 = 100BASE-TX half-duplex
110 = 100BASE-TX full-duplex
*/
#define phyPHYSPCS_SPEED_MASK 0x000Cu
#define phyPHYSPCS_SPEED_10 0x0004u
#define phyPHYSPCS_FULL_DUPLEX 0x0010u
/*
* Description of all capabilities that can be advertised to
* the peer (usually a switch or router).
*/
#define phyADVERTISE_CSMA 0x0001u /* Only selector supported. */
#define phyADVERTISE_10HALF 0x0020u /* Try for 10mbps half-duplex. */
#define phyADVERTISE_10FULL 0x0040u /* Try for 10mbps full-duplex. */
#define phyADVERTISE_100HALF 0x0080u /* Try for 100mbps half-duplex. */
#define phyADVERTISE_100FULL 0x0100u /* Try for 100mbps full-duplex. */
#define phyADVERTISE_ALL ( phyADVERTISE_10HALF | phyADVERTISE_10FULL | \
phyADVERTISE_100HALF | phyADVERTISE_100FULL )
/* Send a reset commando to a set of PHY-ports. */
static uint32_t xPhyReset( EthernetPhy_t *pxPhyObject, uint32_t ulPhyMask );
static BaseType_t xHas_1F_PHYSPCS( uint32_t ulPhyID )
{
BaseType_t xResult;
switch( ulPhyID )
{
case PHY_ID_LAN8720:
case PHY_ID_LAN8742A:
case PHY_ID_KSZ8041:
/*
case PHY_ID_KSZ8051: // same ID as 8041
case PHY_ID_KSZ8081: // same ID as 8041
*/
case PHY_ID_KSZ8863:
default:
/* Most PHY's have a 1F_PHYSPCS */
xResult = pdTRUE;
break;
case PHY_ID_DP83848I:
xResult = pdFALSE;
break;
}
return xResult;
}
/*-----------------------------------------------------------*/
static BaseType_t xHas_19_PHYCR( uint32_t ulPhyID )
{
BaseType_t xResult;
switch( ulPhyID )
{
case PHY_ID_LAN8742A:
case PHY_ID_DP83848I:
xResult = pdTRUE;
break;
default:
/* Most PHY's do not have a 19_PHYCR */
xResult = pdFALSE;
break;
}
return xResult;
}
/*-----------------------------------------------------------*/
/* Initialise the struct and assign a PHY-read and -write function. */
void vPhyInitialise( EthernetPhy_t *pxPhyObject, xApplicationPhyReadHook_t fnPhyRead, xApplicationPhyWriteHook_t fnPhyWrite )
{
memset( ( void * )pxPhyObject, '\0', sizeof( *pxPhyObject ) );
pxPhyObject->fnPhyRead = fnPhyRead;
pxPhyObject->fnPhyWrite = fnPhyWrite;
}
/*-----------------------------------------------------------*/
/* Discover all PHY's connected by polling 32 indexes ( zero-based ) */
BaseType_t xPhyDiscover( EthernetPhy_t *pxPhyObject )
{
BaseType_t xPhyAddress;
pxPhyObject->xPortCount = 0;
for( xPhyAddress = phyMIN_PHY_ADDRESS; xPhyAddress <= phyMAX_PHY_ADDRESS; xPhyAddress++ )
{
uint32_t ulLowerID;
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_03_PHYSID2, &ulLowerID );
/* A valid PHY id can not be all zeros or all ones. */
if( ( ulLowerID != ( uint16_t )~0u ) && ( ulLowerID != ( uint16_t )0u ) )
{
uint32_t ulUpperID;
uint32_t ulPhyID;
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_02_PHYSID1, &ulUpperID );
ulPhyID = ( ( ( uint32_t ) ulUpperID ) << 16 ) | ( ulLowerID & 0xFFF0 );
pxPhyObject->ucPhyIndexes[ pxPhyObject->xPortCount ] = xPhyAddress;
pxPhyObject->ulPhyIDs[ pxPhyObject->xPortCount ] = ulPhyID;
pxPhyObject->xPortCount++;
/* See if there is more storage space. */
if( pxPhyObject->xPortCount == ipconfigPHY_MAX_PORTS )
{
break;
}
}
}
if( pxPhyObject->xPortCount > 0 )
{
FreeRTOS_printf( ( "PHY ID %lX\n", pxPhyObject->ulPhyIDs[ 0 ] ) );
eventLogAdd( "PHY ID 0x%lX", pxPhyObject->ulPhyIDs[ 0 ] );
}
return pxPhyObject->xPortCount;
}
/*-----------------------------------------------------------*/
/* Send a reset commando to a set of PHY-ports. */
static uint32_t xPhyReset( EthernetPhy_t *pxPhyObject, uint32_t ulPhyMask )
{
uint32_t ulDoneMask, ulConfig;
TickType_t xRemainingTime;
TimeOut_t xTimer;
BaseType_t xPhyIndex;
/* A bit-mask ofPHY ports that are ready. */
ulDoneMask = 0ul;
/* Set the RESET bits high. */
for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ )
{
BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
/* Read Control register. */
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_00_BMCR, &ulConfig );
pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, ulConfig | phyBMCR_RESET );
}
xRemainingTime = ( TickType_t ) pdMS_TO_TICKS( 1000UL );
vTaskSetTimeOutState( &xTimer );
/* The reset should last less than a second. */
for( ;; )
{
for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ )
{
BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_00_BMCR, &ulConfig );
if( ( ulConfig & phyBMCR_RESET ) == 0 )
{
FreeRTOS_printf( ( "xPhyReset: phyBMCR_RESET %d ready\n", (int)xPhyIndex ) );
ulDoneMask |= ( 1ul << xPhyIndex );
}
}
if( ulDoneMask == ulPhyMask )
{
break;
}
if( xTaskCheckForTimeOut( &xTimer, &xRemainingTime ) != pdFALSE )
{
FreeRTOS_printf( ( "xPhyReset: phyBMCR_RESET timed out ( done 0x%02lX )\n", ulDoneMask ) );
break;
}
}
/* Clear the reset bits. */
for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ )
{
BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_00_BMCR, &ulConfig );
pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, ulConfig & ~phyBMCR_RESET );
}
vTaskDelay( pdMS_TO_TICKS( 50ul ) );
eventLogAdd( "PHY reset %d ports", (int)pxPhyObject->xPortCount );
return ulDoneMask;
}
/*-----------------------------------------------------------*/
BaseType_t xPhyConfigure( EthernetPhy_t *pxPhyObject, const PhyProperties_t *pxPhyProperties )
{
uint32_t ulConfig, ulAdvertise;
BaseType_t xPhyIndex;
if( pxPhyObject->xPortCount < 1 )
{
FreeRTOS_printf( ( "xPhyResetAll: No PHY's detected.\n" ) );
return -1;
}
/* The expected ID for the 'LAN8742A' is 0x0007c130. */
/* The expected ID for the 'LAN8720' is 0x0007c0f0. */
/* The expected ID for the 'DP83848I' is 0x20005C90. */
/* Set advertise register. */
if( ( pxPhyProperties->ucSpeed == ( uint8_t )PHY_SPEED_AUTO ) && ( pxPhyProperties->ucDuplex == ( uint8_t )PHY_DUPLEX_AUTO ) )
{
ulAdvertise = phyADVERTISE_CSMA | phyADVERTISE_ALL;
/* Reset auto-negotiation capability. */
}
else
{
ulAdvertise = phyADVERTISE_CSMA;
if( pxPhyProperties->ucSpeed == ( uint8_t )PHY_SPEED_AUTO )
{
if( pxPhyProperties->ucDuplex == ( uint8_t )PHY_DUPLEX_FULL )
{
ulAdvertise |= phyADVERTISE_10FULL | phyADVERTISE_100FULL;
}
else
{
ulAdvertise |= phyADVERTISE_10HALF | phyADVERTISE_100HALF;
}
}
else if( pxPhyProperties->ucDuplex == ( uint8_t )PHY_DUPLEX_AUTO )
{
if( pxPhyProperties->ucSpeed == ( uint8_t )PHY_SPEED_10 )
{
ulAdvertise |= phyADVERTISE_10FULL | phyADVERTISE_10HALF;
}
else
{
ulAdvertise |= phyADVERTISE_100FULL | phyADVERTISE_100HALF;
}
}
else if( pxPhyProperties->ucSpeed == ( uint8_t )PHY_SPEED_100 )
{
if( pxPhyProperties->ucDuplex == ( uint8_t )PHY_DUPLEX_FULL )
{
ulAdvertise |= phyADVERTISE_100FULL;
}
else
{
ulAdvertise |= phyADVERTISE_100HALF;
}
}
else
{
if( pxPhyProperties->ucDuplex == ( uint8_t )PHY_DUPLEX_FULL )
{
ulAdvertise |= phyADVERTISE_10FULL;
}
else
{
ulAdvertise |= phyADVERTISE_10HALF;
}
}
}
/* Send a reset commando to a set of PHY-ports. */
xPhyReset( pxPhyObject, xPhyGetMask( pxPhyObject ) );
for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ )
{
BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
uint32_t ulPhyID = pxPhyObject->ulPhyIDs[ xPhyIndex ];
/* Write advertise register. */
pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_04_ADVERTISE, ulAdvertise );
/*
AN_EN AN1 AN0 Forced Mode
0 0 0 10BASE-T, Half-Duplex
0 0 1 10BASE-T, Full-Duplex
0 1 0 100BASE-TX, Half-Duplex
0 1 1 100BASE-TX, Full-Duplex
AN_EN AN1 AN0 Advertised Mode
1 0 0 10BASE-T, Half/Full-Duplex
1 0 1 100BASE-TX, Half/Full-Duplex
1 1 0 10BASE-T Half-Duplex
100BASE-TX, Half-Duplex
1 1 1 10BASE-T, Half/Full-Duplex
100BASE-TX, Half/Full-Duplex
*/
/* Read Control register. */
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_00_BMCR, &ulConfig );
ulConfig &= ~( phyBMCR_SPEED_100 | phyBMCR_FULL_DUPLEX );
ulConfig |= phyBMCR_AN_ENABLE;
if( pxPhyProperties->ucSpeed == ( uint8_t )PHY_SPEED_100 )
{
ulConfig |= phyBMCR_SPEED_100;
}
else if( pxPhyProperties->ucSpeed == ( uint8_t )PHY_SPEED_10 )
{
ulConfig &= ~phyBMCR_SPEED_100;
}
if( pxPhyProperties->ucDuplex == ( uint8_t )PHY_DUPLEX_FULL )
{
ulConfig |= phyBMCR_FULL_DUPLEX;
}
else if( pxPhyProperties->ucDuplex == ( uint8_t )PHY_DUPLEX_HALF )
{
ulConfig &= ~phyBMCR_FULL_DUPLEX;
}
if( xHas_19_PHYCR( ulPhyID ) )
{
uint32_t ulPhyControl;
/* Read PHY Control register. */
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_19_PHYCR, &ulPhyControl );
/* Clear bits which might get set: */
ulPhyControl &= ~( PHYCR_MDIX_EN|PHYCR_MDIX_FORCE );
if( pxPhyProperties->ucMDI_X == PHY_MDIX_AUTO )
{
ulPhyControl |= PHYCR_MDIX_EN;
}
else if( pxPhyProperties->ucMDI_X == PHY_MDIX_CROSSED )
{
/* Force direct link = Use crossed RJ45 cable. */
ulPhyControl &= ~PHYCR_MDIX_FORCE;
}
else
{
/* Force crossed link = Use direct RJ45 cable. */
ulPhyControl |= PHYCR_MDIX_FORCE;
}
/* update PHY Control Register. */
pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_19_PHYCR, ulPhyControl );
}
FreeRTOS_printf( ( "+TCP: advertise: %04lX config %04lX\n", ulAdvertise, ulConfig ) );
eventLogAdd( "adv: %04lX config %04lX", ulAdvertise, ulConfig );
}
/* Keep these values for later use. */
pxPhyObject->ulBCRValue = ulConfig;
pxPhyObject->ulACRValue = ulAdvertise;
return 0;
}
/*-----------------------------------------------------------*/
BaseType_t xPhyFixedValue( EthernetPhy_t *pxPhyObject, uint32_t ulPhyMask )
{
BaseType_t xPhyIndex;
uint32_t ulValue, ulBitMask = ( uint32_t )1u;
ulValue = ( uint32_t )0u;
if( pxPhyObject->xPhyPreferences.ucDuplex == PHY_DUPLEX_FULL )
{
ulValue |= phyBMCR_FULL_DUPLEX;
}
if( pxPhyObject->xPhyPreferences.ucSpeed == PHY_SPEED_100 )
{
ulValue |= phyBMCR_SPEED_100;
}
for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 )
{
if( ( ulPhyMask & ulBitMask ) != 0lu )
{
BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
/* Enable Auto-Negotiation. */
pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, ulValue );
}
}
return 0;
}
/*-----------------------------------------------------------*/
BaseType_t xPhyStartAutoNegotiation( EthernetPhy_t *pxPhyObject, uint32_t ulPhyMask )
{
uint32_t xPhyIndex, ulDoneMask, ulBitMask;
uint32_t ulPHYLinkStatus, ulRegValue;
TickType_t xRemainingTime;
TimeOut_t xTimer;
if( ulPhyMask == ( uint32_t )0u )
{
return 0;
}
for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ )
{
if( ( ulPhyMask & ( 1lu << xPhyIndex ) ) != 0lu )
{
BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
/* Enable Auto-Negotiation. */
pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_04_ADVERTISE, pxPhyObject->ulACRValue);
pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, pxPhyObject->ulBCRValue | phyBMCR_AN_RESTART );
}
}
eventLogAdd( "AN start" );
xRemainingTime = ( TickType_t ) pdMS_TO_TICKS( 3000UL );
vTaskSetTimeOutState( &xTimer );
ulDoneMask = 0;
/* Wait until the auto-negotiation will be completed */
for( ;; )
{
ulBitMask = ( uint32_t )1u;
for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 )
{
if( ( ulPhyMask & ulBitMask ) != 0lu )
{
if( ( ulDoneMask & ulBitMask ) == 0lu )
{
BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_01_BMSR, &ulRegValue );
if( ( ulRegValue & phyBMSR_AN_COMPLETE ) != 0 )
{
ulDoneMask |= ulBitMask;
}
}
}
}
if( ulPhyMask == ulDoneMask )
{
break;
}
if( xTaskCheckForTimeOut( &xTimer, &xRemainingTime ) != pdFALSE )
{
FreeRTOS_printf( ( "xPhyReset: phyBMCR_RESET timed out ( done 0x%02lX )\n", ulDoneMask ) );
eventLogAdd( "ANtimed out");
break;
}
}
eventLogAdd( "AN done %02lX / %02lX", ulDoneMask, ulPhyMask );
if( ulDoneMask != ( uint32_t)0u )
{
ulBitMask = ( uint32_t )1u;
pxPhyObject->ulLinkStatusMask &= ~( ulDoneMask );
for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 )
{
BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
uint32_t ulPhyID = pxPhyObject->ulPhyIDs[ xPhyIndex ];
if( ( ulDoneMask & ulBitMask ) == ( uint32_t )0u )
{
continue;
}
/* Clear the 'phyBMCR_AN_RESTART' bit. */
pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, pxPhyObject->ulBCRValue );
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_01_BMSR, &ulRegValue);
if( ( ulRegValue & phyBMSR_LINK_STATUS ) != 0 )
{
ulPHYLinkStatus |= phyBMSR_LINK_STATUS;
pxPhyObject->ulLinkStatusMask |= ulBitMask;
}
else
{
ulPHYLinkStatus &= ~( phyBMSR_LINK_STATUS );
}
if( xHas_1F_PHYSPCS( ulPhyID ) )
{
/* 31 RW PHY Special Control Status */
uint32_t ulControlStatus;
pxPhyObject->fnPhyRead( xPhyAddress, phyREG_1F_PHYSPCS, &ulControlStatus);
ulRegValue = 0;
if( ( ulControlStatus & phyPHYSPCS_FULL_DUPLEX ) != 0 )
{
ulRegValue |= phyPHYSTS_DUPLEX_STATUS;
}
if( ( ulControlStatus & phyPHYSPCS_SPEED_MASK ) == phyPHYSPCS_SPEED_10 )
{
ulRegValue |= phyPHYSTS_SPEED_STATUS;
}
}
else
{
/* Read the result of the auto-negotiation. */
pxPhyObject->fnPhyRead( xPhyAddress, PHYREG_10_PHYSTS, &ulRegValue);
}
FreeRTOS_printf( ( ">> Autonego ready: %08lx: %s duplex %u mbit %s status\n",
ulRegValue,
( ulRegValue & phyPHYSTS_DUPLEX_STATUS ) ? "full" : "half",
( ulRegValue & phyPHYSTS_SPEED_STATUS ) ? 10 : 100,
( ( ulPHYLinkStatus |= phyBMSR_LINK_STATUS ) != 0) ? "high" : "low" ) );
eventLogAdd( "%s duplex %u mbit %s st",
( ulRegValue & phyPHYSTS_DUPLEX_STATUS ) ? "full" : "half",
( ulRegValue & phyPHYSTS_SPEED_STATUS ) ? 10 : 100,
( ( ulPHYLinkStatus |= phyBMSR_LINK_STATUS ) != 0) ? "high" : "low" );
{
uint32_t regs[4];
int i,j;
int address = 0x10;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
pxPhyObject->fnPhyRead( xPhyAddress, address, regs + j );
address++;
}
eventLogAdd("%04lX %04lX %04lX %04lX",
regs[0], regs[1], regs[2], regs[3]);
}
}
if( ( ulRegValue & phyPHYSTS_DUPLEX_STATUS ) != ( uint32_t )0u )
{
pxPhyObject->xPhyProperties.ucDuplex = PHY_DUPLEX_FULL;
}
else
{
pxPhyObject->xPhyProperties.ucDuplex = PHY_DUPLEX_HALF;
}
if( ( ulRegValue & phyPHYSTS_SPEED_STATUS ) != 0 )
{
pxPhyObject->xPhyProperties.ucSpeed = PHY_SPEED_10;
}
else
{
pxPhyObject->xPhyProperties.ucSpeed = PHY_SPEED_100;
}
}
} /* if( ulDoneMask != ( uint32_t)0u ) */
return 0;
}
/*-----------------------------------------------------------*/
BaseType_t xPhyCheckLinkStatus( EthernetPhy_t *pxPhyObject, BaseType_t xHadReception )
{
uint32_t ulStatus, ulBitMask = 1u;
BaseType_t xPhyIndex;
BaseType_t xNeedCheck = pdFALSE;
if( xHadReception > 0 )
{
/* A packet was received. No need to check for the PHY status now,
but set a timer to check it later on. */
vTaskSetTimeOutState( &( pxPhyObject->xLinkStatusTimer ) );
pxPhyObject->xLinkStatusRemaining = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
}
else if( xTaskCheckForTimeOut( &( pxPhyObject->xLinkStatusTimer ), &( pxPhyObject->xLinkStatusRemaining ) ) != pdFALSE )
{
for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 )
{
BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
if( pxPhyObject->fnPhyRead( xPhyAddress, phyREG_01_BMSR, &ulStatus ) == 0 )
{
if( !!( pxPhyObject->ulLinkStatusMask & ulBitMask ) != !!( ulStatus & phyBMSR_LINK_STATUS ) )
{
if( ( ulStatus & phyBMSR_LINK_STATUS ) != 0 )
{
pxPhyObject->ulLinkStatusMask |= ulBitMask;
}
else
{
pxPhyObject->ulLinkStatusMask &= ~( ulBitMask );
}
FreeRTOS_printf( ( "xPhyCheckLinkStatus: PHY LS now %02lX\n", pxPhyObject->ulLinkStatusMask ) );
eventLogAdd( "PHY LS now %02lX", pxPhyObject->ulLinkStatusMask );
xNeedCheck = pdTRUE;
}
}
}
vTaskSetTimeOutState( &( pxPhyObject->xLinkStatusTimer ) );
if( ( pxPhyObject->ulLinkStatusMask & phyBMSR_LINK_STATUS ) != 0 )
{
pxPhyObject->xLinkStatusRemaining = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
}
else
{
pxPhyObject->xLinkStatusRemaining = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS );
}
}
return xNeedCheck;
}
/*-----------------------------------------------------------*/

View file

@ -78,23 +78,13 @@
#include "FreeRTOS_DNS.h"
#include "NetworkBufferManagement.h"
#include "NetworkInterface.h"
#include "phyHandling.h"
/* ST includes. */
#include "stm32f4xx_hal.h"
#ifndef BMSR_LINK_STATUS
#define BMSR_LINK_STATUS 0x0004UL
#endif
#ifndef PHY_LS_HIGH_CHECK_TIME_MS
/* Check if the LinkSStatus in the PHY is still high after 15 seconds of not
receiving packets. */
#define PHY_LS_HIGH_CHECK_TIME_MS 15000
#endif
#ifndef PHY_LS_LOW_CHECK_TIME_MS
/* Check if the LinkSStatus in the PHY is still low every second. */
#define PHY_LS_LOW_CHECK_TIME_MS 1000
#ifdef STM32F7xx
#include "stm32f7xx_hal.h"
#else
#include "stm32f4xx_hal.h"
#endif
/* Interrupt events to process. Currently only the Rx event is processed
@ -110,75 +100,7 @@ expansion. */
ETH_DMA_IT_FBE | ETH_DMA_IT_RWT | ETH_DMA_IT_RPS | ETH_DMA_IT_RBU | ETH_DMA_IT_R | \
ETH_DMA_IT_TU | ETH_DMA_IT_RO | ETH_DMA_IT_TJT | ETH_DMA_IT_TPS | ETH_DMA_IT_T )
/* Naming and numbering of PHY registers. */
#define PHY_REG_00_BMCR 0x00 /* Basic Mode Control Register. */
#define PHY_REG_01_BMSR 0x01 /* Basic Mode Status Register. */
#define PHY_REG_02_PHYSID1 0x02 /* PHYS ID 1 */
#define PHY_REG_03_PHYSID2 0x03 /* PHYS ID 2 */
#define PHY_REG_04_ADVERTISE 0x04 /* Advertisement control reg */
#define PHY_ID_LAN8720 0x0007c0f0
#define PHY_ID_DP83848I 0x20005C90
#ifndef USE_STM324xG_EVAL
#define USE_STM324xG_EVAL 1
#endif
#if( USE_STM324xG_EVAL == 0 )
#define EXPECTED_PHY_ID PHY_ID_LAN8720
#define PHY_REG_1F_PHYSPCS 0x1F /* 31 RW PHY Special Control Status */
/* Use 3 bits in register 31 */
#define PHYSPCS_SPEED_MASK 0x0C
#define PHYSPCS_SPEED_10 0x04
#define PHYSPCS_SPEED_100 0x08
#define PHYSPCS_FULL_DUPLEX 0x10
#else
#define EXPECTED_PHY_ID PHY_ID_DP83848I
#define PHY_REG_10_PHY_SR 0x10 /* PHY status register Offset */
#define PHY_REG_19_PHYCR 0x19 /* 25 RW PHY Control Register */
#endif
/* Some defines used internally here to indicate preferences about speed, MDIX
(wired direct or crossed), and duplex (half or full). */
#define PHY_SPEED_10 1
#define PHY_SPEED_100 2
#define PHY_SPEED_AUTO (PHY_SPEED_10|PHY_SPEED_100)
#define PHY_MDIX_DIRECT 1
#define PHY_MDIX_CROSSED 2
#define PHY_MDIX_AUTO (PHY_MDIX_CROSSED|PHY_MDIX_DIRECT)
#define PHY_DUPLEX_HALF 1
#define PHY_DUPLEX_FULL 2
#define PHY_DUPLEX_AUTO (PHY_DUPLEX_FULL|PHY_DUPLEX_HALF)
#define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020) /*!< Auto-Negotiation process completed */
/*
* Description of all capabilities that can be advertised to
* the peer (usually a switch or router).
*/
#define ADVERTISE_CSMA 0x0001 /* Only selector supported. */
#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex. */
#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex. */
#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex. */
#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex. */
#define ADVERTISE_ALL ( ADVERTISE_10HALF | ADVERTISE_10FULL | \
ADVERTISE_100HALF | ADVERTISE_100FULL)
/*
* Value for the 'PHY_REG_00_BMCR', the PHY's Basic Mode Control Register.
*/
#define BMCR_FULLDPLX 0x0100 /* Full duplex. */
#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart. */
#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation. */
#define BMCR_SPEED100 0x2000 /* Select 100Mbps. */
#define BMCR_RESET 0x8000 /* Reset the PHY. */
#define PHYCR_MDIX_EN 0x8000 /* Enable Auto MDIX. */
#define PHYCR_MDIX_FORCE 0x4000 /* Force MDIX crossed. */
#define ipFRAGMENT_OFFSET_BIT_MASK ( ( uint16_t ) 0x0fff ) /* The bits in the two byte IP header field that make up the fragment offset value. */
@ -223,6 +145,18 @@ FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */
#define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE )
#endif
/* Two choices must be made: RMII versus MII,
and the index of the PHY in use ( between 0 and 31 ). */
#ifndef ipconfigUSE_RMII
#ifdef STM32F7xx
#define ipconfigUSE_RMII 1
#else
#define ipconfigUSE_RMII 0
#endif /* STM32F7xx */
#endif /* ipconfigUSE_RMII */
/*-----------------------------------------------------------*/
/*
@ -263,43 +197,30 @@ static void prvDMATxDescListInit( void );
*/
static void prvDMARxDescListInit( void );
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
/* After packets have been sent, the network
buffers will be released. */
static void vClearTXBuffers( void );
#endif /* ipconfigZERO_COPY_TX_DRIVER */
/* After packets have been sent, the network
buffers will be released. */
static void vClearTXBuffers( void );
/*-----------------------------------------------------------*/
typedef struct _PhyProperties_t
{
uint8_t speed;
uint8_t mdix;
uint8_t duplex;
uint8_t spare;
} PhyProperties_t;
/* 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 uint32_t ulPHYLinkStatus = 0;
#if( ipconfigUSE_LLMNR == 1 )
static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC };
#endif
static EthernetPhy_t xPhyObject;
/* Ethernet handle. */
static ETH_HandleTypeDef xETH;
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
/* xTXDescriptorSemaphore is a counting semaphore with
a maximum count of ETH_TXBUFNB, which is the number of
DMA TX descriptors. */
static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
#endif /* ipconfigZERO_COPY_TX_DRIVER */
/* xTXDescriptorSemaphore is a counting semaphore with
a maximum count of ETH_TXBUFNB, which is the number of
DMA TX descriptors. */
static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
/*
* Note: it is adviced to define both
@ -314,14 +235,29 @@ static ETH_HandleTypeDef xETH;
* TX buffers are allocated in a zero-copy driver.
*/
/* MAC buffers: ---------------------------------------------------------*/
__ALIGN_BEGIN ETH_DMADescTypeDef DMARxDscrTab[ ETH_RXBUFNB ] __ALIGN_END;/* Ethernet Rx MA Descriptor */
/* Put the DMA descriptors in '.first_data'.
This is important for STM32F7, which has an L1 data cache.
The first 64KB of the SRAM is not cached. */
/* Ethernet Rx MA Descriptor */
__attribute__ ((aligned (32)))
__attribute__ ((section(".first_data")))
ETH_DMADescTypeDef DMARxDscrTab[ ETH_RXBUFNB ];
#if( ipconfigZERO_COPY_RX_DRIVER == 0 )
__ALIGN_BEGIN uint8_t Rx_Buff[ ETH_RXBUFNB ][ ETH_RX_BUF_SIZE ] __ALIGN_END; /* Ethernet Receive Buffer */
/* Ethernet Receive Buffer */
__ALIGN_BEGIN uint8_t Rx_Buff[ ETH_RXBUFNB ][ ETH_RX_BUF_SIZE ] __ALIGN_END;
#endif
__ALIGN_BEGIN ETH_DMADescTypeDef DMATxDscrTab[ ETH_TXBUFNB ] __ALIGN_END;/* Ethernet Tx DMA Descriptor */
/* Ethernet Tx DMA Descriptor */
__attribute__ ((aligned (32)))
__attribute__ ((section(".first_data")))
ETH_DMADescTypeDef DMATxDscrTab[ ETH_TXBUFNB ];
#if( ipconfigZERO_COPY_TX_DRIVER == 0 )
__ALIGN_BEGIN uint8_t Tx_Buff[ ETH_TXBUFNB ][ ETH_TX_BUF_SIZE ] __ALIGN_END; /* Ethernet Transmit Buffer */
/* Ethernet Transmit Buffer */
__ALIGN_BEGIN uint8_t Tx_Buff[ ETH_TXBUFNB ][ ETH_TX_BUF_SIZE ] __ALIGN_END;
#endif
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
@ -330,12 +266,6 @@ __ALIGN_BEGIN ETH_DMADescTypeDef DMATxDscrTab[ ETH_TXBUFNB ] __ALIGN_END;/* Eth
static __IO ETH_DMADescTypeDef *DMATxDescToClear;
#endif
/* Value to be written into the 'Basic mode Control Register'. */
static uint32_t ulBCRvalue;
/* Value to be written into the 'Advertisement Control Register'. */
static uint32_t ulACRValue;
/* ucMACAddress as it appears in main.c */
extern const uint8_t ucMACAddress[ 6 ];
@ -348,13 +278,13 @@ static TaskHandle_t xEMACTaskHandle = NULL;
const PhyProperties_t xPHYProperties =
{
#if( ipconfigETHERNET_AN_ENABLE != 0 )
.speed = PHY_SPEED_AUTO,
.duplex = PHY_DUPLEX_AUTO,
.ucSpeed = PHY_SPEED_AUTO,
.ucDuplex = PHY_DUPLEX_AUTO,
#else
#if( ipconfigETHERNET_USE_100MB != 0 )
.speed = PHY_SPEED_100,
.ucSpeed = PHY_SPEED_100,
#else
.speed = PHY_SPEED_10,
.ucSpeed = PHY_SPEED_10,
#endif
#if( ipconfigETHERNET_USE_FULL_DUPLEX != 0 )
@ -365,11 +295,11 @@ const PhyProperties_t xPHYProperties =
#endif
#if( ipconfigETHERNET_AN_ENABLE != 0 ) && ( ipconfigETHERNET_AUTO_CROSS_ENABLE != 0 )
.mdix = PHY_MDIX_AUTO,
.ucMDI_X = PHY_MDIX_AUTO,
#elif( ipconfigETHERNET_CROSSED_LINK != 0 )
.mdix = PHY_MDIX_CROSSED,
.ucMDI_X = PHY_MDIX_CROSSED,
#else
.mdix = PHY_MDIX_DIRECT,
.ucMDI_X = PHY_MDIX_DIRECT,
#endif
};
@ -390,10 +320,9 @@ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
}
/*-----------------------------------------------------------*/
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef *heth )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef *heth )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* This call-back is only useful in case packets are being sent
zero-copy. Once they're sent, the buffers will be released
@ -406,18 +335,17 @@ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
}
/*-----------------------------------------------------------*/
static void vClearTXBuffers()
{
__IO ETH_DMADescTypeDef *txLastDescriptor = xETH.TxDesc;
size_t uxCount = ( ( UBaseType_t ) ETH_TXBUFNB ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
static void vClearTXBuffers()
{
__IO ETH_DMADescTypeDef *txLastDescriptor = xETH.TxDesc;
NetworkBufferDescriptor_t *pxNetworkBuffer;
uint8_t *ucPayLoad;
size_t uxCount = ( ( UBaseType_t ) ETH_TXBUFNB ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
#endif
/* This function is called after a TX-completion interrupt.
It will release each Network Buffer used in xNetworkInterfaceOutput().
@ -429,7 +357,8 @@ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
{
break;
}
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
{
ucPayLoad = ( uint8_t * )DMATxDescToClear->Buffer1Addr;
if( ucPayLoad != NULL )
@ -441,6 +370,8 @@ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
}
DMATxDescToClear->Buffer1Addr = ( uint32_t )0u;
}
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
DMATxDescToClear = ( ETH_DMADescTypeDef * )( DMATxDescToClear->Buffer2NextDescAddr );
@ -448,8 +379,7 @@ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* Tell the counting semaphore that one more TX descriptor is available. */
xSemaphoreGive( xTXDescriptorSemaphore );
}
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
}
/*-----------------------------------------------------------*/
BaseType_t xNetworkInterfaceInitialise( void )
@ -458,16 +388,12 @@ HAL_StatusTypeDef hal_eth_init_status;
BaseType_t xResult;
if( xEMACTaskHandle == NULL )
{
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
{
if( xTXDescriptorSemaphore == NULL )
{
xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ETH_TXBUFNB, ( UBaseType_t ) ETH_TXBUFNB );
configASSERT( xTXDescriptorSemaphore );
}
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
/* Initialise ETH */
@ -475,7 +401,8 @@ BaseType_t xResult;
xETH.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
xETH.Init.Speed = ETH_SPEED_100M;
xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
xETH.Init.PhyAddress = 1;
/* Value of PhyAddress doesn't matter, will be probed for. */
xETH.Init.PhyAddress = 0;
xETH.Init.MACAddr = ( uint8_t *) ucMACAddress;
xETH.Init.RxMode = ETH_RXINTERRUPT_MODE;
@ -485,7 +412,16 @@ BaseType_t xResult;
by the peripheral. */
xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
#if( ipconfigUSE_RMII != 0 )
{
xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
}
#else
{
xETH.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII;
}
#endif /* ipconfigUSE_RMII */
hal_eth_init_status = HAL_ETH_Init( &xETH );
/* Only for inspection by debugger. */
@ -499,12 +435,8 @@ BaseType_t xResult;
memset( &DMATxDscrTab, '\0', sizeof( DMATxDscrTab ) );
memset( &DMARxDscrTab, '\0', sizeof( DMARxDscrTab ) );
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
{
/* Initialize Tx Descriptors list: Chain Mode */
DMATxDescToClear = DMATxDscrTab;
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
/* Initialise TX-descriptors. */
prvDMATxDescListInit();
@ -529,7 +461,7 @@ BaseType_t xResult;
xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle );
} /* if( xEMACTaskHandle == NULL ) */
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
if( xPhyObject.ulLinkStatusMask != 0 )
{
xETH.Instance->DMAIER |= ETH_DMA_ALL_INTS;
xResult = pdPASS;
@ -538,7 +470,7 @@ BaseType_t xResult;
else
{
/* For now pdFAIL will be returned. But prvEMACHandlerTask() is running
and it will keep on checking the PHY and set ulPHYLinkStatus when necessary. */
and it will keep on checking the PHY and set 'ulLinkStatusMask' when necessary. */
xResult = pdFAIL;
FreeRTOS_printf( ( "Link Status still low\n" ) ) ;
}
@ -683,6 +615,12 @@ const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u );
{
ProtocolPacket_t *pxPacket;
#if( ipconfigZERO_COPY_RX_DRIVER != 0 )
{
configASSERT( bReleaseAfterSend != 0 );
}
#endif /* ipconfigZERO_COPY_RX_DRIVER */
/* If the peripheral must calculate the checksum, it wants
the protocol checksum to have a value of zero. */
pxPacket = ( ProtocolPacket_t * ) ( pxDescriptor->pucEthernetBuffer );
@ -697,28 +635,21 @@ const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u );
/* Open a do {} while ( 0 ) loop to be able to call break. */
do
{
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
if( xPhyObject.ulLinkStatusMask != 0 )
{
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
{
if( xTXDescriptorSemaphore == NULL )
{
break;
}
if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
{
/* Time-out waiting for a free TX descriptor. */
break;
}
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
/* This function does the actual transmission of the packet. The packet is
contained in 'pxDescriptor' that is passed to the function. */
pxDmaTxDesc = xETH.TxDesc;
/* Is this buffer available? */
if( ( pxDmaTxDesc->Status & ETH_DMATXDESC_OWN ) == 0 )
configASSERT ( ( pxDmaTxDesc->Status & ETH_DMATXDESC_OWN ) == 0 );
{
/* Is this buffer available? */
/* Get bytes in current buffer. */
@ -733,20 +664,20 @@ const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u );
{
/* Copy the bytes. */
memcpy( ( void * ) pxDmaTxDesc->Buffer1Addr, pxDescriptor->pucEthernetBuffer, ulTransmitSize );
pxDmaTxDesc->Status |= ETH_DMATXDESC_CIC_TCPUDPICMP_FULL;
}
#else
{
/* Move the buffer. */
pxDmaTxDesc->Buffer1Addr = ( uint32_t )pxDescriptor->pucEthernetBuffer;
/* Ask to set the IPv4 checksum.
Also need an Interrupt on Completion so that 'vClearTXBuffers()' will be called.. */
pxDmaTxDesc->Status |= ETH_DMATXDESC_CIC_TCPUDPICMP_FULL | ETH_DMATXDESC_IC;
/* The Network Buffer has been passed to DMA, no need to release it. */
bReleaseAfterSend = pdFALSE_UNSIGNED;
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
/* Ask to set the IPv4 checksum.
Also need an Interrupt on Completion so that 'vClearTXBuffers()' will be called.. */
pxDmaTxDesc->Status |= ETH_DMATXDESC_CIC_TCPUDPICMP_FULL | ETH_DMATXDESC_IC;
/* Prepare transmit descriptors to give to DMA. */
/* Set LAST and FIRST segment */
@ -758,7 +689,8 @@ const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u );
/* Point to next descriptor */
xETH.TxDesc = ( ETH_DMADescTypeDef * ) ( xETH.TxDesc->Buffer2NextDescAddr );
/* Ensure completion of memory access */
__DSB();
/* Resume DMA transmission*/
xETH.Instance->DMATPDR = 0;
iptraceNETWORK_INTERFACE_TRANSMIT();
@ -977,6 +909,8 @@ uint8_t *pucBuffer;
pxDMARxDescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | (uint32_t)ETH_RX_BUF_SIZE;
pxDMARxDescriptor->Status = ETH_DMARXDESC_OWN;
/* Ensure completion of memory access */
__DSB();
/* When Rx Buffer unavailable flag is set clear it and resume
reception. */
if( ( xETH.Instance->DMASR & ETH_DMASR_RBUS ) != 0 )
@ -993,305 +927,141 @@ uint8_t *pucBuffer;
}
/*-----------------------------------------------------------*/
BaseType_t xSTM32_PhyRead( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue )
{
uint16_t usPrevAddress = xETH.Init.PhyAddress;
BaseType_t xResult;
HAL_StatusTypeDef xHALResult;
xETH.Init.PhyAddress = xAddress;
xHALResult = HAL_ETH_ReadPHYRegister( &xETH, ( uint16_t )xRegister, pulValue );
xETH.Init.PhyAddress = usPrevAddress;
if( xHALResult == HAL_OK )
{
xResult = 0;
}
else
{
xResult = -1;
}
return xResult;
}
/*-----------------------------------------------------------*/
BaseType_t xSTM32_PhyWrite( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue )
{
uint16_t usPrevAddress = xETH.Init.PhyAddress;
BaseType_t xResult;
HAL_StatusTypeDef xHALResult;
xETH.Init.PhyAddress = xAddress;
xHALResult = HAL_ETH_WritePHYRegister( &xETH, ( uint16_t )xRegister, ulValue );
xETH.Init.PhyAddress = usPrevAddress;
if( xHALResult == HAL_OK )
{
xResult = 0;
}
else
{
xResult = -1;
}
return xResult;
}
/*-----------------------------------------------------------*/
void phy_test()
{
BaseType_t xPhyCount;
BaseType_t xPhyIndex;
vPhyInitialise( &xPhyObject, xSTM32_PhyRead, xSTM32_PhyWrite );
xPhyCount = xPhyDiscover( &xPhyObject );
FreeRTOS_printf( ( "PHY count %ld\n", xPhyCount ) );
for( xPhyIndex = 0; xPhyIndex < xPhyCount; xPhyIndex++ )
{
FreeRTOS_printf( ( "PHY[%d] at address %d ( 0x%08X )\n",
xPhyIndex,
xPhyObject.ucPhyIndexes[ xPhyIndex ],
xPhyObject.ulPhyIDs[ xPhyIndex ] ) );
}
}
void vMACBProbePhy( void )
{
uint32_t ulConfig, ulAdvertise, ulLower, ulUpper, ulMACPhyID, ulValue;
TimeOut_t xPhyTime;
TickType_t xRemTime = 0;
#if( EXPECTED_PHY_ID == PHY_ID_DP83848I )
uint32_t ulPhyControl;
#endif
HAL_ETH_ReadPHYRegister(&xETH, PHY_REG_03_PHYSID2, &ulLower);
HAL_ETH_ReadPHYRegister(&xETH, PHY_REG_02_PHYSID1, &ulUpper);
ulMACPhyID = ( ( ulUpper << 16 ) & 0xFFFF0000 ) | ( ulLower & 0xFFF0 );
/* The expected ID for the 'LAN8720' is 0x0007c0f0. */
/* The expected ID for the 'DP83848I' is 0x20005C90. */
FreeRTOS_printf( ( "PHY ID %lX (%s)\n", ulMACPhyID,
( ulMACPhyID == EXPECTED_PHY_ID ) ? "OK" : "Unknown" ) );
/* Remove compiler warning if FreeRTOS_printf() is not defined. */
( void ) ulMACPhyID;
/* Set advertise register. */
if( ( xPHYProperties.speed == PHY_SPEED_AUTO ) && ( xPHYProperties.duplex == PHY_DUPLEX_AUTO ) )
{
ulAdvertise = ADVERTISE_CSMA | ADVERTISE_ALL;
/* Reset auto-negotiation capability. */
}
else
{
ulAdvertise = ADVERTISE_CSMA;
if( xPHYProperties.speed == PHY_SPEED_AUTO )
{
if( xPHYProperties.duplex == PHY_DUPLEX_FULL )
{
ulAdvertise |= ADVERTISE_10FULL | ADVERTISE_100FULL;
}
else
{
ulAdvertise |= ADVERTISE_10HALF | ADVERTISE_100HALF;
}
}
else if( xPHYProperties.duplex == PHY_DUPLEX_AUTO )
{
if( xPHYProperties.speed == PHY_SPEED_10 )
{
ulAdvertise |= ADVERTISE_10FULL | ADVERTISE_10HALF;
}
else
{
ulAdvertise |= ADVERTISE_100FULL | ADVERTISE_100HALF;
}
}
else if( xPHYProperties.speed == PHY_SPEED_100 )
{
if( xPHYProperties.duplex == PHY_DUPLEX_FULL )
{
ulAdvertise |= ADVERTISE_100FULL;
}
else
{
ulAdvertise |= ADVERTISE_100HALF;
}
}
else
{
if( xPHYProperties.duplex == PHY_DUPLEX_FULL )
{
ulAdvertise |= ADVERTISE_10FULL;
}
else
{
ulAdvertise |= ADVERTISE_10HALF;
}
}
}
/* Read Control register. */
HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_00_BMCR, &ulConfig );
HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, ulConfig | BMCR_RESET );
xRemTime = ( TickType_t ) pdMS_TO_TICKS( 1000UL );
vTaskSetTimeOutState( &xPhyTime );
for( ; ; )
{
HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_00_BMCR, &ulValue );
if( ( ulValue & BMCR_RESET ) == 0 )
{
FreeRTOS_printf( ( "BMCR_RESET ready\n" ) );
break;
}
if( xTaskCheckForTimeOut( &xPhyTime, &xRemTime ) != pdFALSE )
{
FreeRTOS_printf( ( "BMCR_RESET timed out\n" ) );
break;
}
}
HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, ulConfig & ~BMCR_RESET );
vTaskDelay( pdMS_TO_TICKS( 50ul ) );
/* Write advertise register. */
HAL_ETH_WritePHYRegister( &xETH, PHY_REG_04_ADVERTISE, ulAdvertise );
/*
AN_EN AN1 AN0 Forced Mode
0 0 0 10BASE-T, Half-Duplex
0 0 1 10BASE-T, Full-Duplex
0 1 0 100BASE-TX, Half-Duplex
0 1 1 100BASE-TX, Full-Duplex
AN_EN AN1 AN0 Advertised Mode
1 0 0 10BASE-T, Half/Full-Duplex
1 0 1 100BASE-TX, Half/Full-Duplex
1 1 0 10BASE-T Half-Duplex
100BASE-TX, Half-Duplex
1 1 1 10BASE-T, Half/Full-Duplex
100BASE-TX, Half/Full-Duplex
*/
/* Read Control register. */
HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_00_BMCR, &ulConfig );
ulConfig &= ~( BMCR_ANRESTART | BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX );
/* HT 12/9/14: always set AN-restart and AN-enable, even though the choices
are limited. */
ulConfig |= (BMCR_ANRESTART | BMCR_ANENABLE);
if( xPHYProperties.speed == PHY_SPEED_100 )
{
ulConfig |= BMCR_SPEED100;
}
else if( xPHYProperties.speed == PHY_SPEED_10 )
{
ulConfig &= ~BMCR_SPEED100;
}
if( xPHYProperties.duplex == PHY_DUPLEX_FULL )
{
ulConfig |= BMCR_FULLDPLX;
}
else if( xPHYProperties.duplex == PHY_DUPLEX_HALF )
{
ulConfig &= ~BMCR_FULLDPLX;
}
#if( EXPECTED_PHY_ID == PHY_ID_LAN8720 )
{
}
#elif( EXPECTED_PHY_ID == PHY_ID_DP83848I )
{
/* Read PHY Control register. */
HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_19_PHYCR, &ulPhyControl );
/* Clear bits which might get set: */
ulPhyControl &= ~( PHYCR_MDIX_EN|PHYCR_MDIX_FORCE );
if( xPHYProperties.mdix == PHY_MDIX_AUTO )
{
ulPhyControl |= PHYCR_MDIX_EN;
}
else if( xPHYProperties.mdix == PHY_MDIX_CROSSED )
{
/* Force direct link = Use crossed RJ45 cable. */
ulPhyControl &= ~PHYCR_MDIX_FORCE;
}
else
{
/* Force crossed link = Use direct RJ45 cable. */
ulPhyControl |= PHYCR_MDIX_FORCE;
}
/* update PHY Control Register. */
HAL_ETH_WritePHYRegister( &xETH, PHY_REG_19_PHYCR, ulPhyControl );
}
#endif
FreeRTOS_printf( ( "+TCP: advertise: %lX config %lX\n", ulAdvertise, ulConfig ) );
/* Now the two values to global values for later use. */
ulBCRvalue = ulConfig;
ulACRValue = ulAdvertise;
vPhyInitialise( &xPhyObject, xSTM32_PhyRead, xSTM32_PhyWrite );
xPhyDiscover( &xPhyObject );
xPhyConfigure( &xPhyObject, &xPHYProperties );
}
/*-----------------------------------------------------------*/
static void prvEthernetUpdateConfig( BaseType_t xForce )
{
__IO uint32_t ulTimeout = 0;
uint32_t ulRegValue = 0;
FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS mask %02lX Force %d\n",
xPhyObject.ulLinkStatusMask,
( int )xForce ) );
FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS %d Force %d\n",
( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ,
xForce ) );
if( ( xForce != pdFALSE ) || ( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) )
if( ( xForce != pdFALSE ) || ( xPhyObject.ulLinkStatusMask != 0 ) )
{
/* Restart the auto-negotiation. */
if( xETH.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE )
{
/* Enable Auto-Negotiation. */
HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, ulBCRvalue | BMCR_ANRESTART );
HAL_ETH_WritePHYRegister( &xETH, PHY_REG_04_ADVERTISE, ulACRValue);
/* Wait until the auto-negotiation will be completed */
do
{
ulTimeout++;
HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_01_BMSR, &ulRegValue );
} while( ( ( ulRegValue & PHY_AUTONEGO_COMPLETE) == 0 ) && ( ulTimeout < PHY_READ_TO ) );
HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, ulBCRvalue & ~BMCR_ANRESTART );
if( ulTimeout < PHY_READ_TO )
{
/* Reset Timeout counter. */
ulTimeout = 0;
HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_01_BMSR, &ulRegValue);
if( ( ulRegValue & BMSR_LINK_STATUS ) != 0 )
{
ulPHYLinkStatus |= BMSR_LINK_STATUS;
}
else
{
ulPHYLinkStatus &= ~( BMSR_LINK_STATUS );
}
#if( EXPECTED_PHY_ID == PHY_ID_LAN8720 )
{
/* 31 RW PHY Special Control Status */
uint32_t ulControlStatus;
HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_1F_PHYSPCS, &ulControlStatus);
ulRegValue = 0;
if( ( ulControlStatus & PHYSPCS_FULL_DUPLEX ) != 0 )
{
ulRegValue |= PHY_DUPLEX_STATUS;
}
if( ( ulControlStatus & PHYSPCS_SPEED_MASK ) == PHYSPCS_SPEED_10 )
{
ulRegValue |= PHY_SPEED_STATUS;
}
}
#elif( EXPECTED_PHY_ID == PHY_ID_DP83848I )
{
/* Read the result of the auto-negotiation. */
HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_10_PHY_SR, &ulRegValue);
}
#endif
FreeRTOS_printf( ( ">> Autonego ready: %08lx: %s duplex %u mbit %s status\n",
ulRegValue,
(ulRegValue & PHY_DUPLEX_STATUS) ? "full" : "half",
(ulRegValue & PHY_SPEED_STATUS) ? 10 : 100,
((ulPHYLinkStatus |= BMSR_LINK_STATUS) != 0) ? "high" : "low" ) );
xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) );
/* Configure the MAC with the Duplex Mode fixed by the
auto-negotiation process. */
if( ( ulRegValue & PHY_DUPLEX_STATUS ) != ( uint32_t ) RESET )
if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL )
{
/* Set Ethernet duplex mode to Full-duplex following the
auto-negotiation. */
xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
}
else
{
/* Set Ethernet duplex mode to Half-duplex following the
auto-negotiation. */
xETH.Init.DuplexMode = ETH_MODE_HALFDUPLEX;
}
/* Configure the MAC with the speed fixed by the
auto-negotiation process. */
if( ( ulRegValue & PHY_SPEED_STATUS) != 0 )
if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 )
{
/* Set Ethernet speed to 10M following the
auto-negotiation. */
xETH.Init.Speed = ETH_SPEED_10M;
}
else
{
/* Set Ethernet speed to 100M following the
auto-negotiation. */
xETH.Init.Speed = ETH_SPEED_100M;
}
} /* if( ulTimeout < PHY_READ_TO ) */
}
else /* AutoNegotiation Disable */
{
uint16_t usValue;
/* Check parameters */
assert_param( IS_ETH_SPEED( xETH.Init.Speed ) );
assert_param( IS_ETH_DUPLEX_MODE( xETH.Init.DuplexMode ) );
/* Set MAC Speed and Duplex Mode to PHY */
usValue = ( uint16_t ) ( xETH.Init.DuplexMode >> 3 ) | ( uint16_t ) ( xETH.Init.Speed >> 1 );
HAL_ETH_WritePHYRegister( &xETH, PHY_REG_00_BMCR, usValue );
if( xETH.Init.DuplexMode == ETH_MODE_FULLDUPLEX )
{
xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_HALF;
}
else
{
xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_FULL;
}
if( xETH.Init.Speed == ETH_SPEED_10M )
{
xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_10;
}
else
{
xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_100;
}
xPhyObject.xPhyPreferences.ucMDI_X = PHY_MDIX_AUTO;
/* Use predefined (fixed) configuration. */
xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) );
}
/* ETHERNET MAC Re-Configuration */
@ -1312,7 +1082,7 @@ BaseType_t xGetPhyLinkStatus( void )
{
BaseType_t xReturn;
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
if( xPhyObject.ulLinkStatusMask != 0 )
{
xReturn = pdPASS;
}
@ -1325,27 +1095,45 @@ BaseType_t xReturn;
}
/*-----------------------------------------------------------*/
/* Uncomment this in case BufferAllocation_1.c is used. */
/*
#define niBUFFER_1_PACKET_SIZE 1536
static __attribute__ ((section(".first_data"))) uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) );
void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
{
uint8_t *ucRAMBuffer = ucNetworkPackets;
uint32_t ul;
for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
{
pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
*( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
}
}
*/
/*-----------------------------------------------------------*/
static void prvEMACHandlerTask( void *pvParameters )
{
TimeOut_t xPhyTime;
TickType_t xPhyRemTime;
UBaseType_t uxLastMinBufferCount = 0;
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
UBaseType_t uxLastMinQueueSpace = 0;
#endif
UBaseType_t uxCurrentCount;
BaseType_t xResult = 0;
uint32_t xStatus;
BaseType_t xResult;
const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
/* Remove compiler warnings about unused parameters. */
( void ) pvParameters;
vTaskSetTimeOutState( &xPhyTime );
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
for( ;; )
{
xResult = 0;
uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
if( uxLastMinBufferCount != uxCurrentCount )
{
@ -1356,7 +1144,6 @@ const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
}
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
if( xTXDescriptorSemaphore != NULL )
{
static UBaseType_t uxLowestSemCount = ( UBaseType_t ) ETH_TXBUFNB - 1;
@ -1369,7 +1156,7 @@ const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
}
}
#endif
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
{
uxCurrentCount = uxGetMinimumIPQueueSpace();
@ -1406,48 +1193,20 @@ const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
{
/* Code to release TX buffers if zero-copy is used. */
ulISREvents &= ~EMAC_IF_TX_EVENT;
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
{
/* Check if DMA packets have been delivered. */
vClearTXBuffers();
}
#endif
}
if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 )
{
/* Future extension: logging about errors that occurred. */
ulISREvents &= ~EMAC_IF_ERR_EVENT;
}
if( xResult > 0 )
if( xPhyCheckLinkStatus( &xPhyObject, xResult ) != 0 )
{
/* A packet was received. No need to check for the PHY status now,
but set a timer to check it later on. */
vTaskSetTimeOutState( &xPhyTime );
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
xResult = 0;
}
else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
{
HAL_ETH_ReadPHYRegister( &xETH, PHY_REG_01_BMSR, &xStatus );
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) )
{
ulPHYLinkStatus = xStatus;
FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) );
/* Something has changed to a Link Status, need re-check. */
prvEthernetUpdateConfig( pdFALSE );
}
vTaskSetTimeOutState( &xPhyTime );
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
{
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
}
else
{
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
}
}
}
}
/*-----------------------------------------------------------*/
@ -1456,3 +1215,4 @@ void ETH_IRQHandler( void )
{
HAL_ETH_IRQHandler( &xETH );
}

View file

@ -0,0 +1,119 @@
#warning Temoporary file and a dependent on the Zynq network interface.
/*
* uncached_memory.c
*
* This module will declare 1 MB of memory and switch off the caching for it.
*
* pucGetUncachedMemory( ulSize ) returns a trunc of this memory with a length
* rounded up to a multiple of 4 KB
*
* ucIsCachedMemory( pucBuffer ) returns non-zero if a given pointer is NOT
* within the range of the 1 MB non-cached memory.
*
*/
/*
* After "_end", 1 MB of uncached memory will be allocated for DMA transfers.
* Both the DMA descriptors as well as all EMAC TX-buffers will be allocated in
* uncached memory.
*/
#include "Zynq/x_emacpsif.h"
#include "Zynq/x_topology.h"
#include "xstatus.h"
#include "xparameters.h"
#include "xparameters_ps.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "FreeRTOS.h"
#include "uncached_memory.h"
#include "Demo_Logging.h"
#define UNCACHED_MEMORY_SIZE 0x100000ul
#define DDR_MEMORY_END (XPAR_PS7_DDR_0_S_AXI_HIGHADDR+1)
static void vInitialiseUncachedMemory( void );
static uint8_t *pucHeadOfMemory;
static uint32_t ulMemorySize;
static uint8_t *pucStartOfMemory = NULL;
uint8_t ucIsCachedMemory( const uint8_t *pucBuffer )
{
uint8_t ucReturn;
if( ( pucStartOfMemory != NULL ) &&
( pucBuffer >= pucStartOfMemory ) &&
( pucBuffer < ( pucStartOfMemory + UNCACHED_MEMORY_SIZE ) ) )
{
ucReturn = pdFALSE;
}
else
{
ucReturn = pdTRUE;
}
return ucReturn;
}
uint8_t *pucGetUncachedMemory( uint32_t ulSize )
{
uint8_t *pucReturn;
if( pucStartOfMemory == NULL )
{
vInitialiseUncachedMemory( );
}
if( ( pucStartOfMemory == NULL ) || ( ulSize > ulMemorySize ) )
{
pucReturn = NULL;
}
else
{
uint32_t ulSkipSize;
pucReturn = pucHeadOfMemory;
ulSkipSize = ( ulSize + 0x1000ul ) & ~0xffful;
pucHeadOfMemory += ulSkipSize;
ulMemorySize -= ulSkipSize;
}
return pucReturn;
}
extern u8 _end;
static void vInitialiseUncachedMemory( )
{
/* At the end of program's space... */
pucStartOfMemory = (uint8_t *) &_end;
/*
* Align the start address to 1 MB boundary.
*/
pucStartOfMemory = (uint8_t *)( ( ( uint32_t )pucStartOfMemory + UNCACHED_MEMORY_SIZE ) & ( ~( UNCACHED_MEMORY_SIZE - 1 ) ) );
if( ( ( u32 )pucStartOfMemory ) + UNCACHED_MEMORY_SIZE > DDR_MEMORY_END )
{
vLoggingPrintf("vInitialiseUncachedMemory: Can not allocate uncached memory\n" );
}
else
{
/*
* Some objects want to be stored in uncached memory. Hence the 1 MB
* address range that starts after "_end" is made uncached
* by setting appropriate attributes in the translation table.
*/
Xil_SetTlbAttributes( ( uint32_t )pucStartOfMemory, 0xc02 ); // addr, attr
/* For experiments in the SDIO driver, make the remaining uncached memory public */
pucHeadOfMemory = pucStartOfMemory;
ulMemorySize = UNCACHED_MEMORY_SIZE;
memset( pucStartOfMemory, '\0', UNCACHED_MEMORY_SIZE );
}
}

View file

@ -0,0 +1,23 @@
/*
* uncached_memory.h
*
* This module will declare 1 MB of memory and switch off the caching for it.
*
* pucGetUncachedMemory( ulSize ) returns a trunc of this memory with a length
* rounded up to a multiple of 4 KB
*
* ucIsCachedMemory( pucBuffer ) returns non-zero if a given pointer is NOT
* within the range of the 1 MB non-cached memory.
*
*/
#ifndef UNCACHEMEMORY_H
#define UNCACHEMEMORY_H
uint8_t *pucGetUncachedMemory( uint32_t ulSize );
uint8_t ucIsCachedMemory( const uint8_t *pucBuffer );
#endif /* UNCACHEMEMORY_H */

View file

@ -0,0 +1,118 @@
/*
* Handling of Ethernet PHY's
* PHY's communicate with an EMAC either through
* a Media-Independent Interface (MII), or a Reduced Media-Independent Interface (RMII).
* The EMAC can poll for PHY ports on 32 different addresses. Each of the PHY ports
* shall be treated independently.
*
*/
#ifndef PHYHANDLING_H
#define PHYHANDLING_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ipconfigPHY_MAX_PORTS
/* There can be at most 32 PHY ports, but in most cases there are 4 or less. */
#define ipconfigPHY_MAX_PORTS 4
#endif
/* A generic user-provided function that reads from the PHY-port at 'xAddress'( 0-based ). A 16-bit value shall be stored in
'*pusValue'. xRegister is the register number ( 0 .. 31 ). In fact all PHY registers are 16-bit.
Return non-zero in case the action failed. */
typedef BaseType_t ( *xApplicationPhyReadHook_t )( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue );
/* A generic user-provided function that writes 'usValue' to the
PHY-port at 'xAddress' ( 0-based ). xRegister is the register number ( 0 .. 31 ).
Return non-zero in case the action failed. */
typedef BaseType_t ( *xApplicationPhyWriteHook_t )( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue );
typedef struct xPhyProperties
{
uint8_t ucSpeed;
uint8_t ucMDI_X; /* MDI-X : Medium Dependent Interface - Crossover */
uint8_t ucDuplex;
uint8_t ucSpare;
} PhyProperties_t;
typedef struct xEthernetPhy
{
xApplicationPhyReadHook_t fnPhyRead;
xApplicationPhyWriteHook_t fnPhyWrite;
uint32_t ulPhyIDs[ ipconfigPHY_MAX_PORTS ];
uint8_t ucPhyIndexes[ ipconfigPHY_MAX_PORTS ];
TimeOut_t xLinkStatusTimer;
TickType_t xLinkStatusRemaining;
BaseType_t xPortCount;
uint32_t ulBCRValue;
uint32_t ulACRValue;
uint32_t ulLinkStatusMask;
PhyProperties_t xPhyPreferences;
PhyProperties_t xPhyProperties;
} EthernetPhy_t;
/* Some defines used internally here to indicate preferences about speed, MDIX
(wired direct or crossed), and duplex (half or full). */
/* Values for PhyProperties_t::ucSpeed : */
#define PHY_SPEED_10 1
#define PHY_SPEED_100 2
#define PHY_SPEED_AUTO 3
/* Values for PhyProperties_t::ucMDI_X : */
#define PHY_MDIX_DIRECT 1
#define PHY_MDIX_CROSSED 2
#define PHY_MDIX_AUTO 3
/* Values for PhyProperties_t::ucDuplex : */
#define PHY_DUPLEX_HALF 1
#define PHY_DUPLEX_FULL 2
#define PHY_DUPLEX_AUTO 3
/* ID's of supported PHY's : */
#define PHY_ID_LAN8742A 0x0007c130
#define PHY_ID_LAN8720 0x0007c0f0
#define PHY_ID_KSZ8041 0x000010A1
#define PHY_ID_KSZ8051 0x000010A1
#define PHY_ID_KSZ8081 0x000010A1
#define PHY_ID_KSZ8863 0x00221430
#define PHY_ID_DP83848I 0x20005C90
/* Initialise the struct and assign a PHY-read and -write function. */
void vPhyInitialise( EthernetPhy_t *pxPhyObject, xApplicationPhyReadHook_t fnPhyRead, xApplicationPhyWriteHook_t fnPhyWrite );
/* Discover all PHY's connected by polling 32 indexes ( zero-based ) */
BaseType_t xPhyDiscover( EthernetPhy_t *pxPhyObject );
/* Send a reset commando to the connected PHY ports and send configuration. */
BaseType_t xPhyConfigure( EthernetPhy_t *pxPhyObject, const PhyProperties_t *pxPhyProperties );
/* Give a commando to start auto negotiation on a set of PHY port's. */
BaseType_t xPhyStartAutoNegotiation( EthernetPhy_t *pxPhyObject, uint32_t ulPhyMask );
/* Do not use auto negotiation but use predefined values from 'pxPhyObject->xPhyPreferences'. */
BaseType_t xPhyFixedValue( EthernetPhy_t *pxPhyObject, uint32_t ulPhyMask );
/* Check the current Link Status.
'xHadReception' : make this true if a packet has been received since the
last call to this function. */
BaseType_t xPhyCheckLinkStatus( EthernetPhy_t *pxPhyObject, BaseType_t xHadReception );
static __inline uint32_t xPhyGetMask( EthernetPhy_t *pxPhyObject )
{
return ( ( ( uint32_t ) 1u ) << pxPhyObject-> xPortCount ) - 1;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View file

@ -0,0 +1,610 @@
/**
*
* \file
*
* \brief KS8851SNL driver for SAM.
*
* Copyright (c) 2013-2015 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>
*/
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "spi_master.h"
#include "ksz8851snl.h"
#include "ksz8851snl_reg.h"
#include "delay.h"
#include "pio.h"
#include "pio_handler.h"
#include "pdc.h"
#include "conf_eth.h"
/* Clock polarity. */
#define SPI_CLK_POLARITY 0
/* Clock phase. */
#define SPI_CLK_PHASE 1
/* SPI PDC register base. */
Pdc *g_p_spi_pdc = 0;
int lUDPLoggingPrintf( const char *pcFormatString, ... );
/* Temporary buffer for PDC reception. */
uint8_t tmpbuf[1536] __attribute__ ((aligned (16)));
union {
uint64_t ul[2];
uint8_t uc[16];
} cmdBuf, respBuf;
void dbg_add_line( const char *pcFormat, ... );
static void spi_clear_ovres( void );
/**
* \brief Read register content, set bitmask and write back to register.
*
* \param reg the register address to modify.
* \param bits_to_set bitmask to apply.
*/
void ksz8851_reg_setbits(uint16_t reg, uint16_t bits_to_set)
{
uint16_t temp;
temp = ksz8851_reg_read(reg);
temp |= bits_to_set;
ksz8851_reg_write(reg, temp);
}
/**
* \brief Read register content, clear bitmask and write back to register.
*
* \param reg the register address to modify.
* \param bits_to_set bitmask to apply.
*/
void ksz8851_reg_clrbits(uint16_t reg, uint16_t bits_to_clr)
{
uint16_t temp;
temp = ksz8851_reg_read(reg);
temp &= ~(uint32_t) bits_to_clr;
ksz8851_reg_write(reg, temp);
}
/**
* \brief Configure the INTN interrupt.
*/
void configure_intn(void (*p_handler) (uint32_t, uint32_t))
{
// gpio_configure_pin(KSZ8851SNL_INTN_GPIO, PIO_INPUT);
// pio_set_input(PIOA, PIO_PA11_IDX, PIO_PULLUP);
/* Configure PIO clock. */
pmc_enable_periph_clk(INTN_ID);
/* Adjust PIO debounce filter parameters, uses 10 Hz filter. */
pio_set_debounce_filter(INTN_PIO, INTN_PIN_MSK, 10);
/* Initialize PIO interrupt handlers, see PIO definition in board.h. */
pio_handler_set(INTN_PIO, INTN_ID, INTN_PIN_MSK,
INTN_ATTR, p_handler);
/* Enable NVIC interrupts. */
NVIC_SetPriority(INTN_IRQn, INT_PRIORITY_PIO);
NVIC_EnableIRQ((IRQn_Type)INTN_ID);
/* Enable PIO interrupts. */
pio_enable_interrupt(INTN_PIO, INTN_PIN_MSK);
}
/**
* \brief Read a register value.
*
* \param reg the register address to modify.
*
* \return the register value.
*/
uint16_t ksz8851_reg_read(uint16_t reg)
{
pdc_packet_t g_pdc_spi_tx_packet;
pdc_packet_t g_pdc_spi_rx_packet;
uint16_t cmd = 0;
uint16_t res = 0;
int iTryCount = 3;
while( iTryCount-- > 0 )
{
uint32_t ulStatus;
spi_clear_ovres();
/* Move register address to cmd bits 9-2, make 32-bit address. */
cmd = (reg << 2) & REG_ADDR_MASK;
/* Last 2 bits still under "don't care bits" handled with byte enable. */
/* Select byte enable for command. */
if (reg & 2) {
/* Odd word address writes bytes 2 and 3 */
cmd |= (0xc << 10);
} else {
/* Even word address write bytes 0 and 1 */
cmd |= (0x3 << 10);
}
/* Add command read code. */
cmd |= CMD_READ;
cmdBuf.uc[0] = cmd >> 8;
cmdBuf.uc[1] = cmd & 0xff;
cmdBuf.uc[2] = CONFIG_SPI_MASTER_DUMMY;
cmdBuf.uc[3] = CONFIG_SPI_MASTER_DUMMY;
/* Prepare PDC transfer. */
g_pdc_spi_tx_packet.ul_addr = (uint32_t) cmdBuf.uc;
g_pdc_spi_tx_packet.ul_size = 4;
g_pdc_spi_rx_packet.ul_addr = (uint32_t) tmpbuf;
g_pdc_spi_rx_packet.ul_size = 4;
pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);
pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, NULL);
pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, NULL);
gpio_set_pin_low(KSZ8851SNL_CSN_GPIO);
spi_disable_interrupt( KSZ8851SNL_SPI, ~0ul );
pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN);
for( ;; )
{
ulStatus = spi_read_status( KSZ8851SNL_SPI );
if( ( ulStatus & ( SPI_SR_OVRES | SPI_SR_ENDRX ) ) != 0 )
{
break;
}
}
gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
if( ( ulStatus & SPI_SR_OVRES ) == 0 )
{
break;
}
pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);
lUDPLoggingPrintf( "ksz8851_reg_read: SPI_SR_OVRES\n" );
}
res = (tmpbuf[3] << 8) | tmpbuf[2];
return res;
}
/**
* \brief Write a register value.
*
* \param reg the register address to modify.
* \param wrdata the new register value.
*/
void ksz8851_reg_write(uint16_t reg, uint16_t wrdata)
{
pdc_packet_t g_pdc_spi_tx_packet;
pdc_packet_t g_pdc_spi_rx_packet;
uint16_t cmd = 0;
int iTryCount = 3;
while( iTryCount-- > 0 )
{
uint32_t ulStatus;
spi_clear_ovres();
/* Move register address to cmd bits 9-2, make 32-bit address. */
cmd = (reg << 2) & REG_ADDR_MASK;
/* Last 2 bits still under "don't care bits" handled with byte enable. */
/* Select byte enable for command. */
if (reg & 2) {
/* Odd word address writes bytes 2 and 3 */
cmd |= (0xc << 10);
} else {
/* Even word address write bytes 0 and 1 */
cmd |= (0x3 << 10);
}
/* Add command write code. */
cmd |= CMD_WRITE;
cmdBuf.uc[0] = cmd >> 8;
cmdBuf.uc[1] = cmd & 0xff;
cmdBuf.uc[2] = wrdata & 0xff;
cmdBuf.uc[3] = wrdata >> 8;
/* Prepare PDC transfer. */
g_pdc_spi_tx_packet.ul_addr = (uint32_t) cmdBuf.uc;
g_pdc_spi_tx_packet.ul_size = 4;
g_pdc_spi_rx_packet.ul_addr = (uint32_t) tmpbuf;
g_pdc_spi_rx_packet.ul_size = 4;
pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);
pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, NULL);
pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, NULL);
gpio_set_pin_low(KSZ8851SNL_CSN_GPIO);
spi_disable_interrupt( KSZ8851SNL_SPI, ~0ul );
pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN);
for( ;; )
{
ulStatus = spi_read_status( KSZ8851SNL_SPI );
if( ( ulStatus & ( SPI_SR_OVRES | SPI_SR_ENDRX ) ) != 0 )
{
break;
}
}
gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
if( ( ulStatus & SPI_SR_OVRES ) == 0 )
{
break;
}
pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);
lUDPLoggingPrintf( "ksz8851_reg_write: SPI_SR_OVRES\n" );
}
}
static void spi_clear_ovres( void )
{
volatile uint32_t rc;
rc = KSZ8851SNL_SPI->SPI_RDR;
spi_read_status( KSZ8851SNL_SPI );
}
/**
* \brief Read internal fifo buffer.
*
* \param buf the buffer to store the data from the fifo buffer.
* \param len the amount of data to read.
*/
void ksz8851_fifo_read(uint8_t *buf, uint32_t len)
{
pdc_packet_t g_pdc_spi_tx_packet;
pdc_packet_t g_pdc_spi_rx_packet;
pdc_packet_t g_pdc_spi_tx_npacket;
pdc_packet_t g_pdc_spi_rx_npacket;
memset( cmdBuf.uc, '\0', sizeof cmdBuf );
cmdBuf.uc[0] = FIFO_READ;
spi_clear_ovres();
/* Prepare PDC transfer. */
g_pdc_spi_tx_packet.ul_addr = (uint32_t) cmdBuf.uc;
g_pdc_spi_tx_packet.ul_size = 9;
g_pdc_spi_rx_packet.ul_addr = (uint32_t) respBuf.uc;
g_pdc_spi_rx_packet.ul_size = 9;
g_pdc_spi_tx_npacket.ul_addr = (uint32_t) buf;
g_pdc_spi_tx_npacket.ul_size = len;
g_pdc_spi_rx_npacket.ul_addr = (uint32_t) buf;
g_pdc_spi_rx_npacket.ul_size = len;
pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);
pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, &g_pdc_spi_tx_npacket);
pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, &g_pdc_spi_rx_npacket);
spi_enable_interrupt(KSZ8851SNL_SPI, SPI_IER_RXBUFF | SPI_IER_OVRES);
pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN);
}
/**
* \brief Write internal fifo buffer.
*
* \param buf the buffer to send to the fifo buffer.
* \param ulActualLength the total amount of data to write.
* \param ulFIFOLength the size of the first pbuf to write from the pbuf chain.
*/
void ksz8851_fifo_write(uint8_t *buf, uint32_t ulActualLength, uint32_t ulFIFOLength)
{
static uint8_t frameID = 0;
pdc_packet_t g_pdc_spi_tx_packet;
pdc_packet_t g_pdc_spi_rx_packet;
pdc_packet_t g_pdc_spi_tx_npacket;
pdc_packet_t g_pdc_spi_rx_npacket;
/* Prepare control word and byte count. */
cmdBuf.uc[0] = FIFO_WRITE;
cmdBuf.uc[1] = frameID++ & 0x3f;
cmdBuf.uc[2] = 0;
cmdBuf.uc[3] = ulActualLength & 0xff;
cmdBuf.uc[4] = ulActualLength >> 8;
spi_clear_ovres();
/* Prepare PDC transfer. */
g_pdc_spi_tx_packet.ul_addr = (uint32_t) cmdBuf.uc;
g_pdc_spi_tx_packet.ul_size = 5;
g_pdc_spi_rx_packet.ul_addr = (uint32_t) respBuf.uc;
g_pdc_spi_rx_packet.ul_size = 5;
g_pdc_spi_tx_npacket.ul_addr = (uint32_t) buf;
g_pdc_spi_tx_npacket.ul_size = ulFIFOLength;
g_pdc_spi_rx_npacket.ul_addr = (uint32_t) tmpbuf;
g_pdc_spi_rx_npacket.ul_size = ulFIFOLength;
pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);
pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, &g_pdc_spi_tx_npacket);
#if( TX_USES_RECV == 1 )
pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, &g_pdc_spi_rx_npacket);
spi_enable_interrupt(KSZ8851SNL_SPI, SPI_IER_ENDRX | SPI_IER_OVRES);
pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN);
#else
spi_enable_interrupt(KSZ8851SNL_SPI, SPI_SR_TXBUFE | SPI_IER_OVRES);
pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_TXTEN);
#endif
}
/**
* \brief Write dummy data to the internal fifo buffer.
*
* \param len the amount of dummy data to write.
*/
void ksz8851_fifo_dummy(uint32_t len)
{
pdc_packet_t g_pdc_spi_tx_packet;
pdc_packet_t g_pdc_spi_rx_packet;
/* Prepare PDC transfer. */
g_pdc_spi_tx_packet.ul_addr = (uint32_t) tmpbuf;
g_pdc_spi_tx_packet.ul_size = len;
g_pdc_spi_rx_packet.ul_addr = (uint32_t) tmpbuf;
g_pdc_spi_rx_packet.ul_size = len;
pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);
pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, NULL);
pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, NULL);
pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN);
while (!(spi_read_status(KSZ8851SNL_SPI) & SPI_SR_ENDRX))
;
}
void ksz8851snl_set_registers(void)
{
/* Init step2-4: write QMU MAC address (low, middle then high). */
ksz8851_reg_write(REG_MAC_ADDR_0, (ETHERNET_CONF_ETHADDR4 << 8) | ETHERNET_CONF_ETHADDR5);
ksz8851_reg_write(REG_MAC_ADDR_2, (ETHERNET_CONF_ETHADDR2 << 8) | ETHERNET_CONF_ETHADDR3);
ksz8851_reg_write(REG_MAC_ADDR_4, (ETHERNET_CONF_ETHADDR0 << 8) | ETHERNET_CONF_ETHADDR1);
/* Init step5: enable QMU Transmit Frame Data Pointer Auto Increment. */
ksz8851_reg_write(REG_TX_ADDR_PTR, ADDR_PTR_AUTO_INC);
/* Init step6: configure QMU transmit control register. */
ksz8851_reg_write(REG_TX_CTRL,
TX_CTRL_ICMP_CHECKSUM |
TX_CTRL_UDP_CHECKSUM |
TX_CTRL_TCP_CHECKSUM |
TX_CTRL_IP_CHECKSUM |
TX_CTRL_FLOW_ENABLE |
TX_CTRL_PAD_ENABLE |
TX_CTRL_CRC_ENABLE
);
/* Init step7: enable QMU Receive Frame Data Pointer Auto Increment. */
ksz8851_reg_write(REG_RX_ADDR_PTR, ADDR_PTR_AUTO_INC);
/* Init step8: configure QMU Receive Frame Threshold for one frame. */
ksz8851_reg_write(REG_RX_FRAME_CNT_THRES, 1);
/* Init step9: configure QMU receive control register1. */
ksz8851_reg_write(REG_RX_CTRL1,
RX_CTRL_UDP_CHECKSUM |
RX_CTRL_TCP_CHECKSUM |
RX_CTRL_IP_CHECKSUM |
RX_CTRL_MAC_FILTER |
RX_CTRL_FLOW_ENABLE |
RX_CTRL_BROADCAST |
RX_CTRL_ALL_MULTICAST|
RX_CTRL_UNICAST);
// ksz8851_reg_write(REG_RX_CTRL1,
// RX_CTRL_UDP_CHECKSUM |
// RX_CTRL_TCP_CHECKSUM |
// RX_CTRL_IP_CHECKSUM |
// RX_CTRL_FLOW_ENABLE |
// RX_CTRL_PROMISCUOUS);
ksz8851_reg_write(REG_RX_CTRL2,
RX_CTRL_IPV6_UDP_NOCHECKSUM |
RX_CTRL_UDP_LITE_CHECKSUM |
RX_CTRL_ICMP_CHECKSUM |
RX_CTRL_BURST_LEN_FRAME);
//#define RXQ_TWOBYTE_OFFSET (0x0200) /* Enable adding 2-byte before frame header for IP aligned with DWORD */
#warning Remember to try the above option to get a 2-byte offset
/* Init step11: configure QMU receive queue: trigger INT and auto-dequeue frame. */
ksz8851_reg_write( REG_RXQ_CMD, RXQ_CMD_CNTL | RXQ_TWOBYTE_OFFSET );
/* Init step12: adjust SPI data output delay. */
ksz8851_reg_write(REG_BUS_CLOCK_CTRL, BUS_CLOCK_166 | BUS_CLOCK_DIVIDEDBY_1);
/* Init step13: restart auto-negotiation. */
ksz8851_reg_setbits(REG_PORT_CTRL, PORT_AUTO_NEG_RESTART);
/* Init step13.1: force link in half duplex if auto-negotiation failed. */
if ((ksz8851_reg_read(REG_PORT_CTRL) & PORT_AUTO_NEG_RESTART) != PORT_AUTO_NEG_RESTART)
{
ksz8851_reg_clrbits(REG_PORT_CTRL, PORT_FORCE_FULL_DUPLEX);
}
/* Init step14: clear interrupt status. */
ksz8851_reg_write(REG_INT_STATUS, 0xFFFF);
/* Init step15: set interrupt mask. */
ksz8851_reg_write(REG_INT_MASK, INT_RX);
/* Init step16: enable QMU Transmit. */
ksz8851_reg_setbits(REG_TX_CTRL, TX_CTRL_ENABLE);
/* Init step17: enable QMU Receive. */
ksz8851_reg_setbits(REG_RX_CTRL1, RX_CTRL_ENABLE);
}
/**
* \brief KSZ8851SNL initialization function.
*
* \return 0 on success, 1 on communication error.
*/
uint32_t ksz8851snl_init(void)
{
uint32_t count = 10;
uint16_t dev_id = 0;
uint8_t id_ok = 0;
/* Configure the SPI peripheral. */
spi_enable_clock(KSZ8851SNL_SPI);
spi_disable(KSZ8851SNL_SPI);
spi_reset(KSZ8851SNL_SPI);
spi_set_master_mode(KSZ8851SNL_SPI);
spi_disable_mode_fault_detect(KSZ8851SNL_SPI);
spi_set_peripheral_chip_select_value(KSZ8851SNL_SPI, ~(uint32_t)(1UL << KSZ8851SNL_CS_PIN));
spi_set_fixed_peripheral_select(KSZ8851SNL_SPI);
//spi_disable_peripheral_select_decode(KSZ8851SNL_SPI);
spi_set_clock_polarity(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, SPI_CLK_POLARITY);
spi_set_clock_phase(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, SPI_CLK_PHASE);
spi_set_bits_per_transfer(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN,
SPI_CSR_BITS_8_BIT);
spi_set_baudrate_div(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, (sysclk_get_cpu_hz() / KSZ8851SNL_CLOCK_SPEED));
// spi_set_transfer_delay(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, CONFIG_SPI_MASTER_DELAY_BS,
// CONFIG_SPI_MASTER_DELAY_BCT);
spi_set_transfer_delay(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, 0, 0);
spi_enable(KSZ8851SNL_SPI);
/* Get pointer to UART PDC register base. */
g_p_spi_pdc = spi_get_pdc_base(KSZ8851SNL_SPI);
pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN);
/* Control RSTN and CSN pin from the driver. */
gpio_configure_pin(KSZ8851SNL_CSN_GPIO, KSZ8851SNL_CSN_FLAGS);
gpio_set_pin_high(KSZ8851SNL_CSN_GPIO);
gpio_configure_pin(KSZ8851SNL_RSTN_GPIO, KSZ8851SNL_RSTN_FLAGS);
/* Reset the Micrel in a proper state. */
while( count-- )
{
/* Perform hardware reset with respect to the reset timing from the datasheet. */
gpio_set_pin_low(KSZ8851SNL_RSTN_GPIO);
vTaskDelay(2);
gpio_set_pin_high(KSZ8851SNL_RSTN_GPIO);
vTaskDelay(2);
/* Init step1: read chip ID. */
dev_id = ksz8851_reg_read(REG_CHIP_ID);
if( ( dev_id & 0xFFF0 ) == CHIP_ID_8851_16 )
{
id_ok = 1;
break;
}
}
if( id_ok != 0 )
{
ksz8851snl_set_registers();
}
return id_ok ? 1 : -1;
}
uint32_t ksz8851snl_reinit(void)
{
uint32_t count = 10;
uint16_t dev_id = 0;
uint8_t id_ok = 0;
/* Reset the Micrel in a proper state. */
while( count-- )
{
/* Perform hardware reset with respect to the reset timing from the datasheet. */
gpio_set_pin_low(KSZ8851SNL_RSTN_GPIO);
vTaskDelay(2);
gpio_set_pin_high(KSZ8851SNL_RSTN_GPIO);
vTaskDelay(2);
/* Init step1: read chip ID. */
dev_id = ksz8851_reg_read(REG_CHIP_ID);
if( ( dev_id & 0xFFF0 ) == CHIP_ID_8851_16 )
{
id_ok = 1;
break;
}
}
if( id_ok != 0 )
{
ksz8851snl_set_registers();
}
return id_ok ? 1 : -1;
}
uint32_t ksz8851snl_reset_rx( void )
{
uint16_t usValue;
usValue = ksz8851_reg_read(REG_RX_CTRL1);
usValue &= ~( ( uint16_t ) RX_CTRL_ENABLE | RX_CTRL_FLUSH_QUEUE );
ksz8851_reg_write( REG_RX_CTRL1, usValue ); vTaskDelay( 2 );
ksz8851_reg_write( REG_RX_CTRL1, usValue | RX_CTRL_FLUSH_QUEUE ); vTaskDelay( 1 );
ksz8851_reg_write( REG_RX_CTRL1, usValue ); vTaskDelay( 1 );
ksz8851_reg_write( REG_RX_CTRL1, usValue | RX_CTRL_ENABLE ); vTaskDelay( 1 );
return ( uint32_t )usValue;
}
uint32_t ksz8851snl_reset_tx( void )
{
uint16_t usValue;
usValue = ksz8851_reg_read( REG_TX_CTRL );
usValue &= ~( ( uint16_t ) TX_CTRL_ENABLE | TX_CTRL_FLUSH_QUEUE );
ksz8851_reg_write( REG_TX_CTRL, usValue ); vTaskDelay( 2 );
ksz8851_reg_write( REG_TX_CTRL, usValue | TX_CTRL_FLUSH_QUEUE ); vTaskDelay( 1 );
ksz8851_reg_write( REG_TX_CTRL, usValue ); vTaskDelay( 1 );
ksz8851_reg_write( REG_TX_CTRL, usValue | TX_CTRL_ENABLE ); vTaskDelay( 1 );
return ( uint32_t )usValue;
}

View file

@ -0,0 +1,67 @@
/**
*
* \file
*
* \brief KS8851SNL driver for SAM.
*
* Copyright (c) 2013-2015 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>
*/
#ifndef KSZ8851SNL_H_INCLUDED
#define KSZ8851SNL_H_INCLUDED
#include "gpio.h"
void configure_intn(void (*p_handler) (uint32_t, uint32_t));
void ksz8851_reg_setbits(uint16_t reg, uint16_t bits_to_set);
void ksz8851_reg_clrbits(uint16_t reg, uint16_t bits_to_clr);
void ksz8851_fifo_read(uint8_t *buf, uint32_t len);
void ksz8851_fifo_write(uint8_t *buf, uint32_t ulActualLength, uint32_t ulFIFOLength);
void ksz8851_fifo_dummy(uint32_t len);
void ksz8851_reg_write(uint16_t reg, uint16_t wrdata);
uint16_t ksz8851_reg_read(uint16_t reg);
uint32_t ksz8851snl_init(void);
uint32_t ksz8851snl_reinit(void);
uint32_t ksz8851snl_reset_rx( void );
uint32_t ksz8851snl_reset_tx( void );
#endif /* KSZ8851SNL_H_INCLUDED */

View file

@ -0,0 +1,473 @@
/**
*
* \file
*
* \brief KS8851SNL registers definitions.
*
* Copyright (c) 2013-2015 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>
*/
#ifndef KSZ8851SNL_REG_H_INCLUDED
#define KSZ8851SNL_REG_H_INCLUDED
#define REG_ADDR_MASK (0x3F0) /* Register address mask */
#define OPCODE_MASK (3 << 14)
#define CMD_READ (0 << 14)
#define CMD_WRITE (1 << 14)
#define FIFO_READ (0x80)
#define FIFO_WRITE (0xC0)
/*
* MAC Registers
* (Offset 0x00 - 0x25)
*/
#define REG_BUS_ERROR_STATUS (0x06) /* BESR */
#define BUS_ERROR_IBEC (0x8000)
#define BUS_ERROR_IBECV_MASK (0x7800) /* Default IPSec clock at 166Mhz */
#define REG_CHIP_CFG_STATUS (0x08) /* CCFG */
#define LITTLE_ENDIAN_BUS_MODE (0x0400) /* Bus in little endian mode */
#define EEPROM_PRESENCE (0x0200) /* External EEPROM is used */
#define SPI_BUS_MODE (0x0100) /* In SPI bus mode */
#define DATA_BUS_8BIT (0x0080) /* In 8-bit bus mode operation */
#define DATA_BUS_16BIT (0x0040) /* In 16-bit bus mode operation */
#define DATA_BUS_32BIT (0x0020) /* In 32-bit bus mode operation */
#define MULTIPLEX_MODE (0x0010) /* Data and address bus are shared */
#define CHIP_PACKAGE_128PIN (0x0008) /* 128-pin package */
#define CHIP_PACKAGE_80PIN (0x0004) /* 80-pin package */
#define CHIP_PACKAGE_48PIN (0x0002) /* 48-pin package */
#define CHIP_PACKAGE_32PIN (0x0001) /* 32-pin package for SPI host interface only */
#define REG_MAC_ADDR_0 (0x10) /* MARL */
#define REG_MAC_ADDR_1 (0x11) /* MARL */
#define REG_MAC_ADDR_2 (0x12) /* MARM */
#define REG_MAC_ADDR_3 (0x13) /* MARM */
#define REG_MAC_ADDR_4 (0x14) /* MARH */
#define REG_MAC_ADDR_5 (0x15) /* MARH */
#define REG_BUS_CLOCK_CTRL (0x20) /* OBCR */
#define BUS_CLOCK_166 (0x0004) /* 166 MHz on-chip bus clock (defaul is 125MHz) */
#define BUS_CLOCK_DIVIDEDBY_5 (0x0003) /* Bus clock devided by 5 */
#define BUS_CLOCK_DIVIDEDBY_3 (0x0002) /* Bus clock devided by 3 */
#define BUS_CLOCK_DIVIDEDBY_2 (0x0001) /* Bus clock devided by 2 */
#define BUS_CLOCK_DIVIDEDBY_1 (0x0000) /* Bus clock devided by 1 */
#define BUS_CLOCK_DIVIDED_MASK (0x0003) /* Bus clock devider mask */
#define BUS_SPEED_166_MHZ (0x0004) /* Set bus speed to 166 MHz */
#define BUS_SPEED_125_MHZ (0x0000) /* Set bus speed to 125 MHz */
#define BUS_SPEED_83_MHZ (0x0005) /* Set bus speed to 83 MHz (166/2)*/
#define BUS_SPEED_62_5_MHZ (0x0001) /* Set bus speed to 62.5 MHz (125/2) */
#define BUS_SPEED_53_3_MHZ (0x0006) /* Set bus speed to 53.3 MHz (166/3) */
#define BUS_SPEED_41_7_MHZ (0x0002) /* Set bus speed to 41.67 MHz (125/3) */
#define BUS_SPEED_33_2_MHZ (0x0007) /* Set bus speed to 33.2 MHz (166/5) */
#define BUS_SPEED_25_MHZ (0x0003) /* Set bus speed to 25 MHz (125/5) */
#define REG_EEPROM_CTRL (0x22) /* EEPCR */
#define EEPROM_ACCESS_ENABLE (0x0010) /* Enable software to access EEPROM through bit 3 to bit 0 */
#define EEPROM_DATA_IN (0x0008) /* Data receive from EEPROM (EEDI pin) */
#define EEPROM_DATA_OUT (0x0004) /* Data transmit to EEPROM (EEDO pin) */
#define EEPROM_SERIAL_CLOCK (0x0002) /* Serial clock (EESK pin) */
#define EEPROM_CHIP_SELECT (0x0001) /* EEPROM chip select (EECS pin) */
#define REG_MEM_BIST_INFO (0x24) /* MBIR */
#define TX_MEM_TEST_FINISHED (0x1000) /* TX memeory BIST test finish */
#define TX_MEM_TEST_FAILED (0x0800) /* TX memory BIST test fail */
#define TX_MEM_TEST_FAILED_COUNT (0x0700) /* TX memory BIST test fail count */
#define RX_MEM_TEST_FINISHED (0x0010) /* RX memory BIST test finish */
#define RX_MEM_TEST_FAILED (0x0008) /* RX memory BIST test fail */
#define RX_MEM_TEST_FAILED_COUNT (0x0003) /* RX memory BIST test fail count */
#define REG_RESET_CTRL (0x26) /* GRR */
#define QMU_SOFTWARE_RESET (0x0002) /* QMU soft reset (clear TxQ, RxQ) */
#define GLOBAL_SOFTWARE_RESET (0x0001) /* Global soft reset (PHY, MAC, QMU) */
/*
* Wake On Lan Control Registers
* (Offset 0x2A - 0x6B)
*/
#define REG_WOL_CTRL (0x2A) /* WFCR */
#define WOL_MAGIC_ENABLE (0x0080) /* Enable the magic packet pattern detection */
#define WOL_FRAME3_ENABLE (0x0008) /* Enable the wake up frame 3 pattern detection */
#define WOL_FRAME2_ENABLE (0x0004) /* Enable the wake up frame 2 pattern detection */
#define WOL_FRAME1_ENABLE (0x0002) /* Enable the wake up frame 1 pattern detection */
#define WOL_FRAME0_ENABLE (0x0001) /* Enable the wake up frame 0 pattern detection */
#define REG_WOL_FRAME0_CRC0 (0x30) /* WF0CRC0 */
#define REG_WOL_FRAME0_CRC1 (0x32) /* WF0CRC1 */
#define REG_WOL_FRAME0_BYTE_MASK0 (0x34) /* WF0BM0 */
#define REG_WOL_FRAME0_BYTE_MASK1 (0x36) /* WF0BM1 */
#define REG_WOL_FRAME0_BYTE_MASK2 (0x38) /* WF0BM2 */
#define REG_WOL_FRAME0_BYTE_MASK3 (0x3A) /* WF0BM3 */
#define REG_WOL_FRAME1_CRC0 (0x40) /* WF1CRC0 */
#define REG_WOL_FRAME1_CRC1 (0x42) /* WF1CRC1 */
#define REG_WOL_FRAME1_BYTE_MASK0 (0x44) /* WF1BM0 */
#define REG_WOL_FRAME1_BYTE_MASK1 (0x46) /* WF1BM1 */
#define REG_WOL_FRAME1_BYTE_MASK2 (0x48) /* WF1BM2 */
#define REG_WOL_FRAME1_BYTE_MASK3 (0x4A) /* WF1BM3 */
#define REG_WOL_FRAME2_CRC0 (0x50) /* WF2CRC0 */
#define REG_WOL_FRAME2_CRC1 (0x52) /* WF2CRC1 */
#define REG_WOL_FRAME2_BYTE_MASK0 (0x54) /* WF2BM0 */
#define REG_WOL_FRAME2_BYTE_MASK1 (0x56) /* WF2BM1 */
#define REG_WOL_FRAME2_BYTE_MASK2 (0x58) /* WF2BM2 */
#define REG_WOL_FRAME2_BYTE_MASK3 (0x5A) /* WF2BM3 */
#define REG_WOL_FRAME3_CRC0 (0x60) /* WF3CRC0 */
#define REG_WOL_FRAME3_CRC1 (0x62) /* WF3CRC1 */
#define REG_WOL_FRAME3_BYTE_MASK0 (0x64) /* WF3BM0 */
#define REG_WOL_FRAME3_BYTE_MASK1 (0x66) /* WF3BM1 */
#define REG_WOL_FRAME3_BYTE_MASK2 (0x68) /* WF3BM2 */
#define REG_WOL_FRAME3_BYTE_MASK3 (0x6A) /* WF3BM3 */
/*
* Transmit/Receive Control Registers
* (Offset 0x70 - 0x9F)
*/
/* Transmit Frame Header */
#define REG_QDR_DUMMY (0x00) /* Dummy address to access QMU RxQ, TxQ */
#define TX_CTRL_INTERRUPT_ON (0x8000) /* Transmit Interrupt on Completion */
#define REG_TX_CTRL (0x70) /* TXCR */
#define TX_CTRL_ICMP_CHECKSUM (0x0100) /* Enable ICMP frame checksum generation */
#define TX_CTRL_UDP_CHECKSUM (0x0080) /* Enable UDP frame checksum generation */
#define TX_CTRL_TCP_CHECKSUM (0x0040) /* Enable TCP frame checksum generation */
#define TX_CTRL_IP_CHECKSUM (0x0020) /* Enable IP frame checksum generation */
#define TX_CTRL_FLUSH_QUEUE (0x0010) /* Clear transmit queue, reset tx frame pointer */
#define TX_CTRL_FLOW_ENABLE (0x0008) /* Enable transmit flow control */
#define TX_CTRL_PAD_ENABLE (0x0004) /* Eanble adding a padding to a packet shorter than 64 bytes */
#define TX_CTRL_CRC_ENABLE (0x0002) /* Enable adding a CRC to the end of transmit frame */
#define TX_CTRL_ENABLE (0x0001) /* Enable tranmsit */
#define REG_TX_STATUS (0x72) /* TXSR */
#define TX_STAT_LATE_COL (0x2000) /* Tranmsit late collision occurs */
#define TX_STAT_MAX_COL (0x1000) /* Tranmsit maximum collision is reached */
#define TX_FRAME_ID_MASK (0x003F) /* Transmit frame ID mask */
#define TX_STAT_ERRORS ( TX_STAT_MAX_COL | TX_STAT_LATE_COL )
#define REG_RX_CTRL1 (0x74) /* RXCR1 */
#define RX_CTRL_FLUSH_QUEUE (0x8000) /* Clear receive queue, reset rx frame pointer */
#define RX_CTRL_UDP_CHECKSUM (0x4000) /* Enable UDP frame checksum verification */
#define RX_CTRL_TCP_CHECKSUM (0x2000) /* Enable TCP frame checksum verification */
#define RX_CTRL_IP_CHECKSUM (0x1000) /* Enable IP frame checksum verification */
#define RX_CTRL_MAC_FILTER (0x0800) /* Receive with address that pass MAC address filtering */
#define RX_CTRL_FLOW_ENABLE (0x0400) /* Enable receive flow control */
#define RX_CTRL_BAD_PACKET (0x0200) /* Eanble receive CRC error frames */
#define RX_CTRL_MULTICAST (0x0100) /* Receive multicast frames that pass the CRC hash filtering */
#define RX_CTRL_BROADCAST (0x0080) /* Receive all the broadcast frames */
#define RX_CTRL_ALL_MULTICAST (0x0040) /* Receive all the multicast frames (including broadcast frames) */
#define RX_CTRL_UNICAST (0x0020) /* Receive unicast frames that match the device MAC address */
#define RX_CTRL_PROMISCUOUS (0x0010) /* Receive all incoming frames, regardless of frame's DA */
#define RX_CTRL_STRIP_CRC (0x0008) /* Enable strip CRC on the received frames */
#define RX_CTRL_INVERSE_FILTER (0x0002) /* Receive with address check in inverse filtering mode */
#define RX_CTRL_ENABLE (0x0001) /* Enable receive */
/* Address filtering scheme mask */
#define RX_CTRL_FILTER_MASK ( RX_CTRL_INVERSE_FILTER | RX_CTRL_PROMISCUOUS | RX_CTRL_MULTICAST | RX_CTRL_MAC_FILTER )
#define REG_RX_CTRL2 (0x76) /* RXCR2 */
#define RX_CTRL_IPV6_UDP_NOCHECKSUM (0x0010) /* No checksum generation and verification if IPv6 UDP is fragment */
#define RX_CTRL_IPV6_UDP_CHECKSUM (0x0008) /* Receive pass IPv6 UDP frame with UDP checksum is zero */
#define RX_CTRL_UDP_LITE_CHECKSUM (0x0004) /* Enable UDP Lite frame checksum generation and verification */
#define RX_CTRL_ICMP_CHECKSUM (0x0002) /* Enable ICMP frame checksum verification */
#define RX_CTRL_BLOCK_MAC (0x0001) /* Receive drop frame if the SA is same as device MAC address */
#define RX_CTRL_BURST_LEN_MASK (0x00e0) /* SRDBL SPI Receive Data Burst Length */
#define RX_CTRL_BURST_LEN_4 (0x0000)
#define RX_CTRL_BURST_LEN_8 (0x0020)
#define RX_CTRL_BURST_LEN_16 (0x0040)
#define RX_CTRL_BURST_LEN_32 (0x0060)
#define RX_CTRL_BURST_LEN_FRAME (0x0080)
#define REG_TX_MEM_INFO (0x78) /* TXMIR */
#define TX_MEM_AVAILABLE_MASK (0x1FFF) /* The amount of memory available in TXQ */
#define REG_RX_FHR_STATUS (0x7C) /* RXFHSR */
#define RX_VALID (0x8000) /* Frame in the receive packet memory is valid */
#define RX_ICMP_ERROR (0x2000) /* ICMP checksum field doesn't match */
#define RX_IP_ERROR (0x1000) /* IP checksum field doesn't match */
#define RX_TCP_ERROR (0x0800) /* TCP checksum field doesn't match */
#define RX_UDP_ERROR (0x0400) /* UDP checksum field doesn't match */
#define RX_BROADCAST (0x0080) /* Received frame is a broadcast frame */
#define RX_MULTICAST (0x0040) /* Received frame is a multicast frame */
#define RX_UNICAST (0x0020) /* Received frame is a unicast frame */
#define RX_PHY_ERROR (0x0010) /* Received frame has runt error */
#define RX_FRAME_ETHER (0x0008) /* Received frame is an Ethernet-type frame */
#define RX_TOO_LONG (0x0004) /* Received frame length exceeds max size 0f 2048 bytes */
#define RX_RUNT_ERROR (0x0002) /* Received frame was demaged by a collision */
#define RX_BAD_CRC (0x0001) /* Received frame has a CRC error */
#define RX_ERRORS ( RX_BAD_CRC | RX_TOO_LONG | RX_RUNT_ERROR | RX_PHY_ERROR | \
RX_ICMP_ERROR | RX_IP_ERROR | RX_TCP_ERROR | RX_UDP_ERROR )
#define REG_RX_FHR_BYTE_CNT (0x7E) /* RXFHBCR */
#define RX_BYTE_CNT_MASK (0x0FFF) /* Received frame byte size mask */
#define REG_TXQ_CMD (0x80) /* TXQCR */
#define TXQ_AUTO_ENQUEUE (0x0004) /* Enable enqueue tx frames from tx buffer automatically */
#define TXQ_MEM_AVAILABLE_INT (0x0002) /* Enable generate interrupt when tx memory is available */
#define TXQ_ENQUEUE (0x0001) /* Enable enqueue tx frames one frame at a time */
#define REG_RXQ_CMD (0x82) /* RXQCR */
#define RXQ_STAT_TIME_INT (0x1000) /* RX interrupt is occured by timer duration */
#define RXQ_STAT_BYTE_CNT_INT (0x0800) /* RX interrupt is occured by byte count threshold */
#define RXQ_STAT_FRAME_CNT_INT (0x0400) /* RX interrupt is occured by frame count threshold */
#define RXQ_TWOBYTE_OFFSET (0x0200) /* Enable adding 2-byte before frame header for IP aligned with DWORD */
#define RXQ_TIME_INT (0x0080) /* Enable RX interrupt by timer duration */
#define RXQ_BYTE_CNT_INT (0x0040) /* Enable RX interrupt by byte count threshold */
#define RXQ_FRAME_CNT_INT (0x0020) /* Enable RX interrupt by frame count threshold */
#define RXQ_AUTO_DEQUEUE (0x0010) /* Enable release rx frames from rx buffer automatically */
#define RXQ_START (0x0008) /* Start QMU transfer operation */
#define RXQ_CMD_FREE_PACKET (0x0001) /* Manual dequeue (release the current frame from RxQ) */
#define RXQ_CMD_CNTL (RXQ_FRAME_CNT_INT|RXQ_AUTO_DEQUEUE)
#define REG_TX_ADDR_PTR (0x84) /* TXFDPR */
#define REG_RX_ADDR_PTR (0x86) /* RXFDPR */
#define ADDR_PTR_AUTO_INC (0x4000) /* Enable Frame data pointer increments automatically */
#define ADDR_PTR_MASK (0x03ff) /* Address pointer mask */
#define REG_RX_TIME_THRES (0x8C) /* RXDTTR */
#define RX_TIME_THRESHOLD_MASK (0xFFFF) /* Set receive timer duration threshold */
#define REG_RX_BYTE_CNT_THRES (0x8E) /* RXDBCTR */
#define RX_BYTE_THRESHOLD_MASK (0xFFFF) /* Set receive byte count threshold */
#define REG_INT_MASK (0x90) /* IER */
#define INT_PHY (0x8000) /* Enable link change interrupt */
#define INT_TX (0x4000) /* Enable transmit done interrupt */
#define INT_RX (0x2000) /* Enable receive interrupt */
#define INT_RX_OVERRUN (0x0800) /* Enable receive overrun interrupt */
#define INT_TX_STOPPED (0x0200) /* Enable transmit process stopped interrupt */
#define INT_RX_STOPPED (0x0100) /* Enable receive process stopped interrupt */
#define INT_TX_SPACE (0x0040) /* Enable transmit space available interrupt */
#define INT_RX_WOL_FRAME (0x0020) /* Enable WOL on receive wake-up frame detect interrupt */
#define INT_RX_WOL_MAGIC (0x0010) /* Enable WOL on receive magic packet detect interrupt */
#define INT_RX_WOL_LINKUP (0x0008) /* Enable WOL on link up detect interrupt */
#define INT_RX_WOL_ENERGY (0x0004) /* Enable WOL on energy detect interrupt */
#define INT_RX_SPI_ERROR (0x0002) /* Enable receive SPI bus error interrupt */
#define INT_RX_WOL_DELAY_ENERGY (0x0001) /* Enable WOL on delay energy detect interrupt */
#define INT_MASK ( INT_RX | INT_TX | INT_PHY )
#define REG_INT_STATUS (0x92) /* ISR */
#define REG_RX_FRAME_CNT_THRES (0x9C) /* RXFCTFC */
#define RX_FRAME_CNT_MASK (0xFF00) /* Received frame count mask */
#define RX_FRAME_THRESHOLD_MASK (0x00FF) /* Set receive frame count threshold mask */
#define REG_TX_TOTAL_FRAME_SIZE (0x9E) /* TXNTFSR */
#define TX_TOTAL_FRAME_SIZE_MASK (0xFFFF) /* Set next total tx frame size mask */
/*
* MAC Address Hash Table Control Registers
* (Offset 0xA0 - 0xA7)
*/
#define REG_MAC_HASH_0 (0xA0) /* MAHTR0 */
#define REG_MAC_HASH_1 (0xA1)
#define REG_MAC_HASH_2 (0xA2) /* MAHTR1 */
#define REG_MAC_HASH_3 (0xA3)
#define REG_MAC_HASH_4 (0xA4) /* MAHTR2 */
#define REG_MAC_HASH_5 (0xA5)
#define REG_MAC_HASH_6 (0xA6) /* MAHTR3 */
#define REG_MAC_HASH_7 (0xA7)
/*
* QMU Receive Queue Watermark Control Registers
* (Offset 0xB0 - 0xB5)
*/
#define REG_RX_LOW_WATERMARK (0xB0) /* FCLWR */
#define RX_LOW_WATERMARK_MASK (0x0FFF) /* Set QMU RxQ low watermark mask */
#define REG_RX_HIGH_WATERMARK (0xB2) /* FCHWR */
#define RX_HIGH_WATERMARK_MASK (0x0FFF) /* Set QMU RxQ high watermark mask */
#define REG_RX_OVERRUN_WATERMARK (0xB4) /* FCOWR */
#define RX_OVERRUN_WATERMARK_MASK (0x0FFF) /* Set QMU RxQ overrun watermark mask */
/*
* Global Control Registers
* (Offset 0xC0 - 0xD3)
*/
#define REG_CHIP_ID (0xC0) /* CIDER */
#define CHIP_ID_MASK (0xFFF0) /* Family ID and chip ID mask */
#define REVISION_MASK (0x000E) /* Chip revision mask */
#define CHIP_ID_SHIFT (4)
#define REVISION_SHIFT (1)
#define CHIP_ID_8851_16 (0x8870) /* KS8851-16/32MQL chip ID */
#define REG_LED_CTRL (0xC6) /* CGCR */
#define LED_CTRL_SEL1 (0x8000) /* Select LED3/LED2/LED1/LED0 indication */
#define LED_CTRL_SEL0 (0x0200) /* Select LED3/LED2/LED1/LED0 indication */
#define REG_IND_IACR (0xC8) /* IACR */
#define TABLE_READ (0x1000) /* Indirect read */
#define TABLE_MIB (0x0C00) /* Select MIB counter table */
#define TABLE_ENTRY_MASK (0x001F) /* Set table entry to access */
#define REG_IND_DATA_LOW (0xD0) /* IADLR */
#define REG_IND_DATA_HIGH (0xD2) /* IADHR */
/*
* Power Management Control Registers
* (Offset 0xD4 - 0xD7)
*/
#define REG_POWER_CNTL (0xD4) /* PMECR */
#define PME_DELAY_ENABLE (0x4000) /* Enable the PME output pin assertion delay */
#define PME_ACTIVE_HIGHT (0x1000) /* PME output pin is active high */
#define PME_FROM_WKFRAME (0x0800) /* PME asserted when wake-up frame is detected */
#define PME_FROM_MAGIC (0x0400) /* PME asserted when magic packet is detected */
#define PME_FROM_LINKUP (0x0200) /* PME asserted when link up is detected */
#define PME_FROM_ENERGY (0x0100) /* PME asserted when energy is detected */
#define PME_EVENT_MASK (0x0F00) /* PME asserted event mask */
#define WAKEUP_AUTO_ENABLE (0x0080) /* Enable auto wake-up in energy mode */
#define WAKEUP_NORMAL_AUTO_ENABLE (0x0040) /* Enable auto goto normal mode from energy detecion mode */
#define WAKEUP_FROM_WKFRAME (0x0020) /* Wake-up from wake-up frame event detected */
#define WAKEUP_FROM_MAGIC (0x0010) /* Wake-up from magic packet event detected */
#define WAKEUP_FROM_LINKUP (0x0008) /* Wake-up from link up event detected */
#define WAKEUP_FROM_ENERGY (0x0004) /* Wake-up from energy event detected */
#define WAKEUP_EVENT_MASK (0x003C) /* Wake-up event mask */
#define POWER_STATE_D1 (0x0003) /* Power saving mode */
#define POWER_STATE_D3 (0x0002) /* Power down mode */
#define POWER_STATE_D2 (0x0001) /* Power detection mode */
#define POWER_STATE_D0 (0x0000) /* Normal operation mode (default) */
#define POWER_STATE_MASK (0x0003) /* Power management mode mask */
#define REG_WAKEUP_TIME (0xD6) /* GSWUTR */
#define WAKEUP_TIME (0xFF00) /* Min time (sec) wake-uo after detected energy */
#define GOSLEEP_TIME (0x00FF) /* Min time (sec) before goto sleep when in energy mode */
/*
* PHY Control Registers
* (Offset 0xD8 - 0xF9)
*/
#define REG_PHY_RESET (0xD8) /* PHYRR */
#define PHY_RESET (0x0001) /* Reset PHY */
#define REG_PHY_CNTL (0xE4) /* P1MBCR */
#define PHY_SPEED_100MBIT (0x2000) /* Force PHY 100Mbps */
#define PHY_AUTO_NEG_ENABLE (0x1000) /* Enable PHY auto-negotiation */
#define PHY_POWER_DOWN (0x0800) /* Set PHY power-down */
#define PHY_AUTO_NEG_RESTART (0x0200) /* Restart PHY auto-negotiation */
#define PHY_FULL_DUPLEX (0x0100) /* Force PHY in full duplex mode */
#define PHY_HP_MDIX (0x0020) /* Set PHY in HP auto MDI-X mode */
#define PHY_FORCE_MDIX (0x0010) /* Force MDI-X */
#define PHY_AUTO_MDIX_DISABLE (0x0008) /* Disable auto MDI-X */
#define PHY_TRANSMIT_DISABLE (0x0002) /* Disable PHY transmit */
#define PHY_LED_DISABLE (0x0001) /* Disable PHY LED */
#define REG_PHY_STATUS (0xE6) /* P1MBSR */
#define PHY_100BT4_CAPABLE (0x8000) /* 100 BASE-T4 capable */
#define PHY_100BTX_FD_CAPABLE (0x4000) /* 100BASE-TX full duplex capable */
#define PHY_100BTX_CAPABLE (0x2000) /* 100BASE-TX half duplex capable */
#define PHY_10BT_FD_CAPABLE (0x1000) /* 10BASE-TX full duplex capable */
#define PHY_10BT_CAPABLE (0x0800) /* 10BASE-TX half duplex capable */
#define PHY_AUTO_NEG_ACKNOWLEDGE (0x0020) /* Auto-negotiation complete */
#define PHY_AUTO_NEG_CAPABLE (0x0008) /* Auto-negotiation capable */
#define PHY_LINK_UP (0x0004) /* PHY link is up */
#define PHY_EXTENDED_CAPABILITY (0x0001) /* PHY extended register capable */
#define REG_PHY_ID_LOW (0xE8) /* PHY1ILR */
#define REG_PHY_ID_HIGH (0xEA) /* PHY1IHR */
#define REG_PHY_AUTO_NEGOTIATION (0xEC) /* P1ANAR */
#define PHY_AUTO_NEG_SYM_PAUSE (0x0400) /* Advertise pause capability */
#define PHY_AUTO_NEG_100BTX_FD (0x0100) /* Advertise 100 full-duplex capability */
#define PHY_AUTO_NEG_100BTX (0x0080) /* Advertise 100 half-duplex capability */
#define PHY_AUTO_NEG_10BT_FD (0x0040) /* Advertise 10 full-duplex capability */
#define PHY_AUTO_NEG_10BT (0x0020) /* Advertise 10 half-duplex capability */
#define PHY_AUTO_NEG_SELECTOR (0x001F) /* Selector field mask */
#define PHY_AUTO_NEG_802_3 (0x0001) /* 802.3 */
#define REG_PHY_REMOTE_CAPABILITY (0xEE) /* P1ANLPR */
#define PHY_REMOTE_SYM_PAUSE (0x0400) /* Link partner pause capability */
#define PHY_REMOTE_100BTX_FD (0x0100) /* Link partner 100 full-duplex capability */
#define PHY_REMOTE_100BTX (0x0080) /* Link partner 100 half-duplex capability */
#define PHY_REMOTE_10BT_FD (0x0040) /* Link partner 10 full-duplex capability */
#define PHY_REMOTE_10BT (0x0020) /* Link partner 10 half-duplex capability */
#define REG_PORT_LINK_MD (0xF4) /* P1SCLMD */
#define PORT_CABLE_10M_SHORT (0x8000) /* Cable length is less than 10m short */
#define PORT_CABLE_STAT_FAILED (0x6000) /* Cable diagnostic test fail */
#define PORT_CABLE_STAT_SHORT (0x4000) /* Short condition detected in the cable */
#define PORT_CABLE_STAT_OPEN (0x2000) /* Open condition detected in the cable */
#define PORT_CABLE_STAT_NORMAL (0x0000) /* Normal condition */
#define PORT_CABLE_DIAG_RESULT (0x6000) /* Cable diagnostic test result mask */
#define PORT_START_CABLE_DIAG (0x1000) /* Enable cable diagnostic test */
#define PORT_FORCE_LINK (0x0800) /* Enable force link pass */
#define PORT_POWER_SAVING (0x0400) /* Disable power saving */
#define PORT_REMOTE_LOOPBACK (0x0200) /* Enable remote loopback at PHY */
#define PORT_CABLE_FAULT_COUNTER (0x01FF) /* Cable length distance to the fault */
#define REG_PORT_CTRL (0xF6) /* P1CR */
#define PORT_LED_OFF (0x8000) /* Turn off all the port LEDs (LED3/LED2/LED1/LED0) */
#define PORT_TX_DISABLE (0x4000) /* Disable port transmit */
#define PORT_AUTO_NEG_RESTART (0x2000) /* Restart auto-negotiation */
#define PORT_POWER_DOWN (0x0800) /* Set port power-down */
#define PORT_AUTO_MDIX_DISABLE (0x0400) /* Disable auto MDI-X */
#define PORT_FORCE_MDIX (0x0200) /* Force MDI-X */
#define PORT_AUTO_NEG_ENABLE (0x0080) /* Enable auto-negotiation */
#define PORT_FORCE_100_MBIT (0x0040) /* Force PHY 100Mbps */
#define PORT_FORCE_FULL_DUPLEX (0x0020) /* Force PHY in full duplex mode */
#define PORT_AUTO_NEG_SYM_PAUSE (0x0010) /* Advertise pause capability */
#define PORT_AUTO_NEG_100BTX_FD (0x0008) /* Advertise 100 full-duplex capability */
#define PORT_AUTO_NEG_100BTX (0x0004) /* Advertise 100 half-duplex capability */
#define PORT_AUTO_NEG_10BT_FD (0x0002) /* Advertise 10 full-duplex capability */
#define PORT_AUTO_NEG_10BT (0x0001) /* Advertise 10 half-duplex capability */
#define REG_PORT_STATUS (0xF8) /* P1SR */
#define PORT_HP_MDIX (0x8000) /* Set PHY in HP auto MDI-X mode */
#define PORT_REVERSED_POLARITY (0x2000) /* Polarity is reversed */
#define PORT_RX_FLOW_CTRL (0x1000) /* Reeive flow control feature is active */
#define PORT_TX_FLOW_CTRL (0x0800) /* Transmit flow control feature is active */
#define PORT_STAT_SPEED_100MBIT (0x0400) /* Link is 100Mbps */
#define PORT_STAT_FULL_DUPLEX (0x0200) /* Link is full duplex mode */
#define PORT_MDIX_STATUS (0x0080) /* Is MDI */
#define PORT_AUTO_NEG_COMPLETE (0x0040) /* Auto-negotiation complete */
#define PORT_STATUS_LINK_GOOD (0x0020) /* PHY link is up */
#define PORT_REMOTE_SYM_PAUSE (0x0010) /* Link partner pause capability */
#define PORT_REMOTE_100BTX_FD (0x0008) /* Link partner 100 full-duplex capability */
#define PORT_REMOTE_100BTX (0x0004) /* Link partner 100 half-duplex capability */
#define PORT_REMOTE_10BT_FD (0x0002) /* Link partner 10 full-duplex capability */
#define PORT_REMOTE_10BT (0x0001) /* Link partner 10 half-duplex capability */
#endif /* KSZ8851SNL_REG_H_INCLUDED */

View file

@ -72,7 +72,7 @@
#include "FreeRTOS_server_private.h"
/* Remove the entire file if TCP is not being used. */
#if( ipconfigUSE_TCP == 1 )
#if( ipconfigUSE_TCP == 1 ) && ( ( ipconfigUSE_HTTP == 1 ) || ( ipconfigUSE_FTP == 1 ) )
#if !defined( ARRAY_SIZE )
#define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )
@ -238,8 +238,11 @@ const char *pcType = "Unknown";
pcType = "closed";
FreeRTOS_closesocket( xNexSocket );
}
FreeRTOS_printf( ( "TPC-server: new %s client\n", pcType ) );
{
struct freertos_sockaddr xRemoteAddress;
FreeRTOS_GetRemoteAddress( pxClient->xSocket, &xRemoteAddress );
FreeRTOS_printf( ( "TPC-server: new %s client %xip\n", pcType, (unsigned)FreeRTOS_ntohl( xRemoteAddress.sin_addr ) ) );
}
/* Remove compiler warnings in case FreeRTOS_printf() is not used. */
( void ) pcType;
@ -379,4 +382,4 @@ BaseType_t xLength = strlen( pcDir );
#endif /* ipconfigSUPPORT_SIGNALS */
/*-----------------------------------------------------------*/
#endif /* ipconfigUSE_TCP != 1 */
#endif /* ( ipconfigUSE_TCP == 1 ) && ( ( ipconfigUSE_HTTP == 1 ) || ( ipconfigUSE_FTP == 1 ) ) */

View file

@ -72,6 +72,9 @@
#include "FreeRTOS_TCP_server.h"
#include "FreeRTOS_server_private.h"
/* Remove the whole file if HTTP is not supported. */
#if( ipconfigUSE_HTTP == 1 )
/* FreeRTOS+FAT includes. */
#include "ff_stdio.h"
@ -453,3 +456,5 @@ static const char *pcGetContentsType (const char *apFname)
return pcResult;
}
#endif /* ipconfigUSE_HTTP */