commit 7ee6dc7527ad6f8cc1e965db1867bfeb55f9035b Author: abazlaev Date: Fri Jan 20 18:21:51 2023 +0700 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..27ce6c7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Listings +Objects diff --git a/EventRecorderStub.scvd b/EventRecorderStub.scvd new file mode 100644 index 0000000..2956b29 --- /dev/null +++ b/EventRecorderStub.scvd @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/FreeRTOS/source/FreeRTOS_ARP.c b/FreeRTOS/source/FreeRTOS_ARP.c new file mode 100644 index 0000000..20aff6b --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_ARP.c @@ -0,0 +1,1226 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_ARP.c + * @brief Implements the Address Resolution Protocol for the FreeRTOS+TCP network stack. + */ + +/* Standard includes. */ +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Timers.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#if ( ipconfigUSE_LLMNR == 1 ) + #include "FreeRTOS_DNS.h" +#endif /* ipconfigUSE_LLMNR */ +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +/** @brief When the age of an entry in the ARP table reaches this value (it counts down + * to zero, so this is an old entry) an ARP request will be sent to see if the + * entry is still valid and can therefore be refreshed. */ +#define arpMAX_ARP_AGE_BEFORE_NEW_ARP_REQUEST ( 3 ) + +/** @brief The time between gratuitous ARPs. */ +#ifndef arpGRATUITOUS_ARP_PERIOD + #define arpGRATUITOUS_ARP_PERIOD ( pdMS_TO_TICKS( 20000U ) ) +#endif + +/** @brief When there is another device which has the same IP address as the IP address + * of this device, a defensive ARP request should be sent out. However, according to + * RFC 5227 section 1.1, there must be a minimum interval of 10 seconds between + * consecutive defensive ARP packets. */ +#ifndef arpIP_CLASH_RESET_TIMEOUT_MS + #define arpIP_CLASH_RESET_TIMEOUT_MS 10000U +#endif + +/** @brief Maximum number of defensive ARPs to be sent for an ARP clash per + * arpIP_CLASH_RESET_TIMEOUT_MS period. The retries are limited to one as outlined + * by RFC 5227 section 2.4 part b.*/ +#ifndef arpIP_CLASH_MAX_RETRIES + #define arpIP_CLASH_MAX_RETRIES 1U +#endif + +/* + * Lookup an MAC address in the ARP cache from the IP address. + */ +static eARPLookupResult_t prvCacheLookup( uint32_t ulAddressToLookup, + MACAddress_t * const pxMACAddress ); + +/*-----------------------------------------------------------*/ + +static void vProcessARPPacketReply( const ARPPacket_t * pxARPFrame, + uint32_t ulSenderProtocolAddress ); + +/*-----------------------------------------------------------*/ + +/** @brief The ARP cache. */ +_static ARPCacheRow_t xARPCache[ ipconfigARP_CACHE_ENTRIES ]; + +/** @brief The time at which the last gratuitous ARP was sent. Gratuitous ARPs are used + * to ensure ARP tables are up to date and to detect IP address conflicts. */ +static TickType_t xLastGratuitousARPTime = 0U; + +/* + * IP-clash detection is currently only used internally. When DHCP doesn't respond, the + * driver can try out a random LinkLayer IP address (169.254.x.x). It will send out a + * gratuitous ARP message and, after a period of time, check the variables here below: + */ +#if ( ipconfigARP_USE_CLASH_DETECTION != 0 ) + /* Becomes non-zero if another device responded to a gratuitous ARP message. */ + BaseType_t xARPHadIPClash; + /* MAC-address of the other device containing the same IP-address. */ + MACAddress_t xARPClashMacAddress; +#endif /* ipconfigARP_USE_CLASH_DETECTION */ + +/*-----------------------------------------------------------*/ + +/** + * @brief Process the ARP packets. + * + * @param[in] pxARPFrame: The ARP Frame (the ARP packet). + * + * @return An enum which says whether to return the frame or to release it. + */ +eFrameProcessingResult_t eARPProcessPacket( ARPPacket_t * const pxARPFrame ) +{ + eFrameProcessingResult_t eReturn = eReleaseBuffer; + ARPHeader_t * pxARPHeader; + uint32_t ulTargetProtocolAddress, ulSenderProtocolAddress; +/* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + /* Next defensive request must not be sent for arpIP_CLASH_RESET_TIMEOUT_MS + * period. */ + static TickType_t uxARPClashTimeoutPeriod = pdMS_TO_TICKS( arpIP_CLASH_RESET_TIMEOUT_MS ); + + /* This local variable is used to keep track of number of ARP requests sent and + * also to limit the requests to arpIP_CLASH_MAX_RETRIES per arpIP_CLASH_RESET_TIMEOUT_MS + * period. */ + static UBaseType_t uxARPClashCounter = 0U; + /* The time at which the last ARP clash was sent. */ + static TimeOut_t xARPClashTimeOut; + + pxARPHeader = &( pxARPFrame->xARPHeader ); + + /* The field ucSenderProtocolAddress is badly aligned, copy byte-by-byte. */ + + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = pxARPHeader->ucSenderProtocolAddress; + pvCopyDest = &ulSenderProtocolAddress; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( ulSenderProtocolAddress ) ); + /* The field ulTargetProtocolAddress is well-aligned, a 32-bits copy. */ + ulTargetProtocolAddress = pxARPHeader->ulTargetProtocolAddress; + + if( uxARPClashCounter != 0U ) + { + /* Has the timeout been reached? */ + if( xTaskCheckForTimeOut( &xARPClashTimeOut, &uxARPClashTimeoutPeriod ) == pdTRUE ) + { + /* We have waited long enough, reset the counter. */ + uxARPClashCounter = 0; + } + } + + /* Introduce a do while loop to allow use of breaks. */ + do + { + /* Only Ethernet hardware type is supported. + * Only IPv4 address can be present in the ARP packet. + * The hardware length (the MAC address) must be 6 bytes. And, + * The Protocol address length must be 4 bytes as it is IPv4. */ + if( ( pxARPHeader->usHardwareType != ipARP_HARDWARE_TYPE_ETHERNET ) || + ( pxARPHeader->usProtocolType != ipARP_PROTOCOL_TYPE ) || + ( pxARPHeader->ucHardwareAddressLength != ipMAC_ADDRESS_LENGTH_BYTES ) || + ( pxARPHeader->ucProtocolAddressLength != ipIP_ADDRESS_LENGTH_BYTES ) ) + { + /* One or more fields are not valid. */ + iptraceDROPPED_INVALID_ARP_PACKET( pxARPHeader ); + break; + } + + /* Check whether the lowest bit of the highest byte is 1 to check for + * multicast address or even a broadcast address (FF:FF:FF:FF:FF:FF). */ + if( ( pxARPHeader->xSenderHardwareAddress.ucBytes[ 0 ] & 0x01U ) == 0x01U ) + { + /* Senders address is a multicast OR broadcast address which is not + * allowed for an ARP packet. Drop the packet. See RFC 1812 section + * 3.3.2. */ + iptraceDROPPED_INVALID_ARP_PACKET( pxARPHeader ); + break; + } + + uint32_t ulHostEndianProtocolAddr = FreeRTOS_ntohl( ulSenderProtocolAddress ); + + if( ( ipFIRST_LOOPBACK_IPv4 <= ulHostEndianProtocolAddr ) && + ( ulHostEndianProtocolAddr < ipLAST_LOOPBACK_IPv4 ) ) + { + /* The local loopback addresses must never appear outside a host. See RFC 1122 + * section 3.2.1.3. */ + iptraceDROPPED_INVALID_ARP_PACKET( pxARPHeader ); + break; + } + + /* Check whether there is a clash with another device for this IP address. */ + if( ( ulSenderProtocolAddress == *ipLOCAL_IP_ADDRESS_POINTER ) && + ( *ipLOCAL_IP_ADDRESS_POINTER != 0UL ) ) + { + if( uxARPClashCounter < arpIP_CLASH_MAX_RETRIES ) + { + /* Increment the counter. */ + uxARPClashCounter++; + + /* Send out a defensive ARP request. */ + FreeRTOS_OutputARPRequest( *ipLOCAL_IP_ADDRESS_POINTER ); + + /* Since an ARP Request for this IP was just sent, do not send a gratuitous + * ARP for arpGRATUITOUS_ARP_PERIOD. */ + xLastGratuitousARPTime = xTaskGetTickCount(); + + /* Note the time at which this request was sent. */ + vTaskSetTimeOutState( &xARPClashTimeOut ); + + /* Reset the time-out period to the given value. */ + uxARPClashTimeoutPeriod = pdMS_TO_TICKS( arpIP_CLASH_RESET_TIMEOUT_MS ); + } + + /* Process received ARP frame to see if there is a clash. */ + #if ( ipconfigARP_USE_CLASH_DETECTION != 0 ) + { + xARPHadIPClash = pdTRUE; + /* Remember the MAC-address of the other device which has the same IP-address. */ + ( void ) memcpy( xARPClashMacAddress.ucBytes, pxARPHeader->xSenderHardwareAddress.ucBytes, sizeof( xARPClashMacAddress.ucBytes ) ); + } + #endif /* ipconfigARP_USE_CLASH_DETECTION */ + + break; + } + + traceARP_PACKET_RECEIVED(); + + /* Don't do anything if the local IP address is zero because + * that means a DHCP request has not completed. */ + if( *ipLOCAL_IP_ADDRESS_POINTER != 0U ) + { + switch( pxARPHeader->usOperation ) + { + case ipARP_REQUEST: + + /* The packet contained an ARP request. Was it for the IP + * address of the node running this code? And does the MAC + * address claim that it is coming from this device itself? */ + if( ( ulTargetProtocolAddress == *ipLOCAL_IP_ADDRESS_POINTER ) && + ( memcmp( ( void * ) ipLOCAL_MAC_ADDRESS, + ( void * ) ( pxARPHeader->xSenderHardwareAddress.ucBytes ), + ipMAC_ADDRESS_LENGTH_BYTES ) != 0 ) ) + { + iptraceSENDING_ARP_REPLY( ulSenderProtocolAddress ); + + /* The request is for the address of this node. Add the + * entry into the ARP cache, or refresh the entry if it + * already exists. */ + vARPRefreshCacheEntry( &( pxARPHeader->xSenderHardwareAddress ), ulSenderProtocolAddress ); + + /* Generate a reply payload in the same buffer. */ + pxARPHeader->usOperation = ( uint16_t ) ipARP_REPLY; + + ( void ) memcpy( &( pxARPHeader->xTargetHardwareAddress ), + &( pxARPHeader->xSenderHardwareAddress ), + sizeof( MACAddress_t ) ); + + pxARPHeader->ulTargetProtocolAddress = ulSenderProtocolAddress; + + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = ipLOCAL_MAC_ADDRESS; + pvCopyDest = pxARPHeader->xSenderHardwareAddress.ucBytes; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( MACAddress_t ) ); + + pvCopySource = ipLOCAL_IP_ADDRESS_POINTER; + pvCopyDest = pxARPHeader->ucSenderProtocolAddress; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxARPHeader->ucSenderProtocolAddress ) ); + + eReturn = eReturnEthernetFrame; + } + + break; + + case ipARP_REPLY: + vProcessARPPacketReply( pxARPFrame, ulSenderProtocolAddress ); + + break; + + default: + /* Invalid. */ + break; + } + } + } while( ipFALSE_BOOL ); + + return eReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief A device has sent an ARP reply, process it. + * @param[in] pxARPFrame: The ARP packet received. + * @param[in] ulSenderProtocolAddress: The IPv4 address involved. + */ +static void vProcessARPPacketReply( const ARPPacket_t * pxARPFrame, + uint32_t ulSenderProtocolAddress ) +{ + const ARPHeader_t * pxARPHeader = &( pxARPFrame->xARPHeader ); + uint32_t ulTargetProtocolAddress = pxARPHeader->ulTargetProtocolAddress; + + /* If the packet is meant for this device or if the entry already exists. */ + if( ( ulTargetProtocolAddress == *ipLOCAL_IP_ADDRESS_POINTER ) || + ( xIsIPInARPCache( ulSenderProtocolAddress ) == pdTRUE ) ) + { + iptracePROCESSING_RECEIVED_ARP_REPLY( ulTargetProtocolAddress ); + vARPRefreshCacheEntry( &( pxARPHeader->xSenderHardwareAddress ), ulSenderProtocolAddress ); + } + + if( pxARPWaitingNetworkBuffer != NULL ) + { + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const IPPacket_t * pxARPWaitingIPPacket = ( ( IPPacket_t * ) pxARPWaitingNetworkBuffer->pucEthernetBuffer ); + const IPHeader_t * pxARPWaitingIPHeader = &( pxARPWaitingIPPacket->xIPHeader ); + + if( ulSenderProtocolAddress == pxARPWaitingIPHeader->ulSourceIPAddress ) + { + IPStackEvent_t xEventMessage; + const TickType_t xDontBlock = ( TickType_t ) 0; + + xEventMessage.eEventType = eNetworkRxEvent; + xEventMessage.pvData = ( void * ) pxARPWaitingNetworkBuffer; + + if( xSendEventStructToIPTask( &xEventMessage, xDontBlock ) != pdPASS ) + { + /* Failed to send the message, so release the network buffer. */ + vReleaseNetworkBufferAndDescriptor( pxARPWaitingNetworkBuffer ); + } + + /* Clear the buffer. */ + pxARPWaitingNetworkBuffer = NULL; + + /* Found an ARP resolution, disable ARP resolution timer. */ + vIPSetARPResolutionTimerEnableState( pdFALSE ); + + iptrace_DELAYED_ARP_REQUEST_REPLIED(); + } + } +} + +/** + * @brief Check whether an IP address is in the ARP cache. + * + * @param[in] ulAddressToLookup: The 32-bit representation of an IP address to + * check for. + * + * @return When the IP-address is found: pdTRUE, else pdFALSE. + */ +BaseType_t xIsIPInARPCache( uint32_t ulAddressToLookup ) +{ + BaseType_t x, xReturn = pdFALSE; + + /* Loop through each entry in the ARP cache. */ + for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) + { + /* Does this row in the ARP cache table hold an entry for the IP address + * being queried? */ + if( xARPCache[ x ].ulIPAddress == ulAddressToLookup ) + { + xReturn = pdTRUE; + + /* A matching valid entry was found. */ + if( xARPCache[ x ].ucValid == ( uint8_t ) pdFALSE ) + { + /* This entry is waiting an ARP reply, so is not valid. */ + xReturn = pdFALSE; + } + + break; + } + } + + return xReturn; +} + +/** + * @brief Check whether a packet needs ARP resolution if it is on local subnet. If required send an ARP request. + * + * @param[in] pxNetworkBuffer: The network buffer with the packet to be checked. + * + * @return pdTRUE if the packet needs ARP resolution, pdFALSE otherwise. + */ +BaseType_t xCheckRequiresARPResolution( const NetworkBufferDescriptor_t * pxNetworkBuffer ) +{ + BaseType_t xNeedsARPResolution = pdFALSE; + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const IPPacket_t * pxIPPacket = ( ( IPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); + const IPHeader_t * pxIPHeader = &( pxIPPacket->xIPHeader ); + + if( ( pxIPHeader->ulSourceIPAddress & xNetworkAddressing.ulNetMask ) == ( *ipLOCAL_IP_ADDRESS_POINTER & xNetworkAddressing.ulNetMask ) ) + { + /* If the IP is on the same subnet and we do not have an ARP entry already, + * then we should send out ARP for finding the MAC address. */ + if( xIsIPInARPCache( pxIPHeader->ulSourceIPAddress ) == pdFALSE ) + { + FreeRTOS_OutputARPRequest( pxIPHeader->ulSourceIPAddress ); + + /* This packet needs resolution since this is on the same subnet + * but not in the ARP cache. */ + xNeedsARPResolution = pdTRUE; + } + } + + return xNeedsARPResolution; +} + +#if ( ipconfigUSE_ARP_REMOVE_ENTRY != 0 ) + +/** + * @brief Remove an ARP cache entry that matches with .pxMACAddress. + * + * @param[in] pxMACAddress: Pointer to the MAC address whose entry shall + * be removed.. + * @return When the entry was found and remove: the IP-address, otherwise zero. + */ + uint32_t ulARPRemoveCacheEntryByMac( const MACAddress_t * pxMACAddress ) + { + BaseType_t x; + uint32_t lResult = 0; + + configASSERT( pxMACAddress != NULL ); + + /* For each entry in the ARP cache table. */ + for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) + { + if( ( memcmp( xARPCache[ x ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) ) == 0 ) ) + { + lResult = xARPCache[ x ].ulIPAddress; + ( void ) memset( &xARPCache[ x ], 0, sizeof( xARPCache[ x ] ) ); + break; + } + } + + return lResult; + } + +#endif /* ipconfigUSE_ARP_REMOVE_ENTRY != 0 */ +/*-----------------------------------------------------------*/ + +/** + * @brief Add/update the ARP cache entry MAC-address to IP-address mapping. + * + * @param[in] pxMACAddress: Pointer to the MAC address whose mapping is being + * updated. + * @param[in] ulIPAddress: 32-bit representation of the IP-address whose mapping + * is being updated. + */ +void vARPRefreshCacheEntry( const MACAddress_t * pxMACAddress, + const uint32_t ulIPAddress ) +{ + BaseType_t x = 0; + BaseType_t xIpEntry = -1; + BaseType_t xMacEntry = -1; + BaseType_t xUseEntry = 0; + BaseType_t xAllDone = pdFALSE; + uint8_t ucMinAgeFound = 0U; + + #if ( ipconfigARP_STORES_REMOTE_ADDRESSES == 0 ) + /* Only process the IP address if it is on the local network. */ + if( ( ulIPAddress & xNetworkAddressing.ulNetMask ) == ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) ) + #else + + /* If ipconfigARP_STORES_REMOTE_ADDRESSES is non-zero, IP addresses with + * a different netmask will also be stored. After when replying to a UDP + * message from a different netmask, the IP address can be looped up and a + * reply sent. This option is useful for systems with multiple gateways, + * the reply will surely arrive. If ipconfigARP_STORES_REMOTE_ADDRESSES is + * zero the the gateway address is the only option. */ + + if( pdTRUE ) + #endif + { + /* Start with the maximum possible number. */ + ucMinAgeFound--; + + /* For each entry in the ARP cache table. */ + for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) + { + BaseType_t xMatchingMAC; + + if( pxMACAddress != NULL ) + { + if( memcmp( xARPCache[ x ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) ) == 0 ) + { + xMatchingMAC = pdTRUE; + } + else + { + xMatchingMAC = pdFALSE; + } + } + else + { + xMatchingMAC = pdFALSE; + } + + /* Does this line in the cache table hold an entry for the IP + * address being queried? */ + if( xARPCache[ x ].ulIPAddress == ulIPAddress ) + { + if( pxMACAddress == NULL ) + { + /* In case the parameter pxMACAddress is NULL, an entry will be reserved to + * indicate that there is an outstanding ARP request, This entry will have + * "ucValid == pdFALSE". */ + xIpEntry = x; + break; + } + + /* See if the MAC-address also matches. */ + if( xMatchingMAC != pdFALSE ) + { + /* A perfect match is found, update the entry and leave this + * function by setting 'xAllDone' to pdTRUE. */ + xARPCache[ x ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE; + xARPCache[ x ].ucValid = ( uint8_t ) pdTRUE; + xAllDone = pdTRUE; + break; + } + + /* Found an entry containing ulIPAddress, but the MAC address + * doesn't match. Might be an entry with ucValid=pdFALSE, waiting + * for an ARP reply. Still want to see if there is match with the + * given MAC address.ucBytes. If found, either of the two entries + * must be cleared. */ + xIpEntry = x; + } + else if( xMatchingMAC != pdFALSE ) + { + /* Found an entry with the given MAC-address, but the IP-address + * is different. Continue looping to find a possible match with + * ulIPAddress. */ + #if ( ipconfigARP_STORES_REMOTE_ADDRESSES != 0 ) + + /* If ARP stores the MAC address of IP addresses outside the + * network, than the MAC address of the gateway should not be + * overwritten. */ + BaseType_t bIsLocal[ 2 ]; + bIsLocal[ 0 ] = ( ( xARPCache[ x ].ulIPAddress & xNetworkAddressing.ulNetMask ) == ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) ); + bIsLocal[ 1 ] = ( ( ulIPAddress & xNetworkAddressing.ulNetMask ) == ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) ); + + if( bIsLocal[ 0 ] == bIsLocal[ 1 ] ) + { + xMacEntry = x; + } + #else /* if ( ipconfigARP_STORES_REMOTE_ADDRESSES != 0 ) */ + xMacEntry = x; + #endif /* if ( ipconfigARP_STORES_REMOTE_ADDRESSES != 0 ) */ + } + + /* _HT_ + * Shouldn't we test for xARPCache[ x ].ucValid == pdFALSE here ? */ + else if( xARPCache[ x ].ucAge < ucMinAgeFound ) + { + /* As the table is traversed, remember the table row that + * contains the oldest entry (the lowest age count, as ages are + * decremented to zero) so the row can be re-used if this function + * needs to add an entry that does not already exist. */ + ucMinAgeFound = xARPCache[ x ].ucAge; + xUseEntry = x; + } + else + { + /* Nothing happens to this cache entry for now. */ + } + } + + if( xAllDone == pdFALSE ) + { + /* A perfect match was not found. See if either the MAC-address + * or the IP-address has a match. */ + if( xMacEntry >= 0 ) + { + xUseEntry = xMacEntry; + + if( xIpEntry >= 0 ) + { + /* Both the MAC address as well as the IP address were found in + * different locations: clear the entry which matches the + * IP-address */ + ( void ) memset( &( xARPCache[ xIpEntry ] ), 0, sizeof( ARPCacheRow_t ) ); + } + } + else if( xIpEntry >= 0 ) + { + /* An entry containing the IP-address was found, but it had a different MAC address */ + xUseEntry = xIpEntry; + } + else + { + /* No matching entry found. */ + } + + /* If the entry was not found, we use the oldest entry and set the IPaddress */ + xARPCache[ xUseEntry ].ulIPAddress = ulIPAddress; + + if( pxMACAddress != NULL ) + { + ( void ) memcpy( xARPCache[ xUseEntry ].xMACAddress.ucBytes, pxMACAddress->ucBytes, sizeof( pxMACAddress->ucBytes ) ); + + iptraceARP_TABLE_ENTRY_CREATED( ulIPAddress, ( *pxMACAddress ) ); + /* And this entry does not need immediate attention */ + xARPCache[ xUseEntry ].ucAge = ( uint8_t ) ipconfigMAX_ARP_AGE; + xARPCache[ xUseEntry ].ucValid = ( uint8_t ) pdTRUE; + } + else if( xIpEntry < 0 ) + { + xARPCache[ xUseEntry ].ucAge = ( uint8_t ) ipconfigMAX_ARP_RETRANSMISSIONS; + xARPCache[ xUseEntry ].ucValid = ( uint8_t ) pdFALSE; + } + else + { + /* Nothing will be stored. */ + } + } + } +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_ARP_REVERSED_LOOKUP == 1 ) + +/** + * @brief Retrieve an entry from the cache table + * + * @param[in] pxMACAddress: The MAC-address of the entry of interest. + * @param[out] pulIPAddress: set to the IP-address found, or unchanged when not found. + * + * @return Either eARPCacheMiss or eARPCacheHit. + */ + eARPLookupResult_t eARPGetCacheEntryByMac( const MACAddress_t * const pxMACAddress, + uint32_t * pulIPAddress ) + { + BaseType_t x; + eARPLookupResult_t eReturn = eARPCacheMiss; + + configASSERT( pxMACAddress != NULL ); + configASSERT( pulIPAddress != NULL ); + + /* Loop through each entry in the ARP cache. */ + for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) + { + /* Does this row in the ARP cache table hold an entry for the MAC + * address being searched? */ + if( memcmp( pxMACAddress->ucBytes, xARPCache[ x ].xMACAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 ) + { + *pulIPAddress = xARPCache[ x ].ulIPAddress; + eReturn = eARPCacheHit; + break; + } + } + + return eReturn; + } +#endif /* ipconfigUSE_ARP_REVERSED_LOOKUP */ + +/*-----------------------------------------------------------*/ + +/** + * @brief Look for ulIPAddress in the ARP cache. + * + * @param[in,out] pulIPAddress: Pointer to the IP-address to be queried to the ARP cache. + * @param[in,out] pxMACAddress: Pointer to a MACAddress_t variable where the MAC address + * will be stored, if found. + * + * @return If the IP address exists, copy the associated MAC address into pxMACAddress, + * refresh the ARP cache entry's age, and return eARPCacheHit. If the IP + * address does not exist in the ARP cache return eARPCacheMiss. If the packet + * cannot be sent for any reason (maybe DHCP is still in process, or the + * addressing needs a gateway but there isn't a gateway defined) then return + * eCantSendPacket. + */ +eARPLookupResult_t eARPGetCacheEntry( uint32_t * pulIPAddress, + MACAddress_t * const pxMACAddress ) +{ + eARPLookupResult_t eReturn; + uint32_t ulAddressToLookup; + + configASSERT( pxMACAddress != NULL ); + configASSERT( pulIPAddress != NULL ); + + ulAddressToLookup = *pulIPAddress; + + if( xIsIPv4Multicast( ulAddressToLookup ) != 0 ) + { + /* Get the lowest 23 bits of the IP-address. */ + vSetMultiCastIPv4MacAddress( ulAddressToLookup, pxMACAddress ); + + eReturn = eARPCacheHit; + } + else if( ( *pulIPAddress == ipBROADCAST_IP_ADDRESS ) || /* Is it the general broadcast address 255.255.255.255? */ + ( *pulIPAddress == xNetworkAddressing.ulBroadcastAddress ) ) /* Or a local broadcast address, eg 192.168.1.255? */ + { + /* This is a broadcast so it uses the broadcast MAC address. */ + ( void ) memcpy( pxMACAddress->ucBytes, xBroadcastMACAddress.ucBytes, sizeof( MACAddress_t ) ); + eReturn = eARPCacheHit; + } + else if( *ipLOCAL_IP_ADDRESS_POINTER == 0U ) + { + /* The IP address has not yet been assigned, so there is nothing that + * can be done. */ + eReturn = eCantSendPacket; + } + else if( *ipLOCAL_IP_ADDRESS_POINTER == *pulIPAddress ) + { + /* The address of this device. May be useful for the loopback device. */ + eReturn = eARPCacheHit; + ( void ) memcpy( pxMACAddress->ucBytes, ipLOCAL_MAC_ADDRESS, sizeof( pxMACAddress->ucBytes ) ); + } + else + { + eReturn = eARPCacheMiss; + + if( ( *pulIPAddress & xNetworkAddressing.ulNetMask ) != ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) ) + { + /* No matching end-point is found, look for a gateway. */ + #if ( ipconfigARP_STORES_REMOTE_ADDRESSES == 1 ) + eReturn = prvCacheLookup( *pulIPAddress, pxMACAddress ); + + if( eReturn == eARPCacheHit ) + { + /* The stack is configured to store 'remote IP addresses', i.e. addresses + * belonging to a different the netmask. prvCacheLookup() returned a hit, so + * the MAC address is known. */ + } + else + #endif + { + /* The IP address is off the local network, so look up the + * hardware address of the router, if any. */ + if( xNetworkAddressing.ulGatewayAddress != ( uint32_t ) 0U ) + { + ulAddressToLookup = xNetworkAddressing.ulGatewayAddress; + } + else + { + ulAddressToLookup = *pulIPAddress; + } + } + } + else + { + /* The IP address is on the local network, so lookup the requested + * IP address directly. */ + ulAddressToLookup = *pulIPAddress; + } + + #if ( ipconfigARP_STORES_REMOTE_ADDRESSES == 1 ) + if( eReturn == eARPCacheMiss ) /*lint !e774: (Info -- Boolean within 'if' always evaluates to True, depending on configuration. */ + #else + /* No cache look-up was done, so the result is still 'eARPCacheMiss'. */ + #endif + { + if( ulAddressToLookup == 0U ) + { + /* The address is not on the local network, and there is not a + * router. */ + eReturn = eCantSendPacket; + } + else + { + eReturn = prvCacheLookup( ulAddressToLookup, pxMACAddress ); + + if( eReturn == eARPCacheMiss ) + { + /* It might be that the ARP has to go to the gateway. */ + *pulIPAddress = ulAddressToLookup; + } + } + } + } + + return eReturn; +} + +/*-----------------------------------------------------------*/ + +/** + * @brief Lookup an IP address in the ARP cache. + * + * @param[in] ulAddressToLookup: The 32-bit representation of an IP address to + * lookup. + * @param[out] pxMACAddress: A pointer to MACAddress_t variable where, if there + * is an ARP cache hit, the MAC address corresponding to + * the IP address will be stored. + * + * @return When the IP-address is found: eARPCacheHit, when not found: eARPCacheMiss, + * and when waiting for a ARP reply: eCantSendPacket. + */ +static eARPLookupResult_t prvCacheLookup( uint32_t ulAddressToLookup, + MACAddress_t * const pxMACAddress ) +{ + BaseType_t x; + eARPLookupResult_t eReturn = eARPCacheMiss; + + /* Loop through each entry in the ARP cache. */ + for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) + { + /* Does this row in the ARP cache table hold an entry for the IP address + * being queried? */ + if( xARPCache[ x ].ulIPAddress == ulAddressToLookup ) + { + /* A matching valid entry was found. */ + if( xARPCache[ x ].ucValid == ( uint8_t ) pdFALSE ) + { + /* This entry is waiting an ARP reply, so is not valid. */ + eReturn = eCantSendPacket; + } + else + { + /* A valid entry was found. */ + ( void ) memcpy( pxMACAddress->ucBytes, xARPCache[ x ].xMACAddress.ucBytes, sizeof( MACAddress_t ) ); + eReturn = eARPCacheHit; + } + + break; + } + } + + return eReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief A call to this function will update (or 'Age') the ARP cache entries. + * The function will also try to prevent a removal of entry by sending + * an ARP query. It will also check whether we are waiting on an ARP + * reply - if we are, then an ARP request will be re-sent. + * In case an ARP entry has 'Aged' to 0, it will be removed from the ARP + * cache. + */ +void vARPAgeCache( void ) +{ + BaseType_t x; + TickType_t xTimeNow; + + /* Loop through each entry in the ARP cache. */ + for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) + { + /* If the entry is valid (its age is greater than zero). */ + if( xARPCache[ x ].ucAge > 0U ) + { + /* Decrement the age value of the entry in this ARP cache table row. + * When the age reaches zero it is no longer considered valid. */ + ( xARPCache[ x ].ucAge )--; + + /* If the entry is not yet valid, then it is waiting an ARP + * reply, and the ARP request should be retransmitted. */ + if( xARPCache[ x ].ucValid == ( uint8_t ) pdFALSE ) + { + FreeRTOS_OutputARPRequest( xARPCache[ x ].ulIPAddress ); + } + else if( xARPCache[ x ].ucAge <= ( uint8_t ) arpMAX_ARP_AGE_BEFORE_NEW_ARP_REQUEST ) + { + /* This entry will get removed soon. See if the MAC address is + * still valid to prevent this happening. */ + iptraceARP_TABLE_ENTRY_WILL_EXPIRE( xARPCache[ x ].ulIPAddress ); + FreeRTOS_OutputARPRequest( xARPCache[ x ].ulIPAddress ); + } + else + { + /* The age has just ticked down, with nothing to do. */ + } + + if( xARPCache[ x ].ucAge == 0U ) + { + /* The entry is no longer valid. Wipe it out. */ + iptraceARP_TABLE_ENTRY_EXPIRED( xARPCache[ x ].ulIPAddress ); + xARPCache[ x ].ulIPAddress = 0U; + } + } + } + + xTimeNow = xTaskGetTickCount(); + + if( ( xLastGratuitousARPTime == ( TickType_t ) 0 ) || ( ( xTimeNow - xLastGratuitousARPTime ) > ( TickType_t ) arpGRATUITOUS_ARP_PERIOD ) ) + { + FreeRTOS_OutputARPRequest( *ipLOCAL_IP_ADDRESS_POINTER ); + xLastGratuitousARPTime = xTimeNow; + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Send a Gratuitous ARP packet to allow this node to announce the IP-MAC + * mapping to the entire network. + */ +void vARPSendGratuitous( void ) +{ + /* Setting xLastGratuitousARPTime to 0 will force a gratuitous ARP the next + * time vARPAgeCache() is called. */ + xLastGratuitousARPTime = ( TickType_t ) 0; + + /* Let the IP-task call vARPAgeCache(). */ + ( void ) xSendEventToIPTask( eARPTimerEvent ); +} + +/*-----------------------------------------------------------*/ + +/** + * @brief Create and send an ARP request packet. + * + * @param[in] ulIPAddress: A 32-bit representation of the IP-address whose + * physical (MAC) address is required. + */ +void FreeRTOS_OutputARPRequest( uint32_t ulIPAddress ) +{ + NetworkBufferDescriptor_t * pxNetworkBuffer; + + /* This is called from the context of the IP event task, so a block time + * must not be used. */ + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( sizeof( ARPPacket_t ), ( TickType_t ) 0U ); + + if( pxNetworkBuffer != NULL ) + { + pxNetworkBuffer->ulIPAddress = ulIPAddress; + vARPGenerateRequestPacket( pxNetworkBuffer ); + + #if ( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) + { + if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES ) + { + BaseType_t xIndex; + + for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ ) + { + pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0U; + } + + pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; + } + } + #endif /* if( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) */ + + if( xIsCallingFromIPTask() != pdFALSE ) + { + iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer ); + /* Only the IP-task is allowed to call this function directly. */ + ( void ) xNetworkInterfaceOutput( pxNetworkBuffer, pdTRUE ); + } + else + { + IPStackEvent_t xSendEvent; + + /* Send a message to the IP-task to send this ARP packet. */ + xSendEvent.eEventType = eNetworkTxEvent; + xSendEvent.pvData = pxNetworkBuffer; + + if( xSendEventStructToIPTask( &xSendEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL ) + { + /* Failed to send the message, so release the network buffer. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + } + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Wait for address resolution: look-up the IP-address in the ARP-cache, and if + * needed send an ARP request, and wait for a reply. This function is useful when + * called before FreeRTOS_sendto(). + * + * @param[in] ulIPAddress: The IP-address to look-up. + * @param[in] uxTicksToWait: The maximum number of clock ticks to wait for a reply. + * + * @return Zero when successful. + */ +BaseType_t xARPWaitResolution( uint32_t ulIPAddress, + TickType_t uxTicksToWait ) +{ + BaseType_t xResult = -pdFREERTOS_ERRNO_EADDRNOTAVAIL; + TimeOut_t xTimeOut; + MACAddress_t xMACAddress; + eARPLookupResult_t xLookupResult; + size_t uxSendCount = ipconfigMAX_ARP_RETRANSMISSIONS; + uint32_t ulIPAddressCopy = ulIPAddress; + + /* The IP-task is not supposed to call this function. */ + configASSERT( xIsCallingFromIPTask() == pdFALSE ); + + xLookupResult = eARPGetCacheEntry( &( ulIPAddressCopy ), &( xMACAddress ) ); + + if( xLookupResult == eARPCacheMiss ) + { + const TickType_t uxSleepTime = pdMS_TO_TICKS( 250U ); + + /* We might use ipconfigMAX_ARP_RETRANSMISSIONS here. */ + vTaskSetTimeOutState( &xTimeOut ); + + while( uxSendCount > 0U ) + { + FreeRTOS_OutputARPRequest( ulIPAddressCopy ); + + vTaskDelay( uxSleepTime ); + + xLookupResult = eARPGetCacheEntry( &( ulIPAddressCopy ), &( xMACAddress ) ); + + if( ( xTaskCheckForTimeOut( &( xTimeOut ), &( uxTicksToWait ) ) == pdTRUE ) || + ( xLookupResult != eARPCacheMiss ) ) + { + break; + } + + /* Decrement the count. */ + uxSendCount--; + } + } + + if( xLookupResult == eARPCacheHit ) + { + xResult = 0; + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Generate an ARP request packet by copying various constant details to + * the buffer. + * + * @param[in,out] pxNetworkBuffer: Pointer to the buffer which has to be filled with + * the ARP request packet details. + */ +void vARPGenerateRequestPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ +/* Part of the Ethernet and ARP headers are always constant when sending an IPv4 + * ARP packet. This array defines the constant parts, allowing this part of the + * packet to be filled in using a simple memcpy() instead of individual writes. */ + static const uint8_t xDefaultPartARPPacketHeader[] = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Ethernet destination address. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Ethernet source address. */ + 0x08, 0x06, /* Ethernet frame type (ipARP_FRAME_TYPE). */ + 0x00, 0x01, /* usHardwareType (ipARP_HARDWARE_TYPE_ETHERNET). */ + 0x08, 0x00, /* usProtocolType. */ + ipMAC_ADDRESS_LENGTH_BYTES, /* ucHardwareAddressLength. */ + ipIP_ADDRESS_LENGTH_BYTES, /* ucProtocolAddressLength. */ + 0x00, 0x01, /* usOperation (ipARP_REQUEST). */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* xSenderHardwareAddress. */ + 0x00, 0x00, 0x00, 0x00, /* ulSenderProtocolAddress. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* xTargetHardwareAddress. */ + }; + + ARPPacket_t * pxARPPacket; + +/* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + /* Buffer allocation ensures that buffers always have space + * for an ARP packet. See buffer allocation implementations 1 + * and 2 under portable/BufferManagement. */ + configASSERT( pxNetworkBuffer != NULL ); + configASSERT( pxNetworkBuffer->xDataLength >= sizeof( ARPPacket_t ) ); + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxARPPacket = ( ( ARPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); + + /* memcpy the const part of the header information into the correct + * location in the packet. This copies: + * xEthernetHeader.ulDestinationAddress + * xEthernetHeader.usFrameType; + * xARPHeader.usHardwareType; + * xARPHeader.usProtocolType; + * xARPHeader.ucHardwareAddressLength; + * xARPHeader.ucProtocolAddressLength; + * xARPHeader.usOperation; + * xARPHeader.xTargetHardwareAddress; + */ + + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = xDefaultPartARPPacketHeader; + pvCopyDest = pxARPPacket; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( xDefaultPartARPPacketHeader ) ); + + pvCopySource = ipLOCAL_MAC_ADDRESS; + pvCopyDest = pxARPPacket->xEthernetHeader.xSourceAddress.ucBytes; + ( void ) memcpy( pvCopyDest, pvCopySource, ipMAC_ADDRESS_LENGTH_BYTES ); + + pvCopySource = ipLOCAL_MAC_ADDRESS; + pvCopyDest = pxARPPacket->xARPHeader.xSenderHardwareAddress.ucBytes; + ( void ) memcpy( pvCopyDest, pvCopySource, ipMAC_ADDRESS_LENGTH_BYTES ); + + pvCopySource = ipLOCAL_IP_ADDRESS_POINTER; + pvCopyDest = pxARPPacket->xARPHeader.ucSenderProtocolAddress; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxARPPacket->xARPHeader.ucSenderProtocolAddress ) ); + pxARPPacket->xARPHeader.ulTargetProtocolAddress = pxNetworkBuffer->ulIPAddress; + + pxNetworkBuffer->xDataLength = sizeof( ARPPacket_t ); + + iptraceCREATING_ARP_REQUEST( pxNetworkBuffer->ulIPAddress ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief A call to this function will clear the ARP cache. + */ +void FreeRTOS_ClearARP( void ) +{ + ( void ) memset( xARPCache, 0, sizeof( xARPCache ) ); +} +/*-----------------------------------------------------------*/ + +#if 1 + +/** + * @brief This function will check if the target IP-address belongs to this device. + * If so, the packet will be passed to the IP-stack, who will answer it. + * The function is to be called within the function xNetworkInterfaceOutput(). + * + * @param[in] pxDescriptor: The network buffer which is to be checked for loop-back. + * @param[in] bReleaseAfterSend: pdTRUE: Driver is allowed to transfer ownership of descriptor. + * pdFALSE: Driver is not allowed to take ownership of descriptor, + * make a copy of it. + * + * @return pdTRUE/pdFALSE: There is/isn't a loopback address in the packet. + */ + BaseType_t xCheckLoopback( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t bReleaseAfterSend ) + { + BaseType_t xResult = pdFALSE; + NetworkBufferDescriptor_t * pxUseDescriptor = pxDescriptor; + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const IPPacket_t * pxIPPacket = ( ( IPPacket_t * ) pxUseDescriptor->pucEthernetBuffer ); + + if( pxIPPacket->xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) + { + if( memcmp( pxIPPacket->xEthernetHeader.xDestinationAddress.ucBytes, ipLOCAL_MAC_ADDRESS, ipMAC_ADDRESS_LENGTH_BYTES ) == 0 ) + { + xResult = pdTRUE; + + if( bReleaseAfterSend == pdFALSE ) + { + /* Driver is not allowed to transfer the ownership + * of descriptor, so make a copy of it */ + pxUseDescriptor = + pxDuplicateNetworkBufferWithDescriptor( pxDescriptor, pxDescriptor->xDataLength ); + } + + if( pxUseDescriptor != NULL ) + { + IPStackEvent_t xRxEvent; + + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = pxUseDescriptor; + + if( xSendEventStructToIPTask( &xRxEvent, 0U ) != pdTRUE ) + { + vReleaseNetworkBufferAndDescriptor( pxUseDescriptor ); + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) ); + } + } + } + } + + return xResult; + } + +#endif /* 0 */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 ) + + void FreeRTOS_PrintARPCache( void ) + { + BaseType_t x, xCount = 0; + + /* Loop through each entry in the ARP cache. */ + for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) + { + if( ( xARPCache[ x ].ulIPAddress != 0U ) && ( xARPCache[ x ].ucAge > ( uint8_t ) 0U ) ) + { + /* See if the MAC-address also matches, and we're all happy */ + FreeRTOS_printf( ( "ARP %2d: %3u - %16xip : %02x:%02x:%02x : %02x:%02x:%02x\n", + ( int ) x, + xARPCache[ x ].ucAge, + ( unsigned ) xARPCache[ x ].ulIPAddress, + xARPCache[ x ].xMACAddress.ucBytes[ 0 ], + xARPCache[ x ].xMACAddress.ucBytes[ 1 ], + xARPCache[ x ].xMACAddress.ucBytes[ 2 ], + xARPCache[ x ].xMACAddress.ucBytes[ 3 ], + xARPCache[ x ].xMACAddress.ucBytes[ 4 ], + xARPCache[ x ].xMACAddress.ucBytes[ 5 ] ) ); + xCount++; + } + } + + FreeRTOS_printf( ( "Arp has %ld entries\n", xCount ) ); + } + +#endif /* ( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 ) */ diff --git a/FreeRTOS/source/FreeRTOS_DHCP.c b/FreeRTOS/source/FreeRTOS_DHCP.c new file mode 100644 index 0000000..098e01c --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_DHCP.c @@ -0,0 +1,1282 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_DHCP.c + * @brief Implements the Dynamic Host Configuration Protocol for the FreeRTOS+TCP network stack. + */ + +/* Standard includes. */ +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_IP_Timers.h" + + +/* Exclude the entire file if DHCP is not enabled. */ +#if ( ipconfigUSE_DHCP != 0 ) + + #include "NetworkInterface.h" + #include "NetworkBufferManagement.h" + +/** @brief The UDP socket used for all incoming and outgoing DHCP traffic. */ + _static Socket_t xDHCPSocket; + + #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + /* Define the Link Layer IP address: 169.254.x.x */ + #define LINK_LAYER_ADDRESS_0 169 + #define LINK_LAYER_ADDRESS_1 254 + +/* Define the netmask used: 255.255.0.0 */ + #define LINK_LAYER_NETMASK_0 255 + #define LINK_LAYER_NETMASK_1 255 + #define LINK_LAYER_NETMASK_2 0 + #define LINK_LAYER_NETMASK_3 0 + #endif + + +/* + * Generate a DHCP discover message and send it on the DHCP socket. + */ + static BaseType_t prvSendDHCPDiscover( void ); + +/* + * Interpret message received on the DHCP socket. + */ + _static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType ); + +/* + * Generate a DHCP request packet, and send it on the DHCP socket. + */ + static BaseType_t prvSendDHCPRequest( void ); + +/* + * Prepare to start a DHCP transaction. This initialises some state variables + * and creates the DHCP socket if necessary. + */ + static void prvInitialiseDHCP( void ); + +/* + * Creates the part of outgoing DHCP messages that are common to all outgoing + * DHCP messages. + */ + static uint8_t * prvCreatePartDHCPMessage( struct freertos_sockaddr * pxAddress, + BaseType_t xOpcode, + const uint8_t * const pucOptionsArray, + size_t * pxOptionsArraySize ); + +/* + * Create the DHCP socket, if it has not been created already. + */ + _static void prvCreateDHCPSocket( void ); + +/* + * Close the DHCP socket. + */ + static void prvCloseDHCPSocket( void ); + +/* + * After DHCP has failed to answer, prepare everything to start searching + * for (trying-out) LinkLayer IP-addresses, using the random method: Send + * a gratuitous ARP request and wait if another device responds to it. + */ + #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + static void prvPrepareLinkLayerIPLookUp( void ); + #endif + +/*-----------------------------------------------------------*/ + +/** @brief Hold information in between steps in the DHCP state machine. */ + _static DHCPData_t xDHCPData; + +/*-----------------------------------------------------------*/ + +/** + * @brief Check whether a given socket is the DHCP socket or not. + * + * @param[in] xSocket: The socket to be checked. + * + * @return If the socket given as parameter is the DHCP socket - return + * pdTRUE, else pdFALSE. + */ + BaseType_t xIsDHCPSocket( const ConstSocket_t xSocket ) + { + BaseType_t xReturn; + + if( xDHCPSocket == xSocket ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; + } + /*-----------------------------------------------------------*/ + +/** + * @brief The application can indicate a preferred IP address by calling this function. + * before starting up the IP-task by calling FreeRTOS_IPInit(). + * + * @param[in] ulIPAddress: The preferred IP-address. + * + * @return The previous value of ulPreferredIPAddress. + */ + uint32_t vDHCPSetPreferredIPAddress( uint32_t ulIPAddress ) + { + uint32_t ulPrevious = xDHCPData.ulPreferredIPAddress; + + xDHCPData.ulPreferredIPAddress = ulIPAddress; + + return ulPrevious; + } + +/** + * @brief Returns the current state of a DHCP process. + * + * @return The current state ( eDHCPState_t ) of the DHCP process. + */ + eDHCPState_t eGetDHCPState( void ) + { + return EP_DHCPData.eDHCPState; + } + +/** + * @brief Process the DHCP state machine based on current state. + * + * @param[in] xReset: Is the DHCP state machine starting over? pdTRUE/pdFALSE. + * @param[in] eExpectedState: The function will only run if the state is expected. + */ + void vDHCPProcess( BaseType_t xReset, + eDHCPState_t eExpectedState ) + { + BaseType_t xGivingUp = pdFALSE; + + #if ( ipconfigUSE_DHCP_HOOK != 0 ) + eDHCPCallbackAnswer_t eAnswer; + #endif /* ipconfigUSE_DHCP_HOOK */ + + /* Is DHCP starting over? */ + if( xReset != pdFALSE ) + { + EP_DHCPData.eDHCPState = eInitialWait; + } + + if( ( EP_DHCPData.eDHCPState != eExpectedState ) && ( xReset == pdFALSE ) ) + { + /* When the DHCP event was generated, the DHCP client was + * in a different state. Therefore, ignore this event. */ + FreeRTOS_debug_printf( ( "DHCP wrong state: expect: %d got: %d : ignore\n", + eExpectedState, EP_DHCPData.eDHCPState ) ); + } + else + { + switch( EP_DHCPData.eDHCPState ) + { + case eInitialWait: + + /* Initial state. Create the DHCP socket, timer, etc. if they + * have not already been created. */ + prvInitialiseDHCP(); + EP_DHCPData.eDHCPState = eWaitingSendFirstDiscover; + break; + + case eWaitingSendFirstDiscover: + /* Ask the user if a DHCP discovery is required. */ + #if ( ipconfigUSE_DHCP_HOOK != 0 ) + eAnswer = xApplicationDHCPHook( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress ); + + if( eAnswer == eDHCPContinue ) + #endif /* ipconfigUSE_DHCP_HOOK */ + { + /* See if prvInitialiseDHCP() has creates a socket. */ + if( xDHCPSocket == NULL ) + { + xGivingUp = pdTRUE; + } + else + { + *ipLOCAL_IP_ADDRESS_POINTER = 0U; + + /* Send the first discover request. */ + EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); + + if( prvSendDHCPDiscover() == pdPASS ) + { + EP_DHCPData.eDHCPState = eWaitingOffer; + } + else + { + /* Either the creation of a message buffer failed, or sendto(). + * Try again in the next cycle. */ + FreeRTOS_debug_printf( ( "Send failed during eWaitingSendFirstDiscover\n" ) ); + } + } + } + + #if ( ipconfigUSE_DHCP_HOOK != 0 ) + else + { + if( eAnswer == eDHCPUseDefaults ) + { + ( void ) memcpy( &( xNetworkAddressing ), &( xDefaultAddressing ), sizeof( xNetworkAddressing ) ); + } + + /* The user indicates that the DHCP process does not continue. */ + xGivingUp = pdTRUE; + } + #endif /* ipconfigUSE_DHCP_HOOK */ + break; + + case eSendDHCPRequest: + + if( prvSendDHCPRequest() == pdPASS ) + { + /* Send succeeded, go to state 'eWaitingAcknowledge'. */ + EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); + EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; + EP_DHCPData.eDHCPState = eWaitingAcknowledge; + } + else + { + /* Either the creation of a message buffer failed, or sendto(). + * Try again in the next cycle. */ + FreeRTOS_debug_printf( ( "Send failed during eSendDHCPRequest.\n" ) ); + } + + break; + + case eWaitingOffer: + + xGivingUp = pdFALSE; + + /* Look for offers coming in. */ + if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER ) == pdPASS ) + { + #if ( ipconfigUSE_DHCP_HOOK != 0 ) + /* Ask the user if a DHCP request is required. */ + eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, EP_DHCPData.ulOfferedIPAddress ); + + if( eAnswer == eDHCPContinue ) + #endif /* ipconfigUSE_DHCP_HOOK */ + { + /* An offer has been made, the user wants to continue, + * generate the request. */ + if( prvSendDHCPRequest() == pdPASS ) + { + EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); + EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; + EP_DHCPData.eDHCPState = eWaitingAcknowledge; + } + else + { + /* Either the creation of a message buffer failed, or sendto(). + * Try again in the next cycle. */ + FreeRTOS_debug_printf( ( "Send failed during eWaitingOffer/1.\n" ) ); + EP_DHCPData.eDHCPState = eSendDHCPRequest; + } + + break; + } + + #if ( ipconfigUSE_DHCP_HOOK != 0 ) + if( eAnswer == eDHCPUseDefaults ) + { + ( void ) memcpy( &( xNetworkAddressing ), &( xDefaultAddressing ), sizeof( xNetworkAddressing ) ); + } + + /* The user indicates that the DHCP process does not continue. */ + xGivingUp = pdTRUE; + #endif /* ipconfigUSE_DHCP_HOOK */ + } + + /* Is it time to send another Discover? */ + else if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod ) + { + /* It is time to send another Discover. Increase the time + * period, and if it has not got to the point of giving up - send + * another discovery. */ + EP_DHCPData.xDHCPTxPeriod <<= 1; + + if( EP_DHCPData.xDHCPTxPeriod <= ( TickType_t ) ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) + { + if( xApplicationGetRandomNumber( &( EP_DHCPData.ulTransactionId ) ) != pdFALSE ) + { + EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); + + if( EP_DHCPData.xUseBroadcast != pdFALSE ) + { + EP_DHCPData.xUseBroadcast = pdFALSE; + } + else + { + EP_DHCPData.xUseBroadcast = pdTRUE; + } + + if( prvSendDHCPDiscover() == pdPASS ) + { + FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", EP_DHCPData.xDHCPTxPeriod ) ); + } + else + { + /* Either the creation of a message buffer failed, or sendto(). + * Try again in the next cycle. */ + FreeRTOS_debug_printf( ( "Send failed during eWaitingOffer/2.\n" ) ); + EP_DHCPData.eDHCPState = eInitialWait; + } + } + else + { + FreeRTOS_debug_printf( ( "vDHCPProcess: failed to generate a random Transaction ID\n" ) ); + } + } + else + { + FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n", EP_DHCPData.xDHCPTxPeriod, ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) ); + + #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + { + /* Only use a fake Ack if the default IP address == 0x00 + * and the link local addressing is used. Start searching + * a free LinkLayer IP-address. Next state will be + * 'eGetLinkLayerAddress'. */ + prvPrepareLinkLayerIPLookUp(); + + /* Setting an IP address manually so set to not using + * leased address mode. */ + EP_DHCPData.eDHCPState = eGetLinkLayerAddress; + } + #else + { + xGivingUp = pdTRUE; + } + #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ + } + } + else + { + /* There was no DHCP reply, there was no time-out, just keep on waiting. */ + } + + break; + + case eWaitingAcknowledge: + + /* Look for acks coming in. */ + if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK ) == pdPASS ) + { + FreeRTOS_debug_printf( ( "vDHCPProcess: acked %xip\n", ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) ); + + /* DHCP completed. The IP address can now be used, and the + * timer set to the lease timeout time. */ + *ipLOCAL_IP_ADDRESS_POINTER = EP_DHCPData.ulOfferedIPAddress; + + /* Setting the 'local' broadcast address, something like + * '192.168.1.255'. */ + EP_IPv4_SETTINGS.ulBroadcastAddress = ( EP_DHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask; + EP_DHCPData.eDHCPState = eLeasedAddress; + + iptraceDHCP_SUCCEDEED( EP_DHCPData.ulOfferedIPAddress ); + + /* DHCP failed, the default configured IP-address will be used + * Now call vIPNetworkUpCalls() to send the network-up event and + * start the ARP timer. */ + vIPNetworkUpCalls(); + + /* Close socket to ensure packets don't queue on it. */ + prvCloseDHCPSocket(); + + if( EP_DHCPData.ulLeaseTime == 0U ) + { + EP_DHCPData.ulLeaseTime = ( uint32_t ) dhcpDEFAULT_LEASE_TIME; + } + else if( EP_DHCPData.ulLeaseTime < dhcpMINIMUM_LEASE_TIME ) + { + EP_DHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME; + } + else + { + /* The lease time is already valid. */ + } + + /* Check for clashes. */ + vARPSendGratuitous(); + vDHCPTimerReload( EP_DHCPData.ulLeaseTime ); + } + else + { + /* Is it time to send another Discover? */ + if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod ) + { + /* Increase the time period, and if it has not got to the + * point of giving up - send another request. */ + EP_DHCPData.xDHCPTxPeriod <<= 1; + + if( EP_DHCPData.xDHCPTxPeriod <= ( TickType_t ) ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) + { + EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); + + if( prvSendDHCPRequest() == pdPASS ) + { + /* The message is sent. Stay in state 'eWaitingAcknowledge'. */ + } + else + { + /* Either the creation of a message buffer failed, or sendto(). + * Try again in the next cycle. */ + FreeRTOS_debug_printf( ( "Send failed during eWaitingAcknowledge.\n" ) ); + EP_DHCPData.eDHCPState = eSendDHCPRequest; + } + } + else + { + /* Give up, start again. */ + EP_DHCPData.eDHCPState = eInitialWait; + } + } + } + + break; + + #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + case eGetLinkLayerAddress: + + if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod ) + { + if( xARPHadIPClash == pdFALSE ) + { + /* ARP OK. proceed. */ + iptraceDHCP_SUCCEDEED( EP_DHCPData.ulOfferedIPAddress ); + + /* Auto-IP succeeded, the default configured IP-address will + * be used. Now call vIPNetworkUpCalls() to send the + * network-up event and start the ARP timer. */ + vIPNetworkUpCalls(); + EP_DHCPData.eDHCPState = eNotUsingLeasedAddress; + } + else + { + /* ARP clashed - try another IP address. */ + prvPrepareLinkLayerIPLookUp(); + + /* Setting an IP address manually so set to not using leased + * address mode. */ + EP_DHCPData.eDHCPState = eGetLinkLayerAddress; + } + } + break; + #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ + + case eLeasedAddress: + + if( FreeRTOS_IsNetworkUp() != 0 ) + { + /* Resend the request at the appropriate time to renew the lease. */ + prvCreateDHCPSocket(); + + if( xDHCPSocket != NULL ) + { + uint32_t ulID = 0U; + + if( xApplicationGetRandomNumber( &( ulID ) ) != pdFALSE ) + { + EP_DHCPData.ulTransactionId = ulID; + } + + EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); + EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; + + if( prvSendDHCPRequest() == pdPASS ) + { + /* The packet was sent successfully, wait for an acknowledgement. */ + EP_DHCPData.eDHCPState = eWaitingAcknowledge; + } + else + { + /* The packet was not sent, try sending it later. */ + EP_DHCPData.eDHCPState = eSendDHCPRequest; + FreeRTOS_debug_printf( ( "Send failed eLeasedAddress.\n" ) ); + } + + /* From now on, we should be called more often */ + vDHCPTimerReload( dhcpINITIAL_TIMER_PERIOD ); + } + } + else + { + /* See PR #53 on github/freertos/freertos */ + FreeRTOS_printf( ( "DHCP: lease time finished but network is down\n" ) ); + vDHCPTimerReload( pdMS_TO_TICKS( 5000U ) ); + } + + break; + + case eNotUsingLeasedAddress: + + vIPSetDHCPTimerEnableState( pdFALSE ); + break; + + default: + /* Lint: all options are included. */ + break; + } + + if( xGivingUp != pdFALSE ) + { + /* xGivingUp became true either because of a time-out, or because + * xApplicationDHCPHook() returned another value than 'eDHCPContinue', + * meaning that the conversion is cancelled from here. */ + + /* Revert to static IP address. */ + taskENTER_CRITICAL(); + { + *ipLOCAL_IP_ADDRESS_POINTER = xNetworkAddressing.ulDefaultIPAddress; + iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( xNetworkAddressing.ulDefaultIPAddress ); + } + taskEXIT_CRITICAL(); + + EP_DHCPData.eDHCPState = eNotUsingLeasedAddress; + vIPSetDHCPTimerEnableState( pdFALSE ); + + /* DHCP failed, the default configured IP-address will be used. Now + * call vIPNetworkUpCalls() to send the network-up event and start the ARP + * timer. */ + vIPNetworkUpCalls(); + + /* Close socket to ensure packets don't queue on it. */ + prvCloseDHCPSocket(); + } + } + } + /*-----------------------------------------------------------*/ + +/** + * @brief Close the DHCP socket. + */ + static void prvCloseDHCPSocket( void ) + { + if( xDHCPSocket != NULL ) + { + /* This modules runs from the IP-task. Use the internal + * function 'vSocketClose()` to close the socket. */ + ( void ) vSocketClose( xDHCPSocket ); + xDHCPSocket = NULL; + } + } + /*-----------------------------------------------------------*/ + +/** + * @brief Create a DHCP socket with the defined timeouts. + */ + _static void prvCreateDHCPSocket( void ) + { + struct freertos_sockaddr xAddress; + BaseType_t xReturn; + TickType_t xTimeoutTime = ( TickType_t ) 0; + + /* Create the socket, if it has not already been created. */ + if( xDHCPSocket == NULL ) + { + xDHCPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + if( xDHCPSocket != FREERTOS_INVALID_SOCKET ) + { + /* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the + * context of the IP task. */ + ( void ) FreeRTOS_setsockopt( xDHCPSocket, 0, FREERTOS_SO_RCVTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) ); + ( void ) FreeRTOS_setsockopt( xDHCPSocket, 0, FREERTOS_SO_SNDTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) ); + + /* Bind to the standard DHCP client port. */ + xAddress.sin_port = ( uint16_t ) dhcpCLIENT_PORT_IPv4; + xReturn = vSocketBind( xDHCPSocket, &xAddress, sizeof( xAddress ), pdFALSE ); + + if( xReturn != 0 ) + { + /* Binding failed, close the socket again. */ + prvCloseDHCPSocket(); + } + } + else + { + /* Change to NULL for easier testing. */ + xDHCPSocket = NULL; + } + } + } + /*-----------------------------------------------------------*/ + +/** + * @brief Initialise the DHCP state machine by creating DHCP socket and + * begin the transaction. + */ + static void prvInitialiseDHCP( void ) + { + /* Initialise the parameters that will be set by the DHCP process. Per + * https://www.ietf.org/rfc/rfc2131.txt, Transaction ID should be a random + * value chosen by the client. */ + + /* Check for random number generator API failure. */ + if( xApplicationGetRandomNumber( &( EP_DHCPData.ulTransactionId ) ) != pdFALSE ) + { + EP_DHCPData.xUseBroadcast = 0; + EP_DHCPData.ulOfferedIPAddress = 0U; + EP_DHCPData.ulDHCPServerAddress = 0U; + EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; + + /* Create the DHCP socket if it has not already been created. */ + prvCreateDHCPSocket(); + FreeRTOS_debug_printf( ( "prvInitialiseDHCP: start after %lu ticks\n", dhcpINITIAL_TIMER_PERIOD ) ); + vDHCPTimerReload( dhcpINITIAL_TIMER_PERIOD ); + } + else + { + /* There was a problem with the randomiser. */ + } + } + /*-----------------------------------------------------------*/ + +/** + * @brief Check whether the DHCP response from the server has all valid + * invariant parameters and valid (non broadcast and non localhost) + * IP address being assigned to the device. + * + * @param[in] pxDHCPMessage: The DHCP message. + * + * @return pdPASS if the DHCP response has correct parameters; pdFAIL otherwise. + */ + static BaseType_t prvIsValidDHCPResponse( const DHCPMessage_IPv4_t * pxDHCPMessage ) + { + BaseType_t xReturn = pdPASS; + + if( ( pxDHCPMessage->ulDHCPCookie != ( uint32_t ) dhcpCOOKIE ) || + ( pxDHCPMessage->ucOpcode != ( uint8_t ) dhcpREPLY_OPCODE ) || + ( pxDHCPMessage->ucAddressType != ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET ) || + ( pxDHCPMessage->ucAddressLength != ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH ) || + ( ( FreeRTOS_ntohl( pxDHCPMessage->ulYourIPAddress_yiaddr ) & 0xFFU ) == 0xFFU ) || + ( ( ( pxDHCPMessage->ulYourIPAddress_yiaddr & 0x7FU ) ^ 0x7FU ) == 0x00U ) ) + { + /* Invalid cookie OR + * Unexpected opcode OR + * Incorrect address type OR + * Incorrect address length OR + * The DHCP server is trying to assign a broadcast address to the device OR + * The DHCP server is trying to assign a localhost address to the device. */ + xReturn = pdFAIL; + } + + return xReturn; + } + +/** + * @brief Process the DHCP replies. + * + * @param[in] xExpectedMessageType: The type of the message the DHCP state machine is expecting. + * Messages of different type will be dropped. + * + * @return pdPASS: if DHCP options are received correctly; pdFAIL: Otherwise. + */ + _static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType ) + { + uint8_t * pucUDPPayload; + int32_t lBytes; + const DHCPMessage_IPv4_t * pxDHCPMessage; + const uint8_t * pucByte; + uint8_t ucOptionCode; + uint32_t ulProcessed, ulParameter; + BaseType_t xReturn = pdFALSE; + const uint32_t ulMandatoryOptions = 2U; /* DHCP server address, and the correct DHCP message type must be present in the options. */ + /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + /* Passing the address of a pointer (pucUDPPayload) because FREERTOS_ZERO_COPY is used. */ + lBytes = FreeRTOS_recvfrom( xDHCPSocket, &pucUDPPayload, 0U, FREERTOS_ZERO_COPY, NULL, NULL ); + + if( lBytes > 0 ) + { + /* Map a DHCP structure onto the received data. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxDHCPMessage = ( ( DHCPMessage_IPv4_t * ) pucUDPPayload ); + + /* Sanity check. */ + if( lBytes < ( int32_t ) sizeof( DHCPMessage_IPv4_t ) ) + { + /* Not enough bytes. */ + } + else if( prvIsValidDHCPResponse( pxDHCPMessage ) == pdFAIL ) + { + /* Invalid values in DHCP response. */ + } + else if( ( pxDHCPMessage->ulTransactionID != FreeRTOS_htonl( EP_DHCPData.ulTransactionId ) ) ) + { + /* Transaction ID does not match. */ + } + else /* Looks like a valid DHCP response, with the same transaction ID. */ + { + if( memcmp( pxDHCPMessage->ucClientHardwareAddress, + ipLOCAL_MAC_ADDRESS, + sizeof( MACAddress_t ) ) != 0 ) + { + /* Target MAC address doesn't match. */ + } + else + { + size_t uxIndex, uxPayloadDataLength, uxLength; + + /* None of the essential options have been processed yet. */ + ulProcessed = 0U; + + /* Walk through the options until the dhcpOPTION_END_BYTE byte + * is found, taking care not to walk off the end of the options. */ + pucByte = &( pucUDPPayload[ sizeof( DHCPMessage_IPv4_t ) ] ); + uxIndex = 0; + uxPayloadDataLength = ( ( size_t ) lBytes ) - sizeof( DHCPMessage_IPv4_t ); + + while( uxIndex < uxPayloadDataLength ) + { + ucOptionCode = pucByte[ uxIndex ]; + + if( ucOptionCode == ( uint8_t ) dhcpOPTION_END_BYTE ) + { + /* Ready, the last byte has been seen. */ + /* coverity[break_stmt] : Break statement terminating the loop */ + break; + } + + if( ucOptionCode == ( uint8_t ) dhcpIPv4_ZERO_PAD_OPTION_CODE ) + { + /* The value zero is used as a pad byte, + * it is not followed by a length byte. */ + uxIndex = uxIndex + 1U; + continue; + } + + /* Stop if the response is malformed. */ + if( ( uxIndex + 1U ) < uxPayloadDataLength ) + { + /* Fetch the length byte. */ + uxLength = ( size_t ) pucByte[ uxIndex + 1U ]; + uxIndex = uxIndex + 2U; + + if( !( ( ( uxIndex + uxLength ) - 1U ) < uxPayloadDataLength ) ) + { + /* There are not as many bytes left as there should be. */ + break; + } + } + else + { + /* The length byte is missing. */ + break; + } + + /* In most cases, a 4-byte network-endian parameter follows, + * just get it once here and use later. */ + if( uxLength >= sizeof( ulParameter ) ) + { + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = &pucByte[ uxIndex ]; + pvCopyDest = &ulParameter; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( ulParameter ) ); + /* 'uxIndex' will be increased at the end of this loop. */ + } + else + { + ulParameter = 0; + } + + /* Confirm uxIndex is still a valid index after adjustments to uxIndex above */ + if( !( uxIndex < uxPayloadDataLength ) ) + { + break; + } + + /* Option-specific handling. */ + switch( ucOptionCode ) + { + case dhcpIPv4_MESSAGE_TYPE_OPTION_CODE: + + if( pucByte[ uxIndex ] == ( uint8_t ) xExpectedMessageType ) + { + /* The message type is the message type the + * state machine is expecting. */ + ulProcessed++; + } + else + { + if( pucByte[ uxIndex ] == ( uint8_t ) dhcpMESSAGE_TYPE_NACK ) + { + if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_ACK ) + { + /* Start again. */ + EP_DHCPData.eDHCPState = eInitialWait; + } + } + + /* Stop processing further options. */ + uxLength = 0; + } + + break; + + case dhcpIPv4_SUBNET_MASK_OPTION_CODE: + + if( uxLength == sizeof( uint32_t ) ) + { + EP_IPv4_SETTINGS.ulNetMask = ulParameter; + } + + break; + + case dhcpIPv4_GATEWAY_OPTION_CODE: + + /* The DHCP server may send more than 1 gateway addresses. */ + if( uxLength >= sizeof( uint32_t ) ) + { + /* ulProcessed is not incremented in this case + * because the gateway is not essential. */ + EP_IPv4_SETTINGS.ulGatewayAddress = ulParameter; + } + + break; + + case dhcpIPv4_DNS_SERVER_OPTIONS_CODE: + + /* The DHCP server may send more than 1 DNS server addresses. */ + if( uxLength >= sizeof( uint32_t ) ) + { + /* ulProcessed is not incremented in this case + * because the DNS server is not essential. Only the + * first DNS server address is taken. */ + EP_IPv4_SETTINGS.ulDNSServerAddress = ulParameter; + } + + break; + + case dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE: + + if( uxLength == sizeof( uint32_t ) ) + { + if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_OFFER ) + { + /* Offers state the replying server. */ + ulProcessed++; + EP_DHCPData.ulDHCPServerAddress = ulParameter; + } + else + { + /* The ack must come from the expected server. */ + if( EP_DHCPData.ulDHCPServerAddress == ulParameter ) + { + ulProcessed++; + } + } + } + + break; + + case dhcpIPv4_LEASE_TIME_OPTION_CODE: + + if( uxLength == sizeof( EP_DHCPData.ulLeaseTime ) ) + { + /* ulProcessed is not incremented in this case + * because the lease time is not essential. */ + + /* The DHCP parameter is in seconds, convert + * to host-endian format. */ + EP_DHCPData.ulLeaseTime = FreeRTOS_ntohl( ulParameter ); + + /* Divide the lease time by two to ensure a renew + * request is sent before the lease actually expires. */ + EP_DHCPData.ulLeaseTime >>= 1; + + /* Multiply with configTICK_RATE_HZ to get clock ticks. */ + EP_DHCPData.ulLeaseTime = ( uint32_t ) configTICK_RATE_HZ * ( uint32_t ) EP_DHCPData.ulLeaseTime; + } + + break; + + default: + + /* Not interested in this field. */ + + break; + } + + /* Jump over the data to find the next option code. */ + if( uxLength == 0U ) + { + break; + } + + uxIndex = uxIndex + uxLength; + } + + /* Were all the mandatory options received? */ + if( ulProcessed >= ulMandatoryOptions ) + { + /* HT:endian: used to be network order */ + EP_DHCPData.ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr; + FreeRTOS_printf( ( "vDHCPProcess: offer %xip\n", ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) ); + xReturn = pdPASS; + } + } + } + + FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayload ); + } /* if( lBytes > 0 ) */ + + return xReturn; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Create a partial DHCP message by filling in all the 'constant' fields. + * + * @param[out] pxAddress: Address to be filled in. + * @param[out] xOpcode: Opcode to be filled in the packet. Will always be 'dhcpREQUEST_OPCODE'. + * @param[in] pucOptionsArray: The options to be added to the packet. + * @param[in,out] pxOptionsArraySize: Byte count of the options. Its value might change. + * + * @return Ethernet buffer of the partially created DHCP packet. + */ + static uint8_t * prvCreatePartDHCPMessage( struct freertos_sockaddr * pxAddress, + BaseType_t xOpcode, + const uint8_t * const pucOptionsArray, + size_t * pxOptionsArraySize ) + { + DHCPMessage_IPv4_t * pxDHCPMessage; + size_t uxRequiredBufferSize = sizeof( DHCPMessage_IPv4_t ) + *pxOptionsArraySize; + const NetworkBufferDescriptor_t * pxNetworkBuffer; + uint8_t * pucUDPPayloadBuffer = NULL; + + #if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) + const char * pucHostName = pcApplicationHostnameHook(); + size_t uxNameLength = strlen( pucHostName ); + uint8_t * pucPtr; + +/* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + /* Two extra bytes for option code and length. */ + uxRequiredBufferSize += ( 2U + uxNameLength ); + #endif /* if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) */ + + /* Obtain a network buffer with the required amount of storage. It doesn't make much sense + * to use a time-out here, because that would cause the IP-task to wait for itself. */ + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( sizeof( UDPPacket_t ) + uxRequiredBufferSize, 0U ); + + if( pxNetworkBuffer != NULL ) + { + /* Leave space for the UDP header. */ + pucUDPPayloadBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ); + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxDHCPMessage = ( ( DHCPMessage_IPv4_t * ) pucUDPPayloadBuffer ); + + /* Most fields need to be zero. */ + ( void ) memset( pxDHCPMessage, 0x00, sizeof( DHCPMessage_IPv4_t ) ); + + /* Create the message. */ + pxDHCPMessage->ucOpcode = ( uint8_t ) xOpcode; + pxDHCPMessage->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET; + pxDHCPMessage->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH; + pxDHCPMessage->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId ); + pxDHCPMessage->ulDHCPCookie = ( uint32_t ) dhcpCOOKIE; + + if( EP_DHCPData.xUseBroadcast != pdFALSE ) + { + pxDHCPMessage->usFlags = ( uint16_t ) dhcpBROADCAST; + } + else + { + pxDHCPMessage->usFlags = 0U; + } + + ( void ) memcpy( &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) ); + + /* Copy in the const part of the options options. */ + ( void ) memcpy( &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), pucOptionsArray, *pxOptionsArraySize ); + + #if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) + { + /* With this option, the hostname can be registered as well which makes + * it easier to lookup a device in a router's list of DHCP clients. */ + + /* Point to where the OPTION_END was stored to add data. */ + pucPtr = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + ( *pxOptionsArraySize - 1U ) ] ); + pucPtr[ 0U ] = dhcpIPv4_DNS_HOSTNAME_OPTIONS_CODE; + pucPtr[ 1U ] = ( uint8_t ) uxNameLength; + + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = pucHostName; + pvCopyDest = &pucPtr[ 2U ]; + + ( void ) memcpy( pvCopyDest, pvCopySource, uxNameLength ); + pucPtr[ 2U + uxNameLength ] = ( uint8_t ) dhcpOPTION_END_BYTE; + *pxOptionsArraySize += ( size_t ) ( 2U + uxNameLength ); + } + #endif /* if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) */ + + /* Map in the client identifier. */ + ( void ) memcpy( &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ), + ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) ); + + /* Set the addressing. */ + pxAddress->sin_addr = ipBROADCAST_IP_ADDRESS; + pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT_IPv4; + } + + return pucUDPPayloadBuffer; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Create and send a DHCP request message through the DHCP socket. + * @return Returns pdPASS when the message is successfully created and sent. + */ + static BaseType_t prvSendDHCPRequest( void ) + { + BaseType_t xResult = pdFAIL; + uint8_t * pucUDPPayloadBuffer; + struct freertos_sockaddr xAddress; + static const uint8_t ucDHCPRequestOptions[] = + { + /* Do not change the ordering without also changing + * dhcpCLIENT_IDENTIFIER_OFFSET, dhcpREQUESTED_IP_ADDRESS_OFFSET and + * dhcpDHCP_SERVER_IP_ADDRESS_OFFSET. */ + dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_REQUEST, /* Message type option. */ + dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 7, 1, 0, 0, 0, 0, 0, 0, /* Client identifier. */ + dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */ + dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address of the DHCP server. */ + dhcpOPTION_END_BYTE + }; + size_t uxOptionsLength = sizeof( ucDHCPRequestOptions ); + /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, + ( BaseType_t ) dhcpREQUEST_OPCODE, + ucDHCPRequestOptions, + &( uxOptionsLength ) ); + + if( pucUDPPayloadBuffer != NULL ) + { + /* Copy in the IP address being requested. */ + + /* + * Use helper variables for memcpy() source & dest to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = &EP_DHCPData.ulOfferedIPAddress; + pvCopyDest = &pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ]; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulOfferedIPAddress ) ); + + /* Copy in the address of the DHCP server being used. */ + pvCopySource = &EP_DHCPData.ulDHCPServerAddress; + pvCopyDest = &pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ]; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulDHCPServerAddress ) ); + + FreeRTOS_debug_printf( ( "vDHCPProcess: reply %xip\n", ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) ); + iptraceSENDING_DHCP_REQUEST(); + + if( FreeRTOS_sendto( xDHCPSocket, pucUDPPayloadBuffer, sizeof( DHCPMessage_IPv4_t ) + uxOptionsLength, FREERTOS_ZERO_COPY, &xAddress, ( socklen_t ) sizeof( xAddress ) ) == 0 ) + { + /* The packet was not successfully queued for sending and must be + * returned to the stack. */ + FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); + } + else + { + xResult = pdPASS; + } + } + + return xResult; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Create and send a DHCP discover packet through the DHCP socket. + * @return Returns pdPASS when the message is successfully created and sent. + */ + static BaseType_t prvSendDHCPDiscover( void ) + { + BaseType_t xResult = pdFAIL; + uint8_t * pucUDPPayloadBuffer; + struct freertos_sockaddr xAddress; + static const uint8_t ucDHCPDiscoverOptions[] = + { + /* Do not change the ordering without also changing dhcpCLIENT_IDENTIFIER_OFFSET. */ + dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_DISCOVER, /* Message type option. */ + dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 7, 1, 0, 0, 0, 0, 0, 0, /* Client identifier. */ + dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */ + dhcpIPv4_PARAMETER_REQUEST_OPTION_CODE, 3, dhcpIPv4_SUBNET_MASK_OPTION_CODE, dhcpIPv4_GATEWAY_OPTION_CODE, dhcpIPv4_DNS_SERVER_OPTIONS_CODE, /* Parameter request option. */ + dhcpOPTION_END_BYTE + }; + size_t uxOptionsLength = sizeof( ucDHCPDiscoverOptions ); + + pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, + ( BaseType_t ) dhcpREQUEST_OPCODE, + ucDHCPDiscoverOptions, + &( uxOptionsLength ) ); + + if( pucUDPPayloadBuffer != NULL ) + { + const void * pvCopySource; + void * pvCopyDest; + + FreeRTOS_debug_printf( ( "vDHCPProcess: discover\n" ) ); + iptraceSENDING_DHCP_DISCOVER(); + + if( xDHCPData.ulPreferredIPAddress != 0U ) + { + /* Fill in the IPv4 address. */ + pvCopySource = &xDHCPData.ulPreferredIPAddress; + pvCopyDest = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] ); + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulPreferredIPAddress ) ); + } + else + { + /* Remove option-50 from the list because it is not used. */ + size_t uxCopyLength; + /* Exclude this line from branch coverage as the not-taken condition will never happen unless the code is modified */ + configASSERT( uxOptionsLength > ( dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ) ); /* LCOV_EXCL_BR_LINE */ + uxCopyLength = uxOptionsLength - ( dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ); + pvCopySource = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ] ); + pvCopyDest = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpOPTION_50_OFFSET ] ); + ( void ) memmove( pvCopyDest, pvCopySource, uxCopyLength ); + /* Send 6 bytes less than foreseen. */ + uxOptionsLength -= dhcpOPTION_50_SIZE; + } + + if( FreeRTOS_sendto( xDHCPSocket, + pucUDPPayloadBuffer, + sizeof( DHCPMessage_IPv4_t ) + uxOptionsLength, + FREERTOS_ZERO_COPY, + &( xAddress ), + ( socklen_t ) sizeof( xAddress ) ) == 0 ) + { + /* The packet was not successfully queued for sending and must be + * returned to the stack. */ + FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); + } + else + { + xResult = pdTRUE; + } + } + + return xResult; + } + /*-----------------------------------------------------------*/ + + + #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + +/** + * @brief When DHCP has failed, the code can assign a Link-Layer address, and check if + * another device already uses the IP-address. + */ + static void prvPrepareLinkLayerIPLookUp( void ) + { + uint8_t ucLinkLayerIPAddress[ 2 ]; + uint32_t ulNumbers[ 2 ]; + + /* After DHCP has failed to answer, prepare everything to start + * trying-out LinkLayer IP-addresses, using the random method. */ + EP_DHCPData.xDHCPTxTime = xTaskGetTickCount(); + + xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); + xApplicationGetRandomNumber( &( ulNumbers[ 1 ] ) ); + ucLinkLayerIPAddress[ 0 ] = ( uint8_t ) 1 + ( uint8_t ) ( ulNumbers[ 0 ] % 0xFDU ); /* get value 1..254 for IP-address 3rd byte of IP address to try. */ + ucLinkLayerIPAddress[ 1 ] = ( uint8_t ) 1 + ( uint8_t ) ( ulNumbers[ 1 ] % 0xFDU ); /* get value 1..254 for IP-address 4th byte of IP address to try. */ + + EP_IPv4_SETTINGS.ulGatewayAddress = 0U; + + /* prepare xDHCPData with data to test. */ + EP_DHCPData.ulOfferedIPAddress = + FreeRTOS_inet_addr_quick( LINK_LAYER_ADDRESS_0, LINK_LAYER_ADDRESS_1, ucLinkLayerIPAddress[ 0 ], ucLinkLayerIPAddress[ 1 ] ); + + EP_DHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; /* don't care about lease time. just put anything. */ + + EP_IPv4_SETTINGS.ulNetMask = + FreeRTOS_inet_addr_quick( LINK_LAYER_NETMASK_0, LINK_LAYER_NETMASK_1, LINK_LAYER_NETMASK_2, LINK_LAYER_NETMASK_3 ); + + /* DHCP completed. The IP address can now be used, and the + * timer set to the lease timeout time. */ + *( ipLOCAL_IP_ADDRESS_POINTER ) = EP_DHCPData.ulOfferedIPAddress; + + /* Setting the 'local' broadcast address, something like 192.168.1.255' */ + EP_IPv4_SETTINGS.ulBroadcastAddress = ( EP_DHCPData.ulOfferedIPAddress & EP_IPv4_SETTINGS.ulNetMask ) | ~EP_IPv4_SETTINGS.ulNetMask; + + /* Close socket to ensure packets don't queue on it. not needed anymore as DHCP failed. but still need timer for ARP testing. */ + prvCloseDHCPSocket(); + + xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); + EP_DHCPData.xDHCPTxPeriod = pdMS_TO_TICKS( 3000U + ( ulNumbers[ 0 ] & 0x3ffU ) ); /* do ARP test every (3 + 0-1024mS) seconds. */ + + xARPHadIPClash = pdFALSE; /* reset flag that shows if have ARP clash. */ + vARPSendGratuitous(); + } + + #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ +/*-----------------------------------------------------------*/ + +#endif /* ipconfigUSE_DHCP != 0 */ diff --git a/FreeRTOS/source/FreeRTOS_DNS.c b/FreeRTOS/source/FreeRTOS_DNS.c new file mode 100644 index 0000000..87ca5c9 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_DNS.c @@ -0,0 +1,833 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_DNS.c + * @brief Implements the Domain Name System for the FreeRTOS+TCP network stack. + */ + +/* Standard includes. */ +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_DNS_Globals.h" +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DNS.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "FreeRTOS_DNS_Cache.h" +#include "FreeRTOS_DNS_Parser.h" +#include "FreeRTOS_DNS_Networking.h" +#include "FreeRTOS_DNS_Callback.h" + + +/* Exclude the entire file if DNS is not enabled. */ +#if ( ipconfigUSE_DNS != 0 ) + +/* + * Create the DNS message in the zero copy buffer passed in the first parameter. + */ + _static size_t prvCreateDNSMessage( uint8_t * pucUDPPayloadBuffer, + const char * pcHostName, + TickType_t uxIdentifier ); + + +/* + * Check if hostname is already known. If not, call prvGetHostByName() to send a DNS request. + */ + #if ( ipconfigDNS_USE_CALLBACKS == 1 ) + static uint32_t prvPrepareLookup( const char * pcHostName, + FOnDNSEvent pCallback, + void * pvSearchID, + TickType_t uxTimeout ); + #else + static uint32_t prvPrepareLookup( const char * pcHostName ); + #endif + +/* + * Prepare and send a message to a DNS server. 'uxReadTimeOut_ticks' will be passed as + * zero, in case the user has supplied a call-back function. + */ + static uint32_t prvGetHostByName( const char * pcHostName, + TickType_t uxIdentifier, + TickType_t uxReadTimeOut_ticks ); + + #if ( ipconfigUSE_LLMNR == 1 ) + /** @brief The MAC address used for LLMNR. */ + const MACAddress_t xLLMNR_MacAdress = { { 0x01, 0x00, 0x5e, 0x00, 0x00, 0xfc } }; + #endif /* ipconfigUSE_LLMNR == 1 */ + +/*-----------------------------------------------------------*/ + +/** + * @brief A DNS query consists of a header, as described in 'struct xDNSMessage' + * It is followed by 1 or more queries, each one consisting of a name and a tail, + * with two fields: type and class + */ + #include "pack_struct_start.h" + struct xDNSTail + { + uint16_t usType; /**< Type of DNS message. */ + uint16_t usClass; /**< Class of DNS message. */ + } + #include "pack_struct_end.h" + typedef struct xDNSTail DNSTail_t; +/*-----------------------------------------------------------*/ + + + #if ( ipconfigDNS_USE_CALLBACKS == 1 ) + +/** + * @brief Define FreeRTOS_gethostbyname() as a normal blocking call. + * @param[in] pcHostName: The hostname whose IP address is being searched for. + * @return The IP-address of the hostname. + */ + uint32_t FreeRTOS_gethostbyname( const char * pcHostName ) + { + return FreeRTOS_gethostbyname_a( pcHostName, NULL, ( void * ) NULL, 0U ); + } + /*-----------------------------------------------------------*/ + +/** @brief Initialise the list of call-back structures. + */ + void vDNSInitialise( void ) + { + vDNSCallbackInitialise(); + } + /*-----------------------------------------------------------*/ + + +/** + * @brief Remove the entry defined by the search ID to cancel a DNS request. + * @param[in] pvSearchID: The search ID of the callback function associated with + * the DNS request being cancelled. Note that the value of + * the pointer matters, not the pointee. + */ + void FreeRTOS_gethostbyname_cancel( void * pvSearchID ) + { + /* _HT_ Should better become a new API call to have the IP-task remove the callback */ + vDNSCheckCallBack( pvSearchID ); + } + /*-----------------------------------------------------------*/ + +/*-----------------------------------------------------------*/ + + + #endif /* ipconfigDNS_USE_CALLBACKS == 1 */ + /*-----------------------------------------------------------*/ + + #if ( ipconfigDNS_USE_CALLBACKS == 0 ) + +/** + * @brief Get the IP-address corresponding to the given hostname. + * @param[in] pcHostName: The hostname whose IP address is being queried. + * @return The IP-address corresponding to the hostname. 0 is returned in + * case of failure. + */ + uint32_t FreeRTOS_gethostbyname( const char * pcHostName ) + { + return prvPrepareLookup( pcHostName ); + } + #else + +/** + * @brief Get the IP-address corresponding to the given hostname. + * @param[in] pcHostName: The hostname whose IP address is being queried. + * @param[in] pCallback: The callback function which will be called upon DNS response. + * @param[in] pvSearchID: Search ID for the callback function. + * @param[in] uxTimeout: Timeout for the callback function. + * @return The IP-address corresponding to the hostname. 0 is returned in case of + * failure. + */ + uint32_t FreeRTOS_gethostbyname_a( const char * pcHostName, + FOnDNSEvent pCallback, + void * pvSearchID, + TickType_t uxTimeout ) + { + return prvPrepareLookup( pcHostName, pCallback, pvSearchID, uxTimeout ); + } + #endif /* if ( ipconfigDNS_USE_CALLBACKS == 0 ) */ + + #if ( ipconfigDNS_USE_CALLBACKS == 1 ) + +/** + * @brief Check if hostname is already known. If not, call prvGetHostByName() to send a DNS request. + * + * @param[in] pcHostName: The hostname whose IP address is being queried. + * @param[in] pCallback: The callback function which will be called upon DNS response. + * @param[in] pvSearchID: Search ID for the callback function. + * @param[in] uxTimeout: Timeout for the callback function. + * @return The IP-address corresponding to the hostname. + */ + static uint32_t prvPrepareLookup( const char * pcHostName, + FOnDNSEvent pCallback, + void * pvSearchID, + TickType_t uxTimeout ) + #else + +/** + * @brief Check if hostname is already known. If not, call prvGetHostByName() to send a DNS request. + * @param[in] pcHostName: The hostname whose IP address is being queried. + * @return The IP-address corresponding to the hostname. + */ + static uint32_t prvPrepareLookup( const char * pcHostName ) + #endif + { + uint32_t ulIPAddress = 0U; + TickType_t uxReadTimeOut_ticks = ipconfigDNS_RECEIVE_BLOCK_TIME_TICKS; + + /* Generate a unique identifier for this query. Keep it in a local variable + * as gethostbyname() may be called from different threads */ + BaseType_t xHasRandom = pdFALSE; + TickType_t uxIdentifier = 0U; + + BaseType_t xLengthOk = pdFALSE; + + if( pcHostName != NULL ) + { + size_t xLength = strlen( pcHostName ) + 1U; + + #if ( ipconfigUSE_DNS_CACHE != 0 ) + if( xLength <= ipconfigDNS_CACHE_NAME_LENGTH ) + #else + if( xLength <= dnsMAX_HOSTNAME_LENGTH ) + #endif + { + /* The name is not too long. */ + xLengthOk = pdTRUE; + } + else + { + FreeRTOS_printf( ( "prvPrepareLookup: name is too long ( %lu > %lu )\n", + ( uint32_t ) xLength, + ( uint32_t ) ipconfigDNS_CACHE_NAME_LENGTH ) ); + } + } + + if( ( pcHostName != NULL ) && ( xLengthOk != pdFALSE ) ) + { + /* If the supplied hostname is IP address, convert it to uint32_t + * and return. */ + #if ( ipconfigINCLUDE_FULL_INET_ADDR == 1 ) + { + ulIPAddress = FreeRTOS_inet_addr( pcHostName ); + } + #endif /* ipconfigINCLUDE_FULL_INET_ADDR == 1 */ + + #if ( ipconfigUSE_DNS_CACHE == 1 ) + /* Check the cache before issuing another DNS request. */ + if( ulIPAddress == 0U ) + { + ulIPAddress = FreeRTOS_dnslookup( pcHostName ); + + if( ulIPAddress != 0U ) + { + FreeRTOS_debug_printf( ( "FreeRTOS_gethostbyname: found '%s' in cache: %lxip\n", pcHostName, ulIPAddress ) ); + } + else + { + /* prvGetHostByName will be called to start a DNS lookup. */ + } + } + #endif /* if ( ipconfigUSE_DNS_CACHE == 1 ) */ + + /* Generate a unique identifier. */ + if( ulIPAddress == 0U ) + { + uint32_t ulNumber; + + xHasRandom = xApplicationGetRandomNumber( &( ulNumber ) ); + /* DNS identifiers are 16-bit. */ + uxIdentifier = ( TickType_t ) ( ulNumber & 0xffffU ); + } + + #if ( ipconfigDNS_USE_CALLBACKS == 1 ) + { + if( pCallback != NULL ) + { + if( ulIPAddress == 0U ) + { + /* The user has provided a callback function, so do not block on recvfrom() */ + if( xHasRandom != pdFALSE ) + { + uxReadTimeOut_ticks = 0U; + vDNSSetCallBack( pcHostName, + pvSearchID, + pCallback, + uxTimeout, + uxIdentifier ); + } + } + else + { + /* The IP address is known, do the call-back now. */ + pCallback( pcHostName, pvSearchID, ulIPAddress ); + } + } + } + #endif /* if ( ipconfigDNS_USE_CALLBACKS == 1 ) */ + + if( ( ulIPAddress == 0U ) && ( xHasRandom != pdFALSE ) ) + { + ulIPAddress = prvGetHostByName( pcHostName, + uxIdentifier, + uxReadTimeOut_ticks ); + } + } + + return ulIPAddress; + } + /*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_LLMNR == 1 ) + +/*! + * @brief If LLMNR is being used then determine if the host name includes a '.' + * if not then LLMNR can be used as the lookup method. + * @param [in] pcHostName hostname to check + * @returns true if the hostname is a dotted format, else false + * + */ + static BaseType_t llmnr_has_dot( const char * pcHostName ) + { + BaseType_t bHasDot = pdFALSE; + const char * pucPtr; + + for( pucPtr = pcHostName; *pucPtr != ( char ) 0; pucPtr++ ) + { + if( *pucPtr == '.' ) + { + bHasDot = pdTRUE; + break; + } + } + + return bHasDot; + } + #endif /* if ( ipconfigUSE_LLMNR == 1 ) */ + +/*! + * @brief create a payload buffer and return it through the parameter + * @param [out] ppxNetworkBuffer network buffer to create + * @param [in] pcHostName hostname to get its length + * @returns pointer address to the payload buffer + * + */ + static uint8_t * prvGetPayloadBuffer( NetworkBufferDescriptor_t ** ppxNetworkBuffer, + const char * pcHostName ) + { + size_t uxExpectedPayloadLength; + uint8_t * pucUDPPayloadBuffer = NULL; + size_t uxHeaderBytes; + + uxHeaderBytes = ipSIZE_OF_ETH_HEADER + + ipSIZE_OF_IPv4_HEADER + + ipSIZE_OF_UDP_HEADER; + + uxExpectedPayloadLength = sizeof( DNSMessage_t ) + + strlen( pcHostName ) + + sizeof( uint16_t ) + + sizeof( uint16_t ) + 2U; + + /* Get a buffer. This uses a maximum delay, but the delay will be + * capped to ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS so the return value + * still needs to be tested. */ + *ppxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxExpectedPayloadLength + + uxHeaderBytes, + 0U ); + + if( *ppxNetworkBuffer != NULL ) + { + pucUDPPayloadBuffer = &( ( *ppxNetworkBuffer )->pucEthernetBuffer[ uxHeaderBytes ] ); + } + + return pucUDPPayloadBuffer; + } + +/*! + * @brief fill pxAddress from pucUDPPayloadBuffer + * @param [out] pxAddress ip address and port ... structure + * @param [in] pcHostName hostname to get its length + */ + static void prvFillSockAddress( struct freertos_sockaddr * pxAddress, + const char * pcHostName ) + { + uint32_t ulIPAddress = 0U; + + #if ( ipconfigUSE_LLMNR != 1 ) + ( void ) pcHostName; + #endif + + /* Obtain the DNS server address. */ + FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress ); + #if ( ipconfigUSE_LLMNR == 1 ) + BaseType_t bHasDot = llmnr_has_dot( pcHostName ); + + if( bHasDot == pdFALSE ) + { + /* Use LLMNR addressing. */ + pxAddress->sin_addr = ipLLMNR_IP_ADDR; /* Is in network byte order. */ + pxAddress->sin_port = ipLLMNR_PORT; + pxAddress->sin_port = FreeRTOS_ntohs( pxAddress->sin_port ); + } + else + #endif /* if ( ipconfigUSE_LLMNR == 1 ) */ + { + /* Use DNS server. */ + pxAddress->sin_addr = ulIPAddress; + pxAddress->sin_port = dnsDNS_PORT; + } + } + +/*! + * @brief return ip address from the dns reply message + * @param [in] pxReceiveBuffer received buffer from the DNS server + * @param [in] uxIdentifier matches sent and received packets + * @returns ip address or zero on error + * + */ + static uint32_t prvDNSReply( const struct xDNSBuffer * pxReceiveBuffer, + TickType_t uxIdentifier ) + { + uint32_t ulIPAddress = 0U; + BaseType_t xExpected; + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const DNSMessage_t * pxDNSMessageHeader = + ( ( const DNSMessage_t * ) + pxReceiveBuffer->pucPayloadBuffer ); + + /* See if the identifiers match. */ + if( uxIdentifier == ( TickType_t ) pxDNSMessageHeader->usIdentifier ) + { + xExpected = pdTRUE; + } + else + { + xExpected = pdFALSE; + } + + /* The reply was received. Process it. */ + #if ( ipconfigDNS_USE_CALLBACKS == 0 ) + + /* It is useless to analyse the unexpected reply + * unless asynchronous look-ups are enabled. */ + if( xExpected != pdFALSE ) + #endif /* ipconfigDNS_USE_CALLBACKS == 0 */ + { + ulIPAddress = DNS_ParseDNSReply( pxReceiveBuffer->pucPayloadBuffer, + pxReceiveBuffer->uxPayloadLength, + xExpected ); + } + + return ulIPAddress; + } + +/*! + * @brief prepare the buffer before sending + * @param [in] pcHostName + * @param [in] uxIdentifier matches sent and received packets + * @param [in] xDNSSocket a valid socket + * @param [in] pxAddress address structure + * @returns pdTRUE if sending the data was successful, pdFALSE otherwise. + */ + static BaseType_t prvSendBuffer( const char * pcHostName, + TickType_t uxIdentifier, + Socket_t xDNSSocket, + const struct freertos_sockaddr * pxAddress ) + { + BaseType_t uxReturn = pdFAIL; + struct xDNSBuffer xDNSBuf = { 0 }; + NetworkBufferDescriptor_t * pxNetworkBuffer = NULL; + + /*get dns message buffer */ + xDNSBuf.pucPayloadBuffer = prvGetPayloadBuffer( &pxNetworkBuffer, + pcHostName ); + + if( xDNSBuf.pucPayloadBuffer != NULL ) + { + xDNSBuf.uxPayloadSize = pxNetworkBuffer->xDataLength; + + #if ( ipconfigUSE_LLMNR == 1 ) + { + if( FreeRTOS_ntohs( pxAddress->sin_port ) == ipLLMNR_PORT ) + { + ( ( ( DNSMessage_t * ) xDNSBuf.pucPayloadBuffer ) )->usFlags = 0; + } + } + #endif + + xDNSBuf.uxPayloadLength = prvCreateDNSMessage( xDNSBuf.pucPayloadBuffer, + pcHostName, + uxIdentifier ); + + /* send the dns message */ + uxReturn = DNS_SendRequest( xDNSSocket, + pxAddress, + &xDNSBuf ); + + if( uxReturn == pdFAIL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + } + + return uxReturn; + } + +/*! + * @brief main dns operation description function + * @param [in] pcHostName hostname to get its ip address + * @param [in] uxIdentifier Identifier to match sent and received packets + * @param [in] xDNSSocket socket + * @returns ip address or zero on error + */ + static uint32_t prvGetHostByNameOp( const char * pcHostName, + TickType_t uxIdentifier, + Socket_t xDNSSocket ) + { + uint32_t ulIPAddress = 0; + + struct freertos_sockaddr xAddress; + DNSBuffer_t xReceiveBuffer = { 0 }; + BaseType_t uxReturn = pdFAIL; + + prvFillSockAddress( &xAddress, pcHostName ); + + do + { + uxReturn = prvSendBuffer( pcHostName, + uxIdentifier, + xDNSSocket, + &xAddress ); + + if( uxReturn == pdFAIL ) + { + break; + } + + /* Create the message in the obtained buffer. */ + + /* receive a dns reply message */ + DNS_ReadReply( xDNSSocket, + &xAddress, + &xReceiveBuffer ); + + if( xReceiveBuffer.pucPayloadBuffer == NULL ) + { + break; + } + + ulIPAddress = prvDNSReply( &xReceiveBuffer, uxIdentifier ); + + /* Finished with the buffer. The zero copy interface + * is being used, so the buffer must be freed by the + * task. */ + FreeRTOS_ReleaseUDPPayloadBuffer( xReceiveBuffer.pucPayloadBuffer ); + } while( ipFALSE_BOOL ); + + return ulIPAddress; + } + +/*! + * @brief helper function to prvGetHostByNameOP with multiple retries equal to + * ipconfigDNS_REQUEST_ATTEMPTS + * + * @param [in] pcHostName hostname to get its ip address + * @param [in] uxIdentifier Identifier to match sent and received packets + * @param [in] xDNSSocket socket + * @returns ip address or zero on error + * + */ + static uint32_t prvGetHostByNameOp_WithRetry( const char * pcHostName, + TickType_t uxIdentifier, + Socket_t xDNSSocket ) + { + uint32_t ulIPAddress; + BaseType_t xAttempt; + + for( xAttempt = 0; xAttempt < ipconfigDNS_REQUEST_ATTEMPTS; xAttempt++ ) + { + ulIPAddress = prvGetHostByNameOp( pcHostName, + uxIdentifier, + xDNSSocket ); + + if( ulIPAddress != 0U ) + { /* ip found, no need to retry */ + break; + } + } + + return ulIPAddress; + } + + +/** + * @brief Prepare and send a message to a DNS server. 'uxReadTimeOut_ticks' will be passed as + * zero, in case the user has supplied a call-back function. + * + * @param[in] pcHostName The hostname for which an IP address is required. + * @param[in] uxIdentifier Identifier to match sent and received packets + * @param[in] uxReadTimeOut_ticks The timeout in ticks for waiting. In case the user has supplied + * a call-back function, this value should be zero. + * @return The IPv4 IP address for the hostname being queried. It will be zero if there is no reply. + */ + static uint32_t prvGetHostByName( const char * pcHostName, + TickType_t uxIdentifier, + TickType_t uxReadTimeOut_ticks ) + { + Socket_t xDNSSocket; + uint32_t ulIPAddress = 0U; + + xDNSSocket = DNS_CreateSocket( uxReadTimeOut_ticks ); + + if( xDNSSocket != NULL ) + { + if( uxReadTimeOut_ticks == 0U ) + { + ulIPAddress = prvGetHostByNameOp( pcHostName, + uxIdentifier, + xDNSSocket ); + } + else + { + ulIPAddress = prvGetHostByNameOp_WithRetry( pcHostName, + uxIdentifier, + xDNSSocket ); + } + + /* Finished with the socket. */ + DNS_CloseSocket( xDNSSocket ); + } + + return ulIPAddress; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Create the DNS message in the zero copy buffer passed in the first parameter. + * @param[in,out] pucUDPPayloadBuffer The zero copy buffer where the DNS message will be created. + * @param[in] pcHostName Hostname to be looked up. + * @param[in] uxIdentifier Identifier to match sent and received packets + * @return Total size of the generated message, which is the space from the last written byte + * to the beginning of the buffer. + */ + _static size_t prvCreateDNSMessage( uint8_t * pucUDPPayloadBuffer, + const char * pcHostName, + TickType_t uxIdentifier ) + { + DNSMessage_t * pxDNSMessageHeader; + size_t uxStart, uxIndex; + DNSTail_t const * pxTail; + static const DNSMessage_t xDefaultPartDNSHeader = + { + 0, /* The identifier will be overwritten. */ + dnsOUTGOING_FLAGS, /* Flags set for standard query. */ + dnsONE_QUESTION, /* One question is being asked. */ + 0, /* No replies are included. */ + 0, /* No authorities. */ + 0 /* No additional authorities. */ + }; + + /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + /* Copy in the const part of the header. Intentionally using different + * pointers with memcpy() to put the information in to correct place. */ + + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = &xDefaultPartDNSHeader; + pvCopyDest = pucUDPPayloadBuffer; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( xDefaultPartDNSHeader ) ); + + /* Write in a unique identifier. Cast the Payload Buffer to DNSMessage_t + * to easily access fields of the DNS Message. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxDNSMessageHeader = ( ( DNSMessage_t * ) pucUDPPayloadBuffer ); + pxDNSMessageHeader->usIdentifier = ( uint16_t ) uxIdentifier; + + /* Create the resource record at the end of the header. First + * find the end of the header. */ + uxStart = sizeof( xDefaultPartDNSHeader ); + + /* Leave a gap for the first length byte. */ + uxIndex = uxStart + 1U; + + /* Copy in the host name. */ + ( void ) strcpy( ( char * ) &( pucUDPPayloadBuffer[ uxIndex ] ), pcHostName ); + + /* Walk through the string to replace the '.' characters with byte + * counts. pucStart holds the address of the byte count. Walking the + * string starts after the byte count position. */ + uxIndex = uxStart; + + do + { + size_t uxLength; + + /* Skip the length byte. */ + uxIndex++; + + while( ( pucUDPPayloadBuffer[ uxIndex ] != ( uint8_t ) 0U ) && + ( pucUDPPayloadBuffer[ uxIndex ] != ( uint8_t ) ASCII_BASELINE_DOT ) ) + { + uxIndex++; + } + + /* Fill in the byte count, then move the pucStart pointer up to + * the found byte position. */ + uxLength = uxIndex - ( uxStart + 1U ); + pucUDPPayloadBuffer[ uxStart ] = ( uint8_t ) uxLength; + + uxStart = uxIndex; + } while( pucUDPPayloadBuffer[ uxIndex ] != ( uint8_t ) 0U ); + + /* Finish off the record. Cast the record onto DNSTail_t structure to easily + * access the fields of the DNS Message. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxTail = ( ( DNSTail_t * ) &( pucUDPPayloadBuffer[ uxStart + 1U ] ) ); + + #if defined( _lint ) || defined( __COVERITY__ ) + ( void ) pxTail; + #else + vSetField16( pxTail, DNSTail_t, usType, dnsTYPE_A_HOST ); + vSetField16( pxTail, DNSTail_t, usClass, dnsCLASS_IN ); + #endif + + /* Return the total size of the generated message, which is the space from + * the last written byte to the beginning of the buffer. */ + return uxIndex + sizeof( DNSTail_t ) + 1U; + } + +/*-----------------------------------------------------------*/ + +/* The function below will only be called : + * when ipconfigDNS_USE_CALLBACKS == 1 + * when ipconfigUSE_LLMNR == 1 + * for testing purposes, by the module test_freertos_tcp.c + */ + +/** + * @brief Perform some preliminary checks and then parse the DNS packet. + * + * @param[in] pxNetworkBuffer: The network buffer to be parsed. + * + * @return pdFAIL Always to indicate that the packet was not consumed and must + * be released by the caller. + */ + uint32_t ulDNSHandlePacket( const NetworkBufferDescriptor_t * pxNetworkBuffer ) + { + uint8_t * pucPayLoadBuffer; + size_t uxPayloadSize; + + /* Only proceed if the payload length indicated in the header + * appears to be valid. */ + if( pxNetworkBuffer->xDataLength >= sizeof( UDPPacket_t ) ) + { + uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t ); + + if( uxPayloadSize >= sizeof( DNSMessage_t ) ) + { + pucPayLoadBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( UDPPacket_t ) ] ); + + /* The parameter pdFALSE indicates that the reply was not expected. */ + ( void ) DNS_ParseDNSReply( pucPayLoadBuffer, + uxPayloadSize, + pdFALSE ); + } + } + + /* The packet was not consumed. */ + return pdFAIL; + } + /*-----------------------------------------------------------*/ + + + #if ( ipconfigUSE_NBNS == 1 ) + +/** + * @brief Handle an NBNS packet. + * + * @param[in] pxNetworkBuffer: The network buffer holding the NBNS packet. + * + * @return pdFAIL to show that the packet was not consumed. + */ + uint32_t ulNBNSHandlePacket( NetworkBufferDescriptor_t * pxNetworkBuffer ) + { + UDPPacket_t * pxUDPPacket = ( ( UDPPacket_t * ) + pxNetworkBuffer->pucEthernetBuffer ); + uint8_t * pucUDPPayloadBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( *pxUDPPacket ) ] ); + + size_t uxBytesNeeded = sizeof( UDPPacket_t ) + sizeof( NBNSRequest_t ); + + /* Check for minimum buffer size. */ + if( pxNetworkBuffer->xDataLength >= uxBytesNeeded ) + { + DNS_TreatNBNS( pucUDPPayloadBuffer, + pxNetworkBuffer->xDataLength, + pxUDPPacket->xIPHeader.ulSourceIPAddress ); + } + + /* The packet was not consumed. */ + return pdFAIL; + } + + #endif /* ipconfigUSE_NBNS */ + +/*-----------------------------------------------------------*/ + +#endif /* ipconfigUSE_DNS != 0 */ + +/*-----------------------------------------------------------*/ + +/* Provide access to private members for testing. */ +#ifdef FREERTOS_ENABLE_UNIT_TESTS + #include "freertos_tcp_test_access_dns_define.h" +#endif diff --git a/FreeRTOS/source/FreeRTOS_DNS_Cache.c b/FreeRTOS/source/FreeRTOS_DNS_Cache.c new file mode 100644 index 0000000..d329826 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_DNS_Cache.c @@ -0,0 +1,371 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_DNS_Cache.c + * @brief File that handles the DNS caching option + */ + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "FreeRTOS_DNS_Cache.h" +#include "FreeRTOS_DNS_Globals.h" + +/* Standard includes. */ +#include +#include +#include + +#if ( ( ipconfigUSE_DNS != 0 ) && ( ipconfigUSE_DNS_CACHE == 1 ) ) + +/** + * @brief cache entry format structure + */ + typedef struct xDNS_CACHE_TABLE_ROW + { + uint32_t ulIPAddresses[ ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ]; /*!< The IP address(es) of an ARP cache entry. */ + char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ]; /*!< The name of the host */ + uint32_t ulTTL; /*!< Time-to-Live (in seconds) from the DNS server. */ + uint32_t ulTimeWhenAddedInSeconds; /*!< time at which the entry was added */ + #if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 ) + uint8_t ucNumIPAddresses; /*!< number of ip addresses for the same entry */ + uint8_t ucCurrentIPAddress; /*!< current ip address index */ + #endif + } DNSCacheRow_t; + +/*! + * @brief DNS cache structure instantiation + */ + static DNSCacheRow_t xDNSCache[ ipconfigDNS_CACHE_ENTRIES ] = { 0x0 }; + +/*! + * @brief indicates the index of a free entry in the cache structure + * \a DNSCacheRow_t + */ + static UBaseType_t uxFreeEntry = 0U; + + + + static BaseType_t prvFindEntryIndex( const char * pcName, + UBaseType_t * uxResult ); + + static BaseType_t prvGetCacheIPEntry( UBaseType_t uxIndex, + uint32_t * pulIP, + uint32_t ulCurrentTimeSeconds ); + + static void prvUpdateCacheEntry( UBaseType_t uxIndex, + uint32_t ulTTL, + const uint32_t * pulIP, + uint32_t ulCurrentTimeSeconds ); + + static void prvInsertCacheEntry( const char * pcName, + uint32_t ulTTL, + const uint32_t * pulIP, + uint32_t ulCurrentTimeSeconds ); + +/** + * @brief perform a dns lookup in the local cache + * @param pcHostName the lookup name + * @return ulIPAddress with the value from the cache else returns a zero if the + * cache is not enabled or the lookup is not successful + * @post the global structure \a xDNSCache might be modified + */ + uint32_t FreeRTOS_dnslookup( const char * pcHostName ) + { + uint32_t ulIPAddress = 0U; + + ( void ) FreeRTOS_ProcessDNSCache( pcHostName, + &ulIPAddress, + 0, + pdTRUE ); + + return ulIPAddress; + } + +/** + * @brief perform a dns update in the local cache + * @param pcName the lookup name + * @param pulIP the ip value to insert/replace + * @param ulTTL Time To live (in seconds) + * @return this is a dummy return, we are actually ignoring the return value + * from this function + * @post the global structure \a xDNSCache might be modified + */ + BaseType_t FreeRTOS_dns_update( const char * pcName, + uint32_t * pulIP, + uint32_t ulTTL ) + { + ( void ) FreeRTOS_ProcessDNSCache( pcName, + pulIP, + ulTTL, + pdFALSE ); + return pdTRUE; + } + +/** + * @brief perform a dns clear in the local cache + * @post the global structure \a xDNSCache is modified + */ + void FreeRTOS_dnsclear( void ) + { + ( void ) memset( xDNSCache, 0x0, sizeof( xDNSCache ) ); + uxFreeEntry = 0U; + } + +/** + * @brief process a DNS Cache request (get, update, or insert) + * + * @param[in] pcName: the name of the host + * @param[in,out] pulIP: when doing a lookup, will be set, when doing an update, + * will be read. + * @param[in] ulTTL: Time To Live (in seconds) + * @param[in] xLookUp: pdTRUE if a look-up is expected, pdFALSE, when the DNS cache must + * be updated. + * @return whether the operation was successful + * @post the global structure \a xDNSCache might be modified + */ + BaseType_t FreeRTOS_ProcessDNSCache( const char * pcName, + uint32_t * pulIP, + uint32_t ulTTL, + BaseType_t xLookUp ) + { + UBaseType_t uxIndex; + BaseType_t xResult; + TickType_t xCurrentTickCount = xTaskGetTickCount(); + uint32_t ulCurrentTimeSeconds; + + configASSERT( ( pcName != NULL ) ); + + ulCurrentTimeSeconds = ( xCurrentTickCount / portTICK_PERIOD_MS ) / 1000U; + xResult = prvFindEntryIndex( pcName, &uxIndex ); + + if( xResult == pdTRUE ) + { /* Element found */ + if( xLookUp == pdTRUE ) + { + /* This statement can only be reached when xResult is true; which + * implies that the entry is present and a 'get' operation will result + * in success. Therefore, it is safe to ignore the return value of the + * below function. */ + ( void ) prvGetCacheIPEntry( uxIndex, + pulIP, + ulCurrentTimeSeconds ); + } + else + { + prvUpdateCacheEntry( uxIndex, + ulTTL, + pulIP, + ulCurrentTimeSeconds ); + } + } + else /* Element not Found xResult = pdFALSE */ + { + if( xLookUp == pdTRUE ) + { + *pulIP = 0U; + } + else + { + prvInsertCacheEntry( pcName, + ulTTL, + pulIP, + ulCurrentTimeSeconds ); + } + } + + if( ( xLookUp == pdFALSE ) || ( *pulIP != 0U ) ) + { + FreeRTOS_debug_printf( ( "FreeRTOS_ProcessDNSCache: %s: '%s' @ %xip (TTL %u)\n", + ( xLookUp != 0 ) ? "look-up" : "add", + pcName, + ( unsigned ) FreeRTOS_ntohl( *pulIP ), + ( unsigned ) FreeRTOS_ntohl( ulTTL ) ) ); + } + + return xResult; + } + +/** + * @brief returns the index of the hostname entry in the dns cache. + * @param[in] pcName find it in the cache + * @param [out] xResult index number + * @returns res pdTRUE if index in found else pdFALSE + */ + static BaseType_t prvFindEntryIndex( const char * pcName, + UBaseType_t * uxResult ) + { + BaseType_t xReturn = pdFALSE; + UBaseType_t uxIndex; + + /* For each entry in the DNS cache table. */ + for( uxIndex = 0; uxIndex < ipconfigDNS_CACHE_ENTRIES; uxIndex++ ) + { + if( xDNSCache[ uxIndex ].pcName[ 0 ] == ( char ) 0 ) + { /* empty slot */ + continue; + } + + if( strcmp( xDNSCache[ uxIndex ].pcName, pcName ) == 0 ) + { /* hostname found */ + xReturn = pdTRUE; + *uxResult = uxIndex; + break; + } + } + + return xReturn; + } + +/** + * @brief get entry at \p index from the cache + * @param[in] uxIndex : index in the cache + * @param[out] pulIP fill it with the result + * @param[in] ulCurrentTimeSeconds current time + * @returns \c pdTRUE if the value is valid \c pdFALSE otherwise + * @post the global structure \a xDNSCache might be modified + * + */ + static BaseType_t prvGetCacheIPEntry( UBaseType_t uxIndex, + uint32_t * pulIP, + uint32_t ulCurrentTimeSeconds ) + { + BaseType_t isRead; + uint32_t ulIPAddressIndex = 0; + uint32_t ulAge = ulCurrentTimeSeconds - xDNSCache[ uxIndex ].ulTimeWhenAddedInSeconds; + + /* Confirm that the record is still fresh. + * The field ulTTL was stored as network-endian. */ + if( ulAge < FreeRTOS_ntohl( xDNSCache[ uxIndex ].ulTTL ) ) + { + #if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 ) + uint8_t ucIndex; + + /* The ucCurrentIPAddress value increments without bound and will rollover, */ + /* modulo it by the number of IP addresses to keep it in range. */ + /* Also perform a final modulo by the max number of IP addresses */ + /* per DNS cache entry to prevent out-of-bounds access in the event */ + /* that ucNumIPAddresses has been corrupted. */ + + ucIndex = xDNSCache[ uxIndex ].ucCurrentIPAddress % xDNSCache[ uxIndex ].ucNumIPAddresses; + ucIndex = ucIndex % ( uint8_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY; + ulIPAddressIndex = ucIndex; + + xDNSCache[ uxIndex ].ucCurrentIPAddress++; + #endif /* if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 ) */ + + *pulIP = xDNSCache[ uxIndex ].ulIPAddresses[ ulIPAddressIndex ]; + isRead = pdTRUE; + } + else + { + /* Age out the old cached record. */ + xDNSCache[ uxIndex ].pcName[ 0 ] = ( char ) 0; + isRead = pdFALSE; + } + + return isRead; + } + +/** + * @brief update entry at \p index in the cache + * @param[in] uxIndex : index in the cache + * @param[in] ulTTL time to live (in seconds) + * @param[in] pulIP ip to update the cache with + * @param[in] ulCurrentTimeSeconds current time + * @post the global structure \a xDNSCache is modified + * + */ + static void prvUpdateCacheEntry( UBaseType_t uxIndex, + uint32_t ulTTL, + const uint32_t * pulIP, + uint32_t ulCurrentTimeSeconds ) + { + uint32_t ulIPAddressIndex = 0; + + #if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 ) + if( xDNSCache[ uxIndex ].ucNumIPAddresses < + ( uint8_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ) + { + /* If more answers exist than there are IP address storage + * slots they will overwrite entry 0 */ + ulIPAddressIndex = xDNSCache[ uxIndex ].ucNumIPAddresses; + xDNSCache[ uxIndex ].ucNumIPAddresses++; + } + #endif + xDNSCache[ uxIndex ].ulIPAddresses[ ulIPAddressIndex ] = *pulIP; + xDNSCache[ uxIndex ].ulTTL = ulTTL; + xDNSCache[ uxIndex ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds; + } + +/** + * @brief insert entry in the cache + * @param[in] pcName cache entry key + * @param[in] ulTTL time to live (in seconds) + * @param[in] pulIP ip address + * @param[in] ulCurrentTimeSeconds current time + * @post the global structure \a xDNSCache is modified + */ + static void prvInsertCacheEntry( const char * pcName, + uint32_t ulTTL, + const uint32_t * pulIP, + uint32_t ulCurrentTimeSeconds ) + { + /* Add or update the item. */ + if( strlen( pcName ) < ( size_t ) ipconfigDNS_CACHE_NAME_LENGTH ) + { + ( void ) strcpy( xDNSCache[ uxFreeEntry ].pcName, pcName ); + + xDNSCache[ uxFreeEntry ].ulIPAddresses[ 0 ] = *pulIP; + xDNSCache[ uxFreeEntry ].ulTTL = ulTTL; + xDNSCache[ uxFreeEntry ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds; + #if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 ) + xDNSCache[ uxFreeEntry ].ucNumIPAddresses = 1; + xDNSCache[ uxFreeEntry ].ucCurrentIPAddress = 0; + + /* Initialize all remaining IP addresses in this entry to 0 */ + ( void ) memset( &xDNSCache[ uxFreeEntry ].ulIPAddresses[ 1 ], + 0, + sizeof( xDNSCache[ uxFreeEntry ].ulIPAddresses[ 1 ] ) * + ( ( uint32_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY - 1U ) ); + #endif + uxFreeEntry++; + + if( uxFreeEntry == ipconfigDNS_CACHE_ENTRIES ) + { + uxFreeEntry = 0; + } + } + } + +#endif /* if ( ( ipconfigUSE_DNS != 0 ) && ( ipconfigUSE_DNS_CACHE == 1 ) ) */ diff --git a/FreeRTOS/source/FreeRTOS_DNS_Callback.c b/FreeRTOS/source/FreeRTOS_DNS_Callback.c new file mode 100644 index 0000000..0c4b7b3 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_DNS_Callback.c @@ -0,0 +1,203 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_DNS_Callback.c + * @brief File that handles the DNS Callback option + */ + +#include "FreeRTOS_DNS_Callback.h" + +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_DNS_Globals.h" +#include "FreeRTOS_IP_Timers.h" + +#if ( ( ipconfigDNS_USE_CALLBACKS == 1 ) && ( ipconfigUSE_DNS != 0 ) ) + +/** + * @brief list of callbacks to send + */ + static List_t xCallbackList; + +/** + * @brief A DNS reply was received, see if there is any matching entry and + * call the handler. + * + * @param[in] uxIdentifier: Identifier associated with the callback function. + * @param[in] pcName: The name associated with the callback function. + * @param[in] ulIPAddress: IP-address obtained from the DNS server. + * + * @return Returns pdTRUE if uxIdentifier was recognized. + */ + BaseType_t xDNSDoCallback( TickType_t uxIdentifier, + const char * pcName, + uint32_t ulIPAddress ) + { + BaseType_t xResult = pdFALSE; + const ListItem_t * pxIterator; + const ListItem_t * xEnd = listGET_END_MARKER( &xCallbackList ); + + vTaskSuspendAll(); + { + for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd ); + pxIterator != ( const ListItem_t * ) xEnd; + pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) ) + { + if( listGET_LIST_ITEM_VALUE( pxIterator ) == uxIdentifier ) + { + DNSCallback_t * pxCallback = ( ( DNSCallback_t * ) + listGET_LIST_ITEM_OWNER( pxIterator ) ); + + pxCallback->pCallbackFunction( pcName, pxCallback->pvSearchID, + ulIPAddress ); + ( void ) uxListRemove( &pxCallback->xListItem ); + vPortFree( pxCallback ); + + if( listLIST_IS_EMPTY( &xCallbackList ) != pdFALSE ) + { + /* The list of outstanding requests is empty. No need for periodic polling. */ + vIPSetDNSTimerEnableState( pdFALSE ); + } + + xResult = pdTRUE; + break; + } + } + } + ( void ) xTaskResumeAll(); + return xResult; + } + +/** + * @brief FreeRTOS_gethostbyname_a() was called along with callback parameters. + * Store them in a list for later reference. + * + * @param[in] pcHostName: The hostname whose IP address is being searched for. + * @param[in] pvSearchID: The search ID of the DNS callback function to set. + * @param[in] pCallbackFunction: The callback function pointer. + * @param[in] uxTimeout: Timeout of the callback function. + * @param[in] uxIdentifier: Random number used as ID in the DNS message. + */ + void vDNSSetCallBack( const char * pcHostName, + void * pvSearchID, + FOnDNSEvent pCallbackFunction, + TickType_t uxTimeout, + TickType_t uxIdentifier ) + { + size_t lLength = strlen( pcHostName ); + DNSCallback_t * pxCallback = ( ( DNSCallback_t * ) pvPortMalloc( sizeof( *pxCallback ) + lLength ) ); + + /* Translate from ms to number of clock ticks. */ + uxTimeout /= portTICK_PERIOD_MS; + + if( pxCallback != NULL ) + { + if( listLIST_IS_EMPTY( &xCallbackList ) != pdFALSE ) + { + /* This is the first one, start the DNS timer to check for timeouts */ + vDNSTimerReload( FreeRTOS_min_uint32( 1000U, uxTimeout ) ); + } + + ( void ) strcpy( pxCallback->pcName, pcHostName ); + pxCallback->pCallbackFunction = pCallbackFunction; + pxCallback->pvSearchID = pvSearchID; + pxCallback->uxRemainingTime = uxTimeout; + vTaskSetTimeOutState( &pxCallback->uxTimeoutState ); + listSET_LIST_ITEM_OWNER( &( pxCallback->xListItem ), ( void * ) pxCallback ); + listSET_LIST_ITEM_VALUE( &( pxCallback->xListItem ), uxIdentifier ); + vTaskSuspendAll(); + { + vListInsertEnd( &xCallbackList, &pxCallback->xListItem ); + } + ( void ) xTaskResumeAll(); + } + else + { + FreeRTOS_debug_printf( ( " vDNSSetCallBack : Could not allocate memory: %u bytes", + ( unsigned ) ( sizeof( *pxCallback ) + lLength ) ) ); + } + } + +/** + * @brief Iterate through the list of call-back structures and remove + * old entries which have reached a timeout. + * As soon as the list has become empty, the DNS timer will be stopped. + * In case pvSearchID is supplied, the user wants to cancel a DNS request. + * + * @param[in] pvSearchID: The search ID of callback function whose associated + * DNS request is being cancelled. If non-ID specific checking of + * all requests is required, then this field should be kept as NULL. + */ + void vDNSCheckCallBack( void * pvSearchID ) + { + const ListItem_t * pxIterator; + const ListItem_t * xEnd = listGET_END_MARKER( &xCallbackList ); + + vTaskSuspendAll(); + { + for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd ); + pxIterator != xEnd; ) + { + DNSCallback_t * pxCallback = ( ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + /* Move to the next item because we might remove this item */ + pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ); + + if( ( pvSearchID != NULL ) && ( pvSearchID == pxCallback->pvSearchID ) ) + { + ( void ) uxListRemove( &( pxCallback->xListItem ) ); + vPortFree( pxCallback ); + } + else if( xTaskCheckForTimeOut( &pxCallback->uxTimeoutState, &pxCallback->uxRemainingTime ) != pdFALSE ) + { + pxCallback->pCallbackFunction( pxCallback->pcName, pxCallback->pvSearchID, 0 ); + ( void ) uxListRemove( &( pxCallback->xListItem ) ); + vPortFree( pxCallback ); + } + else + { + /* This call-back is still waiting for a reply or a time-out. */ + } + } + } + ( void ) xTaskResumeAll(); + + if( listLIST_IS_EMPTY( &xCallbackList ) != pdFALSE ) + { + vIPSetDNSTimerEnableState( pdFALSE ); + } + } + +/** + * @brief initialize the cache + * @post will modify global list xCallbackList + */ + void vDNSCallbackInitialise() + { + vListInitialise( &xCallbackList ); + } +#endif /* if ( ipconfigDNS_USE_CALLBACKS == 1 ) */ diff --git a/FreeRTOS/source/FreeRTOS_DNS_Networking.c b/FreeRTOS/source/FreeRTOS_DNS_Networking.c new file mode 100644 index 0000000..e193fae --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_DNS_Networking.c @@ -0,0 +1,153 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_DNS_Networking.c + * @brief Implements the Domain Name System Networking + * for the FreeRTOS+TCP network stack. + */ + +#include "FreeRTOS.h" + +#include "FreeRTOS_DNS_Networking.h" + +#if ( ipconfigUSE_DNS != 0 ) + +/** + * @brief Create a socket and bind it to the standard DNS port number. + * + * @return The created socket - or NULL if the socket could not be created or could not be bound. + */ + Socket_t DNS_CreateSocket( TickType_t uxReadTimeOut_ticks ) + { + Socket_t xSocket; + struct freertos_sockaddr xAddress; + TickType_t uxWriteTimeOut_ticks = ipconfigDNS_SEND_BLOCK_TIME_TICKS; + BaseType_t xReturn; + + /* This must be the first time this function has been called. Create + * the socket. */ + xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); + + if( xSocketValid( xSocket ) == pdFALSE ) + { + /* There was an error, return NULL. */ + xSocket = NULL; + } + else + { + /* Auto bind the port. */ + xAddress.sin_port = 0U; + xReturn = FreeRTOS_bind( xSocket, &xAddress, ( socklen_t ) sizeof( xAddress ) ); + + /* Check the bind was successful, and clean up if not. */ + if( xReturn != 0 ) + { + ( void ) FreeRTOS_closesocket( xSocket ); + xSocket = NULL; + } + else + { + /* Ideally we should check for the return value. But since we are passing + * correct parameters, and xSocket is != NULL, the return value is + * going to be '0' i.e. success. Thus, return value is discarded */ + ( void ) FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, &( uxWriteTimeOut_ticks ), sizeof( TickType_t ) ); + ( void ) FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &( uxReadTimeOut_ticks ), sizeof( TickType_t ) ); + } + } + + return xSocket; + } + +/** + * @brief perform a DNS network request + * @param xDNSSocket Created socket + * @param xAddress address structure (ip, port etc) + * @param pxDNSBuf buffer to send + * @return xReturn: true if the message could be sent + * false otherwise + * + */ + BaseType_t DNS_SendRequest( Socket_t xDNSSocket, + const struct freertos_sockaddr * xAddress, + const struct xDNSBuffer * pxDNSBuf ) + { + BaseType_t xReturn = pdFALSE; + + iptraceSENDING_DNS_REQUEST(); + + /* Send the DNS message. */ + if( FreeRTOS_sendto( xDNSSocket, + pxDNSBuf->pucPayloadBuffer, + pxDNSBuf->uxPayloadLength, + FREERTOS_ZERO_COPY, + xAddress, + ( socklen_t ) sizeof( *xAddress ) ) != 0 ) + { + xReturn = pdTRUE; + } + else + { + /* The message was not sent so the stack will not be + * releasing the zero copy - it must be released here. */ + xReturn = pdFALSE; + } + + return xReturn; + } + +/** + * @brief perform a DNS network read + * @param xDNSSocket socket + * @param xAddress address to read from + * @param pxReceiveBuffer buffer to fill with received data + */ + void DNS_ReadReply( const ConstSocket_t xDNSSocket, + struct freertos_sockaddr * xAddress, + struct xDNSBuffer * pxReceiveBuffer ) + { + uint32_t ulAddressLength = ( uint32_t ) sizeof( struct freertos_sockaddr ); + + /* Wait for the reply. */ + pxReceiveBuffer->uxPayloadLength = ( size_t ) FreeRTOS_recvfrom( xDNSSocket, + &pxReceiveBuffer->pucPayloadBuffer, + 0, + FREERTOS_ZERO_COPY, + xAddress, + &ulAddressLength ); + pxReceiveBuffer->uxPayloadSize = pxReceiveBuffer->uxPayloadLength; + } + +/** + * @brief perform a DNS network close + * @param xDNSSocket + */ + void DNS_CloseSocket( Socket_t xDNSSocket ) + { + ( void ) FreeRTOS_closesocket( xDNSSocket ); + } +#endif /* if ( ipconfigUSE_DNS != 0 ) */ diff --git a/FreeRTOS/source/FreeRTOS_DNS_Parser.c b/FreeRTOS/source/FreeRTOS_DNS_Parser.c new file mode 100644 index 0000000..cb8c1ca --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_DNS_Parser.c @@ -0,0 +1,953 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_DNS_Parser.c + * @brief Implements the DNS message parser + */ + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" + +#include "FreeRTOS_DNS_Globals.h" +#include "FreeRTOS_DNS_Parser.h" +#include "FreeRTOS_DNS_Cache.h" +#include "FreeRTOS_DNS_Callback.h" + +#include "NetworkBufferManagement.h" + +#include + +#if ( ipconfigUSE_DNS != 0 ) + +/** @brief The list of all callback structures. */ + +/** + * @brief Read the Name field out of a DNS response packet. + * + * @param[in] pucByte: Pointer to the DNS response. + * @param[in] uxRemainingBytes: Length of the DNS response. + * @param[out] pcName: The pointer in which the name in the DNS response will be returned. + * @param[in] uxDestLen: Size of the pcName array. + * + * @return If a fully formed name was found, then return the number of bytes processed in pucByte. + */ + size_t DNS_ReadNameField( const uint8_t * pucByte, + size_t uxRemainingBytes, + char * pcName, + size_t uxDestLen ) + { + size_t uxNameLen = 0U; + size_t uxIndex = 0U; + size_t uxSourceLen = uxRemainingBytes; + + /* uxCount gets the values from pucByte and counts down to 0. + * No need to have a different type than that of pucByte */ + size_t uxCount; + + if( uxSourceLen == ( size_t ) 0U ) + { + /* Return 0 value in case of error. */ + uxIndex = 0U; + } + + /* Determine if the name is the fully coded name, or an offset to the name + * elsewhere in the message. */ + else if( ( pucByte[ uxIndex ] & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET ) + { + /* Jump over the two byte offset. */ + if( uxSourceLen > sizeof( uint16_t ) ) + { + uxIndex += sizeof( uint16_t ); + } + else + { + uxIndex = 0U; + } + } + else + { + /* 'uxIndex' points to the full name. Walk over the string. */ + while( ( uxIndex < uxSourceLen ) && ( pucByte[ uxIndex ] != ( uint8_t ) 0x00U ) ) + { + /* If this is not the first time through the loop, then add a + * separator in the output. */ + if( ( uxNameLen > 0U ) ) + { + if( uxNameLen >= uxDestLen ) + { + uxIndex = 0U; + /* coverity[break_stmt] : Break statement terminating the loop */ + break; + } + + pcName[ uxNameLen ] = '.'; + uxNameLen++; + } + + /* Process the first/next sub-string. */ + uxCount = ( size_t ) pucByte[ uxIndex ]; + uxIndex++; + + if( ( uxIndex + uxCount ) > uxSourceLen ) + { + uxIndex = 0U; + break; + } + + while( uxCount-- != 0U ) + { + if( uxNameLen >= uxDestLen ) + { + uxIndex = 0U; + break; + + /* break out of inner loop here + * break out of outer loop at the test uxNameLen >= uxDestLen. */ + } + + pcName[ uxNameLen ] = ( char ) pucByte[ uxIndex ]; + uxNameLen++; + uxIndex++; + } + } + + /* Confirm that a fully formed name was found. */ + if( uxIndex > 0U ) + { + /* Here, there is no need to check for pucByte[ uxindex ] == 0 because: + * When we break out of the above while loop, uxIndex is made 0 thereby + * failing above check. Whenever we exit the loop otherwise, either + * pucByte[ uxIndex ] == 0 (which makes the check here unnecessary) or + * uxIndex >= uxSourceLen (which makes sure that we do not go in the 'if' + * case). + */ + if( ( uxNameLen < uxDestLen ) && ( uxIndex < uxSourceLen ) ) + { + pcName[ uxNameLen ] = '\0'; + uxIndex++; + } + else + { + uxIndex = 0U; + } + } + } + + return uxIndex; + } + +/** + * @brief Simple routine that jumps over the NAME field of a resource record. + * + * @param[in] pucByte: The pointer to the resource record. + * @param[in] uxLength: Length of the resource record. + * + * @return It returns the number of bytes read, or zero when an error has occurred. + */ + size_t DNS_SkipNameField( const uint8_t * pucByte, + size_t uxLength ) + { + size_t uxChunkLength; + size_t uxSourceLenCpy = uxLength; + size_t uxIndex = 0U; + + if( uxSourceLenCpy == 0U ) + { + uxIndex = 0U; + } + + /* Determine if the name is the fully coded name, or an offset to the name + * elsewhere in the message. */ + else if( ( pucByte[ uxIndex ] & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET ) + { + /* Jump over the two byte offset. */ + if( uxSourceLenCpy > sizeof( uint16_t ) ) + { + uxIndex += sizeof( uint16_t ); + } + else + { + uxIndex = 0U; + } + } + else + { + /* pucByte points to the full name. Walk over the string. */ + while( ( pucByte[ uxIndex ] != 0U ) && ( uxSourceLenCpy > 1U ) ) + { + /* Conversion to size_t causes addition to be done + * in size_t */ + uxChunkLength = ( ( size_t ) pucByte[ uxIndex ] ) + 1U; + + if( uxSourceLenCpy > uxChunkLength ) + { + uxSourceLenCpy -= uxChunkLength; + uxIndex += uxChunkLength; + } + else + { + uxIndex = 0U; + break; + } + } + + /* Confirm that a fully formed name was found. */ + if( uxIndex > 0U ) + { + if( pucByte[ uxIndex ] == 0U ) + { + uxIndex++; + } + else + { + uxIndex = 0U; + } + } + } + + return uxIndex; + } + +/** + * @brief Process a response packet from a DNS server, or an LLMNR reply. + * + * @param[in] pucUDPPayloadBuffer: The DNS response received as a UDP + * payload. + * @param[in] uxBufferLength: Length of the UDP payload buffer. + * @param[in] xExpected: indicates whether the identifier in the reply + * was expected, and thus if the DNS cache may be + * updated with the reply. + * + * @return The IP address in the DNS response if present and if xExpected is set to pdTRUE. + * An error code (dnsPARSE_ERROR) if there was an error in the DNS response. + * 0 if xExpected set to pdFALSE. + */ + uint32_t DNS_ParseDNSReply( uint8_t * pucUDPPayloadBuffer, + size_t uxBufferLength, + BaseType_t xExpected ) + { + DNSMessage_t * pxDNSMessageHeader; + uint32_t ulIPAddress = 0U; + + #if ( ipconfigUSE_LLMNR == 1 ) + char * pcRequestedName = NULL; + #endif + uint8_t * pucByte; + size_t uxSourceBytesRemaining; + uint16_t x; + uint16_t usQuestions; + BaseType_t xReturn = pdTRUE; + + #if ( ipconfigUSE_LLMNR == 1 ) + uint16_t usType = 0U; + uint16_t usClass = 0U; + #endif + #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) + BaseType_t xDoStore = xExpected; + char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ] = ""; + #endif + + /* Ensure that the buffer is of at least minimal DNS message length. */ + if( uxBufferLength < sizeof( DNSMessage_t ) ) + { + xReturn = pdFALSE; + } + else + { + uxSourceBytesRemaining = uxBufferLength; + + /* Parse the DNS message header. Map the byte stream onto a structure + * for easier access. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxDNSMessageHeader = ( ( DNSMessage_t * ) + pucUDPPayloadBuffer ); + + /* Introduce a do {} while (0) to allow the use of breaks. */ + do + { + size_t uxBytesRead = 0U; + size_t uxResult; + + /* Start at the first byte after the header. */ + pucByte = &( pucUDPPayloadBuffer[ sizeof( DNSMessage_t ) ] ); + uxSourceBytesRemaining -= sizeof( DNSMessage_t ); + + /* Skip any question records. */ + usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions ); + + for( x = 0U; x < usQuestions; x++ ) + { + #if ( ipconfigUSE_LLMNR == 1 ) + { + if( x == 0U ) + { + pcRequestedName = ( char * ) pucByte; + } + } + #endif + + #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) + if( x == 0U ) + { + uxResult = DNS_ReadNameField( pucByte, + uxSourceBytesRemaining, + pcName, + sizeof( pcName ) ); + } + else + #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */ + { + /* Skip the variable length pcName field. */ + uxResult = DNS_SkipNameField( pucByte, + uxSourceBytesRemaining ); + } + + /* Check for a malformed response. */ + if( uxResult == 0U ) + { + xReturn = pdFALSE; + break; + } + + uxBytesRead += uxResult; + pucByte = &( pucByte[ uxResult ] ); + uxSourceBytesRemaining -= uxResult; + + /* Check the remaining buffer size. */ + if( uxSourceBytesRemaining >= sizeof( uint32_t ) ) + { + #if ( ipconfigUSE_LLMNR == 1 ) + { + /* usChar2u16 returns value in host endianness. */ + usType = usChar2u16( pucByte ); + usClass = usChar2u16( &( pucByte[ 2 ] ) ); + } + #endif /* ipconfigUSE_LLMNR */ + + /* Skip the type and class fields. */ + pucByte = &( pucByte[ sizeof( uint32_t ) ] ); + uxSourceBytesRemaining -= sizeof( uint32_t ); + } + else + { + xReturn = pdFALSE; + break; + } + } + + if( xReturn == pdFALSE ) + { + /* No need to proceed. Break out of the do-while loop. */ + break; + } + + /* Search through the answer records. */ + pxDNSMessageHeader->usAnswers = + FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers ); + + if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) + == dnsEXPECTED_RX_FLAGS ) + { + ulIPAddress = parseDNSAnswer( pxDNSMessageHeader, + pucByte, + uxSourceBytesRemaining, + &uxBytesRead + #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) + , + pcName, + xDoStore + #endif + ); + } + + #if ( ipconfigUSE_LLMNR == 1 ) + + /* No need to check that pcRequestedName != NULL since sQuestions != 0, then + * pcRequestedName is assigned with this statement + * "pcRequestedName = ( char * ) pucByte;" */ + else if( ( usQuestions != ( uint16_t ) 0U ) && + ( usType == dnsTYPE_A_HOST ) && + ( usClass == dnsCLASS_IN ) ) + { + /* If this is not a reply to our DNS request, it might an LLMNR + * request. */ + if( xApplicationDNSQueryHook( &( pcRequestedName[ 1 ] ) ) != pdFALSE ) + { + int16_t usLength; + NetworkBufferDescriptor_t * pxNewBuffer = NULL; + NetworkBufferDescriptor_t * pxNetworkBuffer = + pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer ); + LLMNRAnswer_t * pxAnswer; + uint8_t * pucNewBuffer = NULL; + + if( pxNetworkBuffer != NULL ) + { + if( xBufferAllocFixedSize == pdFALSE ) + { + size_t uxDataLength = uxBufferLength + + sizeof( UDPHeader_t ) + + sizeof( EthernetHeader_t ) + + sizeof( IPHeader_t ); + + /* Set the size of the outgoing packet. */ + pxNetworkBuffer->xDataLength = uxDataLength; + pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, + uxDataLength + + sizeof( LLMNRAnswer_t ) ); + + if( pxNewBuffer != NULL ) + { + BaseType_t xOffset1, xOffset2; + + xOffset1 = ( BaseType_t ) ( pucByte - pucUDPPayloadBuffer ); + xOffset2 = ( BaseType_t ) ( ( ( uint8_t * ) pcRequestedName ) - pucUDPPayloadBuffer ); + + pxNetworkBuffer = pxNewBuffer; + pucNewBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ); + + pucByte = &( pucNewBuffer[ xOffset1 ] ); + pcRequestedName = ( char * ) &( pucNewBuffer[ xOffset2 ] ); + pxDNSMessageHeader = ( ( DNSMessage_t * ) pucNewBuffer ); + } + else + { + /* Just to indicate that the message may not be answered. */ + pxNetworkBuffer = NULL; + } + } + else + { + pucNewBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ); + } + } + + if( ( pxNetworkBuffer != NULL ) ) + { + pxAnswer = ( ( LLMNRAnswer_t * ) pucByte ); + /* We leave 'usIdentifier' and 'usQuestions' untouched */ + #ifndef _lint + vSetField16( pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_REPONSE ); /* Set the response flag */ + vSetField16( pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 ); /* Provide a single answer */ + vSetField16( pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 ); /* No authority */ + vSetField16( pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 ); /* No additional info */ + #endif /* lint */ + + pxAnswer->ucNameCode = dnsNAME_IS_OFFSET; + pxAnswer->ucNameOffset = ( uint8_t ) ( pcRequestedName - ( char * ) pucNewBuffer ); + + #ifndef _lint + vSetField16( pxAnswer, LLMNRAnswer_t, usType, dnsTYPE_A_HOST ); /* Type A: host */ + vSetField16( pxAnswer, LLMNRAnswer_t, usClass, dnsCLASS_IN ); /* 1: Class IN */ + vSetField32( pxAnswer, LLMNRAnswer_t, ulTTL, dnsLLMNR_TTL_VALUE ); + vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, 4 ); + vSetField32( pxAnswer, LLMNRAnswer_t, + ulIPAddress, + FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) ); + #endif /* lint */ + usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( pucByte - pucNewBuffer ) ); + + prepareReplyDNSMessage( pxNetworkBuffer, usLength ); + /* This function will fill in the eth addresses and send the packet */ + vReturnEthernetFrame( pxNetworkBuffer, pdFALSE ); + + if( pxNewBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxNewBuffer ); + } + } + } + } + else + { + /* Not an expected reply. */ + } + #endif /* ipconfigUSE_LLMNR == 1 */ + ( void ) uxBytesRead; + } while( ipFALSE_BOOL ); + } + + if( xReturn == pdFALSE ) + { + /* There was an error while parsing the DNS response. Return error code. */ + ulIPAddress = ( uint32_t ) dnsPARSE_ERROR; + } + else if( xExpected == pdFALSE ) + { + /* Do not return a valid IP-address in case the reply was not expected. */ + ulIPAddress = 0U; + } + else + { + /* The IP-address found will be returned. */ + } + + #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) + ( void ) xDoStore; + #endif + + return ulIPAddress; + } + +/** + * @brief perform a dns lookup in the local cache + * @param[in] pxDNSMessageHeader DNS header + * @param pucByte buffer + * @param uxSourceBytesRemaining remaining bytes in pucByte + * @param[out] uxBytesRead total bytes consumed by the function + * @param pcName update the cache and /or send to callback + * @param xDoStore whether to update the cache + * @return ip address extracted from the frame or zero if not found + */ + uint32_t parseDNSAnswer( const DNSMessage_t * pxDNSMessageHeader, + uint8_t * pucByte, + size_t uxSourceBytesRemaining, + size_t * uxBytesRead + #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) + , + const char * pcName, + BaseType_t xDoStore + #endif + ) + { + uint16_t x; + size_t uxResult; + const uint16_t usCount = ( uint16_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY; + uint16_t usNumARecordsStored = 0; + BaseType_t xReturn = pdTRUE; + uint16_t usType = 0U; + const DNSAnswerRecord_t * pxDNSAnswerRecord; + const void * pvCopySource; + void * pvCopyDest; + const size_t uxAddressLength = ipSIZE_OF_IPv4_ADDRESS; + uint32_t ulIPAddress = 0U; + uint32_t ulReturnIPAddress = 0U; + uint16_t usDataLength; + uint8_t * pucBuffer = pucByte; + size_t uxRxSourceByteRemaining = uxSourceBytesRemaining; + + for( x = 0U; x < pxDNSMessageHeader->usAnswers; x++ ) + { + BaseType_t xDoAccept; + + if( usNumARecordsStored >= usCount ) + { + /* Only count ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY number of records. */ + break; + } + + uxResult = DNS_SkipNameField( pucBuffer, + uxRxSourceByteRemaining ); + + /* Check for a malformed response. */ + if( uxResult == 0U ) + { + xReturn = pdFALSE; + break; + } + + *uxBytesRead += uxResult; + pucBuffer = &( pucBuffer[ uxResult ] ); + uxRxSourceByteRemaining -= uxResult; + + /* Is there enough data for an IPv4 A record answer and, if so, + * is this an A record? */ + if( uxRxSourceByteRemaining < sizeof( uint16_t ) ) + { + xReturn = pdFALSE; + break; + } + + usType = usChar2u16( pucBuffer ); + + if( usType == ( uint16_t ) dnsTYPE_A_HOST ) + { + if( uxRxSourceByteRemaining >= ( sizeof( DNSAnswerRecord_t ) + uxAddressLength ) ) + { + xDoAccept = pdTRUE; + } + else + { + xDoAccept = pdFALSE; + } + } + else + { + /* Unknown host type. */ + xDoAccept = pdFALSE; + } + + if( xDoAccept != pdFALSE ) + { + /* This is the required record type and is of sufficient size. */ + + /* Mapping pucBuffer to a DNSAnswerRecord allows easy access of the + * fields of the structure. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxDNSAnswerRecord = ( ( DNSAnswerRecord_t * ) pucBuffer ); + + /* Sanity check the data length of an IPv4 answer. */ + if( FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ) == + ( uint16_t ) uxAddressLength ) + { + /* Copy the IP address out of the record. Using different pointers + * to copy only the portion we want is intentional here. */ + + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = &pucBuffer[ sizeof( DNSAnswerRecord_t ) ]; + pvCopyDest = &ulIPAddress; + ( void ) memcpy( pvCopyDest, pvCopySource, uxAddressLength ); + + #if ( ipconfigDNS_USE_CALLBACKS == 1 ) + { + /* See if any asynchronous call was made to FreeRTOS_gethostbyname_a() */ + if( xDNSDoCallback( ( TickType_t ) pxDNSMessageHeader->usIdentifier, + pcName, + ulIPAddress ) != pdFALSE ) + { + /* This device has requested this DNS look-up. + * The result may be stored in the DNS cache. */ + xDoStore = pdTRUE; + } + } + #endif /* ipconfigDNS_USE_CALLBACKS == 1 */ + #if ( ipconfigUSE_DNS_CACHE == 1 ) + { + char cBuffer[ 16 ]; + + /* The reply will only be stored in the DNS cache when the + * request was issued by this device. */ + if( xDoStore != pdFALSE ) + { + ( void ) FreeRTOS_dns_update( + pcName, + &ulIPAddress, + pxDNSAnswerRecord->ulTTL ); + usNumARecordsStored++; /* Track # of A records stored */ + } + + ( void ) FreeRTOS_inet_ntop( FREERTOS_AF_INET, + ( const void * ) &( ulIPAddress ), + cBuffer, + ( socklen_t ) sizeof( cBuffer ) ); + /* Show what has happened. */ + FreeRTOS_printf( ( "DNS[0x%04lX]: The answer to '%s' (%s) will%s be stored\n", + ( UBaseType_t ) pxDNSMessageHeader->usIdentifier, + pcName, + cBuffer, + ( xDoStore != 0 ) ? "" : " NOT" ) ); + } + #endif /* ipconfigUSE_DNS_CACHE */ + + if( ( ulReturnIPAddress == 0U ) && ( ulIPAddress != 0U ) ) + { + /* Remember the first IP-address that is found. */ + ulReturnIPAddress = ulIPAddress; + } + } + + pucBuffer = &( pucBuffer[ sizeof( DNSAnswerRecord_t ) + uxAddressLength ] ); + uxRxSourceByteRemaining -= ( sizeof( DNSAnswerRecord_t ) + uxAddressLength ); + } + else if( uxRxSourceByteRemaining >= sizeof( DNSAnswerRecord_t ) ) + { + /* It's not an A record, so skip it. Get the header location + * and then jump over the header. */ + /* Cast the response to DNSAnswerRecord for easy access to fields of the DNS response. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxDNSAnswerRecord = ( ( DNSAnswerRecord_t * ) pucBuffer ); + + pucBuffer = &( pucBuffer[ sizeof( DNSAnswerRecord_t ) ] ); + uxRxSourceByteRemaining -= sizeof( DNSAnswerRecord_t ); + + /* Determine the length of the answer data from the header. */ + usDataLength = FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ); + + /* Jump over the answer. */ + if( uxRxSourceByteRemaining >= usDataLength ) + { + pucBuffer = &( pucBuffer[ usDataLength ] ); + uxRxSourceByteRemaining -= usDataLength; + } + else + { + /* Malformed response. */ + xReturn = pdFALSE; + break; + } + } + else + { + /* Do nothing */ + } + } + + return ( xReturn != 0 ) ? ulReturnIPAddress : 0U; + } + + #if ( ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_LLMNR == 1 ) ) + +/** + * @brief Send a DNS message to be used in NBNS or LLMNR + * + * @param[in] pxNetworkBuffer: The network buffer descriptor with the DNS message. + * @param[in] lNetLength: The length of the DNS message. + */ + void prepareReplyDNSMessage( NetworkBufferDescriptor_t * pxNetworkBuffer, + BaseType_t lNetLength ) + { + UDPPacket_t * pxUDPPacket; + IPHeader_t * pxIPHeader; + UDPHeader_t * pxUDPHeader; + size_t uxDataLength; + + pxUDPPacket = ( ( UDPPacket_t * ) + pxNetworkBuffer->pucEthernetBuffer ); + pxIPHeader = &pxUDPPacket->xIPHeader; + pxUDPHeader = &pxUDPPacket->xUDPHeader; + /* HT: started using defines like 'ipSIZE_OF_xxx' */ + pxIPHeader->usLength = FreeRTOS_htons( ( uint16_t ) lNetLength + + ipSIZE_OF_IPv4_HEADER + + ipSIZE_OF_UDP_HEADER ); + /* HT:endian: should not be translated, copying from packet to packet */ + pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress; + pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; + pxIPHeader->ucTimeToLive = ipconfigUDP_TIME_TO_LIVE; + pxIPHeader->usIdentification = FreeRTOS_htons( usPacketIdentifier ); + + /* The stack doesn't support fragments, so the fragment offset field must always be zero. + * The header was never memset to zero, so set both the fragment offset and fragmentation flags in one go. + */ + #if ( ipconfigFORCE_IP_DONT_FRAGMENT != 0 ) + pxIPHeader->usFragmentOffset = ipFRAGMENT_FLAGS_DONT_FRAGMENT; + #else + pxIPHeader->usFragmentOffset = 0U; + #endif + usPacketIdentifier++; + pxUDPHeader->usLength = FreeRTOS_htons( ( uint32_t ) lNetLength + + ipSIZE_OF_UDP_HEADER ); + vFlip_16( pxUDPHeader->usSourcePort, pxUDPHeader->usDestinationPort ); + + /* Important: tell NIC driver how many bytes must be sent */ + uxDataLength = ( ( size_t ) lNetLength ) + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_UDP_HEADER + ipSIZE_OF_ETH_HEADER; + + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) + { + /* Calculate the IP header checksum. */ + pxIPHeader->usHeaderChecksum = 0U; + pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); + pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); + + /* calculate the UDP checksum for outgoing package */ + ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxUDPPacket, uxDataLength, pdTRUE ); + } + #endif + + /* Important: tell NIC driver how many bytes must be sent */ + pxNetworkBuffer->xDataLength = uxDataLength; + } + + #endif /* ipconfigUSE_NBNS == 1 || ipconfigUSE_LLMNR == 1 */ + + #if ( ipconfigUSE_NBNS == 1 ) + +/** + * @brief Respond to an NBNS query or an NBNS reply. + * + * @param[in] pucPayload: the UDP payload of the NBNS message. + * @param[in] uxBufferLength: Length of the Buffer. + * @param[in] ulIPAddress: IP address of the sender. + */ + void DNS_TreatNBNS( uint8_t * pucPayload, + size_t uxBufferLength, + uint32_t ulIPAddress ) + { + uint16_t usFlags; + uint16_t usType; + uint16_t usClass; + uint8_t * pucSource; + uint8_t * pucTarget; + uint8_t ucByte; + uint8_t ucNBNSName[ 17 ]; + uint8_t * pucUDPPayloadBuffer = pucPayload; + NetworkBufferDescriptor_t * pxNetworkBuffer; + + /* Read the request flags in host endianness. */ + usFlags = usChar2u16( &( pucUDPPayloadBuffer[ offsetof( NBNSRequest_t, usFlags ) ] ) ); + + if( ( usFlags & dnsNBNS_FLAGS_OPCODE_MASK ) == dnsNBNS_FLAGS_OPCODE_QUERY ) + { + usType = usChar2u16( &( pucUDPPayloadBuffer[ offsetof( NBNSRequest_t, usType ) ] ) ); + usClass = usChar2u16( &( pucUDPPayloadBuffer[ offsetof( NBNSRequest_t, usClass ) ] ) ); + + /* Not used for now */ + ( void ) usClass; + + /* For NBNS a name is 16 bytes long, written with capitals only. + * Make sure that the copy is terminated with a zero. */ + pucTarget = &( ucNBNSName[ sizeof( ucNBNSName ) - 2U ] ); + pucTarget[ 1 ] = ( uint8_t ) 0U; + + /* Start with decoding the last 2 bytes. */ + pucSource = &( pucUDPPayloadBuffer[ ( dnsNBNS_ENCODED_NAME_LENGTH - 2 ) + + offsetof( NBNSRequest_t, ucName ) ] ); + + for( ; ; ) + { + const uint8_t ucCharA = ( uint8_t ) 0x41U; + + ucByte = ( ( uint8_t ) ( ( pucSource[ 0 ] - ucCharA ) << 4 ) ) | + ( pucSource[ 1 ] - ucCharA ); + + /* Make sure there are no trailing spaces in the name. */ + if( ( ucByte == ( uint8_t ) ' ' ) && ( pucTarget[ 1 ] == 0U ) ) + { + ucByte = 0U; + } + + *pucTarget = ucByte; + + if( pucTarget == ucNBNSName ) + { + break; + } + + pucTarget -= 1; + pucSource -= 2; + } + + #if ( ipconfigUSE_DNS_CACHE == 1 ) + { + if( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) != 0U ) + { + /* If this is a response from another device, + * add the name to the DNS cache */ + ( void ) FreeRTOS_dns_update( ( char * ) ucNBNSName, &( ulIPAddress ), 0 ); + } + } + #else + { + /* Avoid compiler warnings. */ + ( void ) ulIPAddress; + } + #endif /* ipconfigUSE_DNS_CACHE */ + + if( ( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) == 0U ) && + ( usType == dnsNBNS_TYPE_NET_BIOS ) && + ( xApplicationDNSQueryHook( ( const char * ) ucNBNSName ) != pdFALSE ) ) + { + uint16_t usLength; + DNSMessage_t * pxMessage; + NBNSAnswer_t * pxAnswer; + NetworkBufferDescriptor_t * pxNewBuffer = NULL; + + /* Someone is looking for a device with ucNBNSName, + * prepare a positive reply. */ + pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer ); + + if( ( xBufferAllocFixedSize == pdFALSE ) && + ( pxNetworkBuffer != NULL ) ) + { + /* The field xDataLength was set to the total length of the UDP packet, + * i.e. the payload size plus sizeof( UDPPacket_t ). */ + pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, pxNetworkBuffer->xDataLength + sizeof( NBNSAnswer_t ) ); + + if( pxNewBuffer != NULL ) + { + pucUDPPayloadBuffer = &( pxNewBuffer->pucEthernetBuffer[ sizeof( UDPPacket_t ) ] ); + pxNetworkBuffer = pxNewBuffer; + } + else + { + /* Just prevent that a reply will be sent */ + pxNetworkBuffer = NULL; + } + } + + /* Should not occur: pucUDPPayloadBuffer is part of a xNetworkBufferDescriptor */ + if( pxNetworkBuffer != NULL ) + { + pxMessage = ( ( DNSMessage_t * ) pucUDPPayloadBuffer ); + + /* As the fields in the structures are not word-aligned, we have to + * copy the values byte-by-byte using macro's vSetField16() and vSetField32() */ + #ifndef _lint + vSetField16( pxMessage, DNSMessage_t, usFlags, dnsNBNS_QUERY_RESPONSE_FLAGS ); /* 0x8500 */ + vSetField16( pxMessage, DNSMessage_t, usQuestions, 0 ); + vSetField16( pxMessage, DNSMessage_t, usAnswers, 1 ); + vSetField16( pxMessage, DNSMessage_t, usAuthorityRRs, 0 ); + vSetField16( pxMessage, DNSMessage_t, usAdditionalRRs, 0 ); + #else + ( void ) pxMessage; + #endif + + pxAnswer = ( ( NBNSAnswer_t * ) &( pucUDPPayloadBuffer[ offsetof( NBNSRequest_t, usType ) ] ) ); + + #ifndef _lint + vSetField16( pxAnswer, NBNSAnswer_t, usType, usType ); /* Type */ + vSetField16( pxAnswer, NBNSAnswer_t, usClass, dnsNBNS_CLASS_IN ); /* Class */ + vSetField32( pxAnswer, NBNSAnswer_t, ulTTL, dnsNBNS_TTL_VALUE ); + vSetField16( pxAnswer, NBNSAnswer_t, usDataLength, 6 ); /* 6 bytes including the length field */ + vSetField16( pxAnswer, NBNSAnswer_t, usNbFlags, dnsNBNS_NAME_FLAGS ); + vSetField32( pxAnswer, NBNSAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) ); + #else + ( void ) pxAnswer; + #endif + + usLength = ( uint16_t ) ( sizeof( NBNSAnswer_t ) + ( size_t ) offsetof( NBNSRequest_t, usType ) ); + + prepareReplyDNSMessage( pxNetworkBuffer, ( BaseType_t ) usLength ); + /* This function will fill in the eth addresses and send the packet */ + vReturnEthernetFrame( pxNetworkBuffer, pdFALSE ); + + if( pxNewBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxNewBuffer ); + } + } + } + } + } + + #endif /* ipconfigUSE_NBNS */ +#endif /* ipconfigUSE_DNS != 0 */ diff --git a/FreeRTOS/source/FreeRTOS_ICMP.c b/FreeRTOS/source/FreeRTOS_ICMP.c new file mode 100644 index 0000000..1fb7297 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_ICMP.c @@ -0,0 +1,240 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_ICMP.c + * @brief Implements the Internet Control Message Protocol for the FreeRTOS+TCP network stack. + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_ICMP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_DNS.h" + +/* + * Turns around an incoming ping request to convert it into a ping reply. + */ +#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) + static eFrameProcessingResult_t prvProcessICMPEchoRequest( ICMPPacket_t * const pxICMPPacket, + const NetworkBufferDescriptor_t * const pxNetworkBuffer ); +#endif /* ipconfigREPLY_TO_INCOMING_PINGS */ + +/* + * Processes incoming ping replies. The application callback function + * vApplicationPingReplyHook() is called with the results. + */ +#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) + static void prvProcessICMPEchoReply( ICMPPacket_t * const pxICMPPacket ); +#endif /* ipconfigSUPPORT_OUTGOING_PINGS */ + +#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) + +/** + * @brief Process an ICMP packet. Only echo requests and echo replies are recognised and handled. + * + * @param[in,out] pxICMPPacket: The IP packet that contains the ICMP message. + * + * @return eReleaseBuffer when the message buffer should be released, or eReturnEthernetFrame + * when the packet should be returned. + */ + eFrameProcessingResult_t ProcessICMPPacket( const NetworkBufferDescriptor_t * const pxNetworkBuffer ) + { + eFrameProcessingResult_t eReturn = eReleaseBuffer; + + iptraceICMP_PACKET_RECEIVED(); + + configASSERT( pxNetworkBuffer->xDataLength >= sizeof( ICMPPacket_t ) ); + + if( pxNetworkBuffer->xDataLength >= sizeof( ICMPPacket_t ) ) + { + /* Map the buffer onto a ICMP-Packet struct to easily access the + * fields of ICMP packet. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + ICMPPacket_t * pxICMPPacket = ( ( ICMPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); + + switch( pxICMPPacket->xICMPHeader.ucTypeOfMessage ) + { + case ipICMP_ECHO_REQUEST: + #if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) + { + eReturn = prvProcessICMPEchoRequest( pxICMPPacket, pxNetworkBuffer ); + } + #endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) */ + break; + + case ipICMP_ECHO_REPLY: + #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) + { + prvProcessICMPEchoReply( pxICMPPacket ); + } + #endif /* ipconfigSUPPORT_OUTGOING_PINGS */ + break; + + default: + /* Only ICMP echo packets are handled. */ + break; + } + } + + return eReturn; + } + +#endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) + +/** + * @brief Process an ICMP echo request. + * + * @param[in,out] pxICMPPacket: The IP packet that contains the ICMP message. + */ + static eFrameProcessingResult_t prvProcessICMPEchoRequest( ICMPPacket_t * const pxICMPPacket, + const NetworkBufferDescriptor_t * const pxNetworkBuffer ) + { + ICMPHeader_t * pxICMPHeader; + IPHeader_t * pxIPHeader; + + pxICMPHeader = &( pxICMPPacket->xICMPHeader ); + pxIPHeader = &( pxICMPPacket->xIPHeader ); + + /* HT:endian: changed back */ + iptraceSENDING_PING_REPLY( pxIPHeader->ulSourceIPAddress ); + + /* The checksum can be checked here - but a ping reply should be + * returned even if the checksum is incorrect so the other end can + * tell that the ping was received - even if the ping reply contains + * invalid data. */ + pxICMPHeader->ucTypeOfMessage = ( uint8_t ) ipICMP_ECHO_REPLY; + pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress; + pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; + /* Update the TTL field. */ + pxIPHeader->ucTimeToLive = ipconfigICMP_TIME_TO_LIVE; + + /* The stack doesn't support fragments, so the fragment offset field must always be zero. + * The header was never memset to zero, so set both the fragment offset and fragmentation flags in one go. + */ + #if ( ipconfigFORCE_IP_DONT_FRAGMENT != 0 ) + pxIPHeader->usFragmentOffset = ipFRAGMENT_FLAGS_DONT_FRAGMENT; + #else + pxIPHeader->usFragmentOffset = 0U; + #endif + + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) + { + /* calculate the IP header checksum, in case the driver won't do that. */ + pxIPHeader->usHeaderChecksum = 0x00U; + pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); + pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); + + /* calculate the ICMP checksum for an outgoing packet. */ + ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxICMPPacket, pxNetworkBuffer->xDataLength, pdTRUE ); + } + #else + { + /* Many EMAC peripherals will only calculate the ICMP checksum + * correctly if the field is nulled beforehand. */ + pxICMPHeader->usChecksum = 0U; + } + #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) */ + + return eReturnEthernetFrame; + } + +#endif /* ipconfigREPLY_TO_INCOMING_PINGS == 1 */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) + +/** + * @brief Process an ICMP echo reply. + * + * @param[in] pxICMPPacket: The IP packet that contains the ICMP message. + */ + static void prvProcessICMPEchoReply( ICMPPacket_t * const pxICMPPacket ) + { + ePingReplyStatus_t eStatus = eSuccess; + uint16_t usDataLength, usCount; + uint8_t * pucByte; + + /* Find the total length of the IP packet. */ + usDataLength = pxICMPPacket->xIPHeader.usLength; + usDataLength = FreeRTOS_ntohs( usDataLength ); + + /* Remove the length of the IP headers to obtain the length of the ICMP + * message itself. */ + usDataLength = ( uint16_t ) ( ( ( uint32_t ) usDataLength ) - ipSIZE_OF_IPv4_HEADER ); + + /* Remove the length of the ICMP header, to obtain the length of + * data contained in the ping. */ + usDataLength = ( uint16_t ) ( ( ( uint32_t ) usDataLength ) - ipSIZE_OF_ICMP_HEADER ); + + /* Checksum has already been checked before in prvProcessIPPacket */ + + /* Find the first byte of the data within the ICMP packet. */ + pucByte = ( uint8_t * ) pxICMPPacket; + pucByte = &( pucByte[ sizeof( ICMPPacket_t ) ] ); + + /* Check each byte. */ + for( usCount = 0; usCount < usDataLength; usCount++ ) + { + if( *pucByte != ( uint8_t ) ipECHO_DATA_FILL_BYTE ) + { + eStatus = eInvalidData; + break; + } + + pucByte++; + } + + /* Call back into the application to pass it the result. */ + vApplicationPingReplyHook( eStatus, pxICMPPacket->xICMPHeader.usIdentifier ); + } + +#endif /* if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/FreeRTOS_IP.c b/FreeRTOS/source/FreeRTOS_IP.c new file mode 100644 index 0000000..8efbbf5 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_IP.c @@ -0,0 +1,2171 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_IP.c + * @brief Implements the basic functionality for the FreeRTOS+TCP network stack. + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_ICMP.h" +#include "FreeRTOS_IP_Timers.h" +#include "FreeRTOS_IP_Utils.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_DNS.h" + +/* IPv4 multi-cast addresses range from 224.0.0.0.0 to 240.0.0.0. */ +#define ipFIRST_MULTI_CAST_IPv4 0xE0000000U /**< Lower bound of the IPv4 multicast address. */ +#define ipLAST_MULTI_CAST_IPv4 0xF0000000U /**< Higher bound of the IPv4 multicast address. */ + +/* The first byte in the IPv4 header combines the IP version (4) with + * with the length of the IP header. */ +#define ipIPV4_VERSION_HEADER_LENGTH_MIN 0x45U /**< Minimum IPv4 header length. */ +#define ipIPV4_VERSION_HEADER_LENGTH_MAX 0x4FU /**< Maximum IPv4 header length. */ + +/** @brief Maximum time to wait for an ARP resolution while holding a packet. */ +#ifndef ipARP_RESOLUTION_MAX_DELAY + #define ipARP_RESOLUTION_MAX_DELAY ( pdMS_TO_TICKS( 2000U ) ) +#endif + +#ifndef iptraceIP_TASK_STARTING + #define iptraceIP_TASK_STARTING() do {} while( ipFALSE_BOOL ) /**< Empty definition in case iptraceIP_TASK_STARTING is not defined. */ +#endif + +#if ( ( ipconfigUSE_TCP == 1 ) && !defined( ipTCP_TIMER_PERIOD_MS ) ) + /** @brief When initialising the TCP timer, give it an initial time-out of 1 second. */ + #define ipTCP_TIMER_PERIOD_MS ( 1000U ) +#endif + +/** @brief Defines how often the ARP timer callback function is executed. The time is + * shorter in the Windows simulator as simulated time is not real time. */ +#ifndef ipARP_TIMER_PERIOD_MS + #ifdef _WINDOWS_ + #define ipARP_TIMER_PERIOD_MS ( 500U ) /* For windows simulator builds. */ + #else + #define ipARP_TIMER_PERIOD_MS ( 10000U ) + #endif +#endif + +#if ( ipconfigUSE_TCP != 0 ) + +/** @brief Set to a non-zero value if one or more TCP message have been processed + * within the last round. */ + BaseType_t xProcessedTCPMessage; +#endif + +/** @brief If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet + * driver will filter incoming packets and only pass the stack those packets it + * considers need processing. In this case ipCONSIDER_FRAME_FOR_PROCESSING() can + * be #-defined away. If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 0 + * then the Ethernet driver will pass all received packets to the stack, and the + * stack must do the filtering itself. In this case ipCONSIDER_FRAME_FOR_PROCESSING + * needs to call eConsiderFrameForProcessing. + */ +#if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#endif +/*-----------------------------------------------------------*/ + +/** @brief The pointer to buffer with packet waiting for ARP resolution. */ +NetworkBufferDescriptor_t * pxARPWaitingNetworkBuffer = NULL; + +/*-----------------------------------------------------------*/ + +static void prvProcessIPEventsAndTimers( void ); + +/* + * The main TCP/IP stack processing task. This task receives commands/events + * from the network hardware drivers and tasks that are using sockets. It also + * maintains a set of protocol timers. + */ +static void prvIPTask( void * pvParameters ); + +/* + * Called when new data is available from the network interface. + */ +static void prvProcessEthernetPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ); + +#if ( ipconfigPROCESS_CUSTOM_ETHERNET_FRAMES != 0 ) + +/* + * The stack will call this user hook for all Ethernet frames that it + * does not support, i.e. other than IPv4, IPv6 and ARP ( for the moment ) + * If this hook returns eReleaseBuffer or eProcessBuffer, the stack will + * release and reuse the network buffer. If this hook returns + * eReturnEthernetFrame, that means user code has reused the network buffer + * to generate a response and the stack will send that response out. + * If this hook returns eFrameConsumed, the user code has ownership of the + * network buffer and has to release it when it's done. + */ + extern eFrameProcessingResult_t eApplicationProcessCustomFrameHook( NetworkBufferDescriptor_t * const pxNetworkBuffer ); +#endif /* ( ipconfigPROCESS_CUSTOM_ETHERNET_FRAMES != 0 ) */ + +/* + * Process incoming IP packets. + */ +static eFrameProcessingResult_t prvProcessIPPacket( IPPacket_t * pxIPPacket, + NetworkBufferDescriptor_t * const pxNetworkBuffer ); + +/* + * The network card driver has received a packet. In the case that it is part + * of a linked packet chain, walk through it to handle every message. + */ +static void prvHandleEthernetPacket( NetworkBufferDescriptor_t * pxBuffer ); + + +/* The function 'prvAllowIPPacket()' checks if a packets should be processed. */ +static eFrameProcessingResult_t prvAllowIPPacket( const IPPacket_t * const pxIPPacket, + const NetworkBufferDescriptor_t * const pxNetworkBuffer, + UBaseType_t uxHeaderLength ); + +#if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) + +/* Even when the driver takes care of checksum calculations, + * the IP-task will still check if the length fields are OK. */ + static BaseType_t xCheckSizeFields( const uint8_t * const pucEthernetBuffer, + size_t uxBufferLength ); +#endif /* ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) */ +/*-----------------------------------------------------------*/ + +/** @brief The queue used to pass events into the IP-task for processing. */ +QueueHandle_t xNetworkEventQueue = NULL; + +/** @brief The IP packet ID. */ +uint16_t usPacketIdentifier = 0U; + +/** @brief For convenience, a MAC address of all 0xffs is defined const for quick + * reference. */ +const MACAddress_t xBroadcastMACAddress = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; + +/** @brief Structure that stores the netmask, gateway address and DNS server addresses. */ +NetworkAddressingParameters_t xNetworkAddressing = { 0, 0, 0, 0, 0 }; + +/** @brief Default values for the above struct in case DHCP + * does not lead to a confirmed request. */ + +/* MISRA Ref 8.9.1 [File scoped variables] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */ +/* coverity[misra_c_2012_rule_8_9_violation] */ +NetworkAddressingParameters_t xDefaultAddressing = { 0, 0, 0, 0, 0 }; + +/** @brief Used to ensure network down events cannot be missed when they cannot be + * posted to the network event queue because the network event queue is already + * full. */ +static volatile BaseType_t xNetworkDownEventPending = pdFALSE; + +/** @brief Stores the handle of the task that handles the stack. The handle is used + * (indirectly) by some utility function to determine if the utility function is + * being called by a task (in which case it is ok to block) or by the IP task + * itself (in which case it is not ok to block). */ + +static TaskHandle_t xIPTaskHandle = NULL; + +/** @brief Simple set to pdTRUE or pdFALSE depending on whether the network is up or + * down (connected, not connected) respectively. */ +static BaseType_t xNetworkUp = pdFALSE; + +/** @brief Set to pdTRUE when the IP task is ready to start processing packets. */ +static BaseType_t xIPTaskInitialised = pdFALSE; + +#if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + /** @brief Keep track of the lowest amount of space in 'xNetworkEventQueue'. */ + static UBaseType_t uxQueueMinimumSpace = ipconfigEVENT_QUEUE_LENGTH; +#endif + +/*-----------------------------------------------------------*/ + +/* Coverity wants to make pvParameters const, which would make it incompatible. Leave the + * function signature as is. */ + +/** + * @brief The IP task handles all requests from the user application and the + * network interface. It receives messages through a FreeRTOS queue called + * 'xNetworkEventQueue'. prvIPTask() is the only task which has access to + * the data of the IP-stack, and so it has no need of using mutexes. + * + * @param[in] pvParameters: Not used. + */ + +/* MISRA Ref 8.13.1 [Not decorating a pointer to const parameter with const] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-813 */ +/* coverity[misra_c_2012_rule_8_13_violation] */ +static void prvIPTask( void * pvParameters ) +{ + /* Just to prevent compiler warnings about unused parameters. */ + ( void ) pvParameters; + + /* A possibility to set some additional task properties. */ + iptraceIP_TASK_STARTING(); + + /* Generate a dummy message to say that the network connection has gone + * down. This will cause this task to initialise the network interface. After + * this it is the responsibility of the network interface hardware driver to + * send this message if a previously connected network is disconnected. */ + FreeRTOS_NetworkDown(); + + #if ( ipconfigUSE_TCP == 1 ) + { + /* Initialise the TCP timer. */ + vTCPTimerReload( pdMS_TO_TICKS( ipTCP_TIMER_PERIOD_MS ) ); + } + #endif + + /* Mark the timer as inactive since we are not waiting on any ARP resolution as of now. */ + vIPSetARPResolutionTimerEnableState( pdFALSE ); + + /* Initialisation is complete and events can now be processed. */ + xIPTaskInitialised = pdTRUE; + + FreeRTOS_debug_printf( ( "prvIPTask started\n" ) ); + + /* Loop, processing IP events. */ + while( ipFOREVER() == pdTRUE ) + { + prvProcessIPEventsAndTimers(); + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Process the events sent to the IP task and process the timers. + */ +static void prvProcessIPEventsAndTimers( void ) +{ + IPStackEvent_t xReceivedEvent; + TickType_t xNextIPSleep; + FreeRTOS_Socket_t * pxSocket; + struct freertos_sockaddr xAddress; + + ipconfigWATCHDOG_TIMER(); + + /* Check the ARP, DHCP and TCP timers to see if there is any periodic + * or timeout processing to perform. */ + vCheckNetworkTimers(); + + /* Calculate the acceptable maximum sleep time. */ + xNextIPSleep = xCalculateSleepTime(); + + /* Wait until there is something to do. If the following call exits + * due to a time out rather than a message being received, set a + * 'NoEvent' value. */ + if( xQueueReceive( xNetworkEventQueue, ( void * ) &xReceivedEvent, xNextIPSleep ) == pdFALSE ) + { + xReceivedEvent.eEventType = eNoEvent; + } + + #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + { + if( xReceivedEvent.eEventType != eNoEvent ) + { + UBaseType_t uxCount; + + uxCount = uxQueueSpacesAvailable( xNetworkEventQueue ); + + if( uxQueueMinimumSpace > uxCount ) + { + uxQueueMinimumSpace = uxCount; + } + } + } + #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ + + iptraceNETWORK_EVENT_RECEIVED( xReceivedEvent.eEventType ); + + switch( xReceivedEvent.eEventType ) + { + case eNetworkDownEvent: + /* Attempt to establish a connection. */ + xNetworkUp = pdFALSE; + prvProcessNetworkDownEvent(); + break; + + case eNetworkRxEvent: + + /* The network hardware driver has received a new packet. A + * pointer to the received buffer is located in the pvData member + * of the received event structure. */ + prvHandleEthernetPacket( ( NetworkBufferDescriptor_t * ) xReceivedEvent.pvData ); + break; + + case eNetworkTxEvent: + + { + NetworkBufferDescriptor_t * pxDescriptor = ( NetworkBufferDescriptor_t * ) xReceivedEvent.pvData; + + /* Send a network packet. The ownership will be transferred to + * the driver, which will release it after delivery. */ + iptraceNETWORK_INTERFACE_OUTPUT( pxDescriptor->xDataLength, pxDescriptor->pucEthernetBuffer ); + ( void ) xNetworkInterfaceOutput( pxDescriptor, pdTRUE ); + } + + break; + + case eARPTimerEvent: + /* The ARP timer has expired, process the ARP cache. */ + vARPAgeCache(); + break; + + case eSocketBindEvent: + + /* FreeRTOS_bind (a user API) wants the IP-task to bind a socket + * to a port. The port number is communicated in the socket field + * usLocalPort. vSocketBind() will actually bind the socket and the + * API will unblock as soon as the eSOCKET_BOUND event is + * triggered. */ + pxSocket = ( ( FreeRTOS_Socket_t * ) xReceivedEvent.pvData ); + xAddress.sin_addr = 0U; /* For the moment. */ + xAddress.sin_port = FreeRTOS_ntohs( pxSocket->usLocalPort ); + pxSocket->usLocalPort = 0U; + ( void ) vSocketBind( pxSocket, &xAddress, sizeof( xAddress ), pdFALSE ); + + /* Before 'eSocketBindEvent' was sent it was tested that + * ( xEventGroup != NULL ) so it can be used now to wake up the + * user. */ + pxSocket->xEventBits |= ( EventBits_t ) eSOCKET_BOUND; + vSocketWakeUpUser( pxSocket ); + break; + + case eSocketCloseEvent: + + /* The user API FreeRTOS_closesocket() has sent a message to the + * IP-task to actually close a socket. This is handled in + * vSocketClose(). As the socket gets closed, there is no way to + * report back to the API, so the API won't wait for the result */ + ( void ) vSocketClose( ( ( FreeRTOS_Socket_t * ) xReceivedEvent.pvData ) ); + break; + + case eStackTxEvent: + + /* The network stack has generated a packet to send. A + * pointer to the generated buffer is located in the pvData + * member of the received event structure. */ + vProcessGeneratedUDPPacket( ( NetworkBufferDescriptor_t * ) xReceivedEvent.pvData ); + break; + + case eDHCPEvent: + /* The DHCP state machine needs processing. */ + #if ( ipconfigUSE_DHCP == 1 ) + { + uintptr_t uxState; + eDHCPState_t eState; + + /* MISRA Ref 11.6.1 [DHCP events and conversion to void] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-116 */ + /* coverity[misra_c_2012_rule_11_6_violation] */ + uxState = ( uintptr_t ) xReceivedEvent.pvData; + /* MISRA Ref 10.5.1 [DHCP events Enum] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-105 */ + /* coverity[misra_c_2012_rule_10_5_violation] */ + eState = ( eDHCPState_t ) uxState; + + /* Process DHCP messages for a given end-point. */ + vDHCPProcess( pdFALSE, eState ); + } + #endif /* ipconfigUSE_DHCP */ + break; + + case eSocketSelectEvent: + + /* FreeRTOS_select() has got unblocked by a socket event, + * vSocketSelect() will check which sockets actually have an event + * and update the socket field xSocketBits. */ + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + #if ( ipconfigSELECT_USES_NOTIFY != 0 ) + { + SocketSelectMessage_t * pxMessage = ( ( SocketSelectMessage_t * ) xReceivedEvent.pvData ); + vSocketSelect( pxMessage->pxSocketSet ); + ( void ) xTaskNotifyGive( pxMessage->xTaskhandle ); + } + #else + { + vSocketSelect( ( ( SocketSelect_t * ) xReceivedEvent.pvData ) ); + } + #endif /* ( ipconfigSELECT_USES_NOTIFY != 0 ) */ + #endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ + break; + + case eSocketSignalEvent: + #if ( ipconfigSUPPORT_SIGNALS != 0 ) + + /* Some task wants to signal the user of this socket in + * order to interrupt a call to recv() or a call to select(). */ + ( void ) FreeRTOS_SignalSocket( ( Socket_t ) xReceivedEvent.pvData ); + #endif /* ipconfigSUPPORT_SIGNALS */ + break; + + case eTCPTimerEvent: + #if ( ipconfigUSE_TCP == 1 ) + + /* Simply mark the TCP timer as expired so it gets processed + * the next time prvCheckNetworkTimers() is called. */ + vIPSetTCPTimerExpiredState( pdTRUE ); + #endif /* ipconfigUSE_TCP */ + break; + + case eTCPAcceptEvent: + + /* The API FreeRTOS_accept() was called, the IP-task will now + * check if the listening socket (communicated in pvData) actually + * received a new connection. */ + #if ( ipconfigUSE_TCP == 1 ) + pxSocket = ( ( FreeRTOS_Socket_t * ) xReceivedEvent.pvData ); + + if( xTCPCheckNewClient( pxSocket ) != pdFALSE ) + { + pxSocket->xEventBits |= ( EventBits_t ) eSOCKET_ACCEPT; + vSocketWakeUpUser( pxSocket ); + } + #endif /* ipconfigUSE_TCP */ + break; + + case eTCPNetStat: + + /* FreeRTOS_netstat() was called to have the IP-task print an + * overview of all sockets and their connections */ + #if ( ( ipconfigUSE_TCP == 1 ) && ( ipconfigHAS_PRINTF == 1 ) ) + vTCPNetStat(); + #endif /* ipconfigUSE_TCP */ + break; + + case eSocketSetDeleteEvent: + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + SocketSelect_t * pxSocketSet = ( SocketSelect_t * ) ( xReceivedEvent.pvData ); + + iptraceMEM_STATS_DELETE( pxSocketSet ); + vEventGroupDelete( pxSocketSet->xSelectGroup ); + vPortFree( ( void * ) pxSocketSet ); + } + #endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ + break; + + case eNoEvent: + /* xQueueReceive() returned because of a normal time-out. */ + break; + + default: + /* Should not get here. */ + break; + } + + if( xNetworkDownEventPending != pdFALSE ) + { + /* A network down event could not be posted to the network event + * queue because the queue was full. + * As this code runs in the IP-task, it can be done directly by + * calling prvProcessNetworkDownEvent(). */ + prvProcessNetworkDownEvent(); + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief The variable 'xIPTaskHandle' is declared static. This function + * gives read-only access to it. + * + * @return The handle of the IP-task. + */ +TaskHandle_t FreeRTOS_GetIPTaskHandle( void ) +{ + return xIPTaskHandle; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Perform all the required tasks when the network gets connected. + */ +void vIPNetworkUpCalls( void ) +{ + xNetworkUp = pdTRUE; + + #if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 ) + { + vApplicationIPNetworkEventHook( eNetworkUp ); + } + #endif /* ipconfigUSE_NETWORK_EVENT_HOOK */ + + #if ( ipconfigDNS_USE_CALLBACKS != 0 ) + { + /* The following function is declared in FreeRTOS_DNS.c and 'private' to + * this library */ + extern void vDNSInitialise( void ); + vDNSInitialise(); + } + #endif /* ipconfigDNS_USE_CALLBACKS != 0 */ + + /* Set remaining time to 0 so it will become active immediately. */ + vARPTimerReload( pdMS_TO_TICKS( ipARP_TIMER_PERIOD_MS ) ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief The variable 'xNetworkDownEventPending' is declared static. This function + * gives read-only access to it. + * + * @return pdTRUE if there a network-down event pending. pdFALSE otherwise. + */ +BaseType_t xIsNetworkDownEventPending( void ) +{ + return xNetworkDownEventPending; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Handle the incoming Ethernet packets. + * + * @param[in] pxBuffer: Linked/un-linked network buffer descriptor(s) + * to be processed. + */ +static void prvHandleEthernetPacket( NetworkBufferDescriptor_t * pxBuffer ) +{ + #if ( ipconfigUSE_LINKED_RX_MESSAGES == 0 ) + { + /* When ipconfigUSE_LINKED_RX_MESSAGES is set to 0 then only one + * buffer will be sent at a time. This is the default way for +TCP to pass + * messages from the MAC to the TCP/IP stack. */ + prvProcessEthernetPacket( pxBuffer ); + } + #else /* ipconfigUSE_LINKED_RX_MESSAGES */ + { + NetworkBufferDescriptor_t * pxNextBuffer; + + /* An optimisation that is useful when there is high network traffic. + * Instead of passing received packets into the IP task one at a time the + * network interface can chain received packets together and pass them into + * the IP task in one go. The packets are chained using the pxNextBuffer + * member. The loop below walks through the chain processing each packet + * in the chain in turn. */ + + /* While there is another packet in the chain. */ + while( pxBuffer != NULL ) + { + /* Store a pointer to the buffer after pxBuffer for use later on. */ + pxNextBuffer = pxBuffer->pxNextBuffer; + + /* Make it NULL to avoid using it later on. */ + pxBuffer->pxNextBuffer = NULL; + + prvProcessEthernetPacket( pxBuffer ); + pxBuffer = pxNextBuffer; + } + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ +} +/*-----------------------------------------------------------*/ + +/** + * @brief Send a network down event to the IP-task. If it fails to post a message, + * the failure will be noted in the variable 'xNetworkDownEventPending' + * and later on a 'network-down' event, it will be executed. + */ +void FreeRTOS_NetworkDown( void ) +{ + static const IPStackEvent_t xNetworkDownEvent = { eNetworkDownEvent, NULL }; + const TickType_t xDontBlock = ( TickType_t ) 0; + + /* Simply send the network task the appropriate event. */ + if( xSendEventStructToIPTask( &xNetworkDownEvent, xDontBlock ) != pdPASS ) + { + /* Could not send the message, so it is still pending. */ + xNetworkDownEventPending = pdTRUE; + } + else + { + /* Message was sent so it is not pending. */ + xNetworkDownEventPending = pdFALSE; + } + + iptraceNETWORK_DOWN(); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Utility function. Process Network Down event from ISR. + * This function is supposed to be called form an ISR. It is recommended + * - * use 'FreeRTOS_NetworkDown()', when calling from a normal task. + * + * @return If the event was processed successfully, then return pdTRUE. + * Else pdFALSE. + */ +BaseType_t FreeRTOS_NetworkDownFromISR( void ) +{ + static const IPStackEvent_t xNetworkDownEvent = { eNetworkDownEvent, NULL }; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + /* Simply send the network task the appropriate event. */ + if( xQueueSendToBackFromISR( xNetworkEventQueue, &xNetworkDownEvent, &xHigherPriorityTaskWoken ) != pdPASS ) + { + xNetworkDownEventPending = pdTRUE; + } + else + { + xNetworkDownEventPending = pdFALSE; + } + + iptraceNETWORK_DOWN(); + + return xHigherPriorityTaskWoken; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Obtain a buffer big enough for a UDP payload of given size. + * + * @param[in] uxRequestedSizeBytes: The size of the UDP payload. + * @param[in] uxBlockTimeTicks: Maximum amount of time for which this call + * can block. This value is capped internally. + * + * @return If a buffer was created then the pointer to that buffer is returned, + * else a NULL pointer is returned. + */ +void * FreeRTOS_GetUDPPayloadBuffer( size_t uxRequestedSizeBytes, + TickType_t uxBlockTimeTicks ) +{ + NetworkBufferDescriptor_t * pxNetworkBuffer; + void * pvReturn; + TickType_t uxBlockTime = uxBlockTimeTicks; + + /* Cap the block time. The reason for this is explained where + * ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS is defined (assuming an official + * FreeRTOSIPConfig.h header file is being used). */ + if( uxBlockTime > ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS ) + { + uxBlockTime = ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS; + } + + /* Obtain a network buffer with the required amount of storage. */ + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( sizeof( UDPPacket_t ) + uxRequestedSizeBytes, uxBlockTime ); + + if( pxNetworkBuffer != NULL ) + { + /* Set the actual packet size in case a bigger buffer was returned. */ + pxNetworkBuffer->xDataLength = sizeof( UDPPacket_t ) + uxRequestedSizeBytes; + /* Skip 3 headers. */ + pvReturn = &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( UDPPacket_t ) ] ); + } + else + { + pvReturn = NULL; + } + + return ( void * ) pvReturn; +} +/*-----------------------------------------------------------*/ + +/*_RB_ Should we add an error or assert if the task priorities are set such that the servers won't function as expected? */ + +/*_HT_ There was a bug in FreeRTOS_TCP_IP.c that only occurred when the applications' priority was too high. + * As that bug has been repaired, there is not an urgent reason to warn. + * It is better though to use the advised priority scheme. */ + +/** + * @brief Initialise the FreeRTOS-Plus-TCP network stack and initialise the IP-task. + * + * @param[in] ucIPAddress: Local IP address. + * @param[in] ucNetMask: Local netmask. + * @param[in] ucGatewayAddress: Local gateway address. + * @param[in] ucDNSServerAddress: Local DNS server address. + * @param[in] ucMACAddress: MAC address of the node. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + */ +/* coverity[single_use] */ +BaseType_t FreeRTOS_IPInit( const uint8_t ucIPAddress[ ipIP_ADDRESS_LENGTH_BYTES ], + const uint8_t ucNetMask[ ipIP_ADDRESS_LENGTH_BYTES ], + const uint8_t ucGatewayAddress[ ipIP_ADDRESS_LENGTH_BYTES ], + const uint8_t ucDNSServerAddress[ ipIP_ADDRESS_LENGTH_BYTES ], + const uint8_t ucMACAddress[ ipMAC_ADDRESS_LENGTH_BYTES ] ) +{ + BaseType_t xReturn = pdFALSE; + + /* Check that the configuration values are correct and that the IP-task has not + * already been initialized. */ + vPreCheckConfigs(); + + /* Attempt to create the queue used to communicate with the IP task. */ + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + static StaticQueue_t xNetworkEventStaticQueue; + static uint8_t ucNetworkEventQueueStorageArea[ ipconfigEVENT_QUEUE_LENGTH * sizeof( IPStackEvent_t ) ]; + xNetworkEventQueue = xQueueCreateStatic( ipconfigEVENT_QUEUE_LENGTH, + sizeof( IPStackEvent_t ), + ucNetworkEventQueueStorageArea, + &xNetworkEventStaticQueue ); + } + #else + { + xNetworkEventQueue = xQueueCreate( ipconfigEVENT_QUEUE_LENGTH, sizeof( IPStackEvent_t ) ); + configASSERT( xNetworkEventQueue != NULL ); + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + if( xNetworkEventQueue != NULL ) + { + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + /* A queue registry is normally used to assist a kernel aware + * debugger. If one is in use then it will be helpful for the debugger + * to show information about the network event queue. */ + vQueueAddToRegistry( xNetworkEventQueue, "NetEvnt" ); + } + #endif /* configQUEUE_REGISTRY_SIZE */ + + if( xNetworkBuffersInitialise() == pdPASS ) + { + /* Store the local IP and MAC address. */ + xNetworkAddressing.ulDefaultIPAddress = FreeRTOS_inet_addr_quick( ucIPAddress[ 0 ], ucIPAddress[ 1 ], ucIPAddress[ 2 ], ucIPAddress[ 3 ] ); + xNetworkAddressing.ulNetMask = FreeRTOS_inet_addr_quick( ucNetMask[ 0 ], ucNetMask[ 1 ], ucNetMask[ 2 ], ucNetMask[ 3 ] ); + xNetworkAddressing.ulGatewayAddress = FreeRTOS_inet_addr_quick( ucGatewayAddress[ 0 ], ucGatewayAddress[ 1 ], ucGatewayAddress[ 2 ], ucGatewayAddress[ 3 ] ); + xNetworkAddressing.ulDNSServerAddress = FreeRTOS_inet_addr_quick( ucDNSServerAddress[ 0 ], ucDNSServerAddress[ 1 ], ucDNSServerAddress[ 2 ], ucDNSServerAddress[ 3 ] ); + xNetworkAddressing.ulBroadcastAddress = ( xNetworkAddressing.ulDefaultIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask; + + ( void ) memcpy( &xDefaultAddressing, &xNetworkAddressing, sizeof( xDefaultAddressing ) ); + + #if ipconfigUSE_DHCP == 1 + { + /* The IP address is not set until DHCP completes. */ + *ipLOCAL_IP_ADDRESS_POINTER = 0x00U; + } + #else + { + /* The IP address is set from the value passed in. */ + *ipLOCAL_IP_ADDRESS_POINTER = xNetworkAddressing.ulDefaultIPAddress; + + /* Added to prevent ARP flood to gateway. Ensure the + * gateway is on the same subnet as the IP address. */ + if( xNetworkAddressing.ulGatewayAddress != 0U ) + { + 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 + * header fragment, which is used when sending UDP packets. */ + ( void ) memcpy( ipLOCAL_MAC_ADDRESS, ucMACAddress, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); + + /* Prepare the sockets interface. */ + vNetworkSocketsInit(); + + /* Create the task that processes Ethernet and stack events. */ + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + static StaticTask_t xIPTaskBuffer; + static StackType_t xIPTaskStack[ ipconfigIP_TASK_STACK_SIZE_WORDS ]; + xIPTaskHandle = xTaskCreateStatic( prvIPTask, + "IP-Task", + ipconfigIP_TASK_STACK_SIZE_WORDS, + NULL, + ipconfigIP_TASK_PRIORITY, + xIPTaskStack, + &xIPTaskBuffer ); + + if( xIPTaskHandle != NULL ) + { + xReturn = pdTRUE; + } + } + #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + { + xReturn = xTaskCreate( prvIPTask, + "IP-task", + ipconfigIP_TASK_STACK_SIZE_WORDS, + NULL, + ipconfigIP_TASK_PRIORITY, + &( xIPTaskHandle ) ); + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + } + else + { + FreeRTOS_debug_printf( ( "FreeRTOS_IPInit: xNetworkBuffersInitialise() failed\n" ) ); + + /* Clean up. */ + vQueueDelete( xNetworkEventQueue ); + xNetworkEventQueue = NULL; + } + } + else + { + FreeRTOS_debug_printf( ( "FreeRTOS_IPInit: Network event queue could not be created\n" ) ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the current address configuration. Only non-NULL pointers will + * be filled in. + * + * @param[out] pulIPAddress: The current IP-address assigned. + * @param[out] pulNetMask: The netmask used for current subnet. + * @param[out] pulGatewayAddress: The gateway address. + * @param[out] pulDNSServerAddress: The DNS server address. + */ +void FreeRTOS_GetAddressConfiguration( uint32_t * pulIPAddress, + uint32_t * pulNetMask, + uint32_t * pulGatewayAddress, + uint32_t * pulDNSServerAddress ) +{ + /* Return the address configuration to the caller. */ + + if( pulIPAddress != NULL ) + { + *pulIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; + } + + if( pulNetMask != NULL ) + { + *pulNetMask = xNetworkAddressing.ulNetMask; + } + + if( pulGatewayAddress != NULL ) + { + *pulGatewayAddress = xNetworkAddressing.ulGatewayAddress; + } + + if( pulDNSServerAddress != NULL ) + { + *pulDNSServerAddress = xNetworkAddressing.ulDNSServerAddress; + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Set the current network address configuration. Only non-NULL pointers will + * be used. + * + * @param[in] pulIPAddress: The current IP-address assigned. + * @param[in] pulNetMask: The netmask used for current subnet. + * @param[in] pulGatewayAddress: The gateway address. + * @param[in] pulDNSServerAddress: The DNS server address. + */ +void FreeRTOS_SetAddressConfiguration( const uint32_t * pulIPAddress, + const uint32_t * pulNetMask, + const uint32_t * pulGatewayAddress, + const uint32_t * pulDNSServerAddress ) +{ + /* Update the address configuration. */ + + if( pulIPAddress != NULL ) + { + *ipLOCAL_IP_ADDRESS_POINTER = *pulIPAddress; + } + + if( pulNetMask != NULL ) + { + xNetworkAddressing.ulNetMask = *pulNetMask; + } + + if( pulGatewayAddress != NULL ) + { + xNetworkAddressing.ulGatewayAddress = *pulGatewayAddress; + } + + if( pulDNSServerAddress != NULL ) + { + xNetworkAddressing.ulDNSServerAddress = *pulDNSServerAddress; + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Release the UDP payload buffer. + * + * @param[in] pvBuffer: Pointer to the UDP buffer that is to be released. + */ +void FreeRTOS_ReleaseUDPPayloadBuffer( void const * pvBuffer ) +{ + vReleaseNetworkBufferAndDescriptor( pxUDPPayloadBuffer_to_NetworkBuffer( pvBuffer ) ); +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Release the memory that was previously obtained by calling FreeRTOS_recv() + * with the flag 'FREERTOS_ZERO_COPY'. + * + * @param[in] xSocket: The socket that was read from. + * @param[in] pvBuffer: The buffer returned in the call to FreeRTOS_recv(). + * @param[in] xByteCount: The number of bytes that have been used. + * + * @return pdPASS if the buffer was released successfully, otherwise pdFAIL is returned. + */ + BaseType_t FreeRTOS_ReleaseTCPPayloadBuffer( Socket_t xSocket, + void const * pvBuffer, + BaseType_t xByteCount ) + { + BaseType_t xByteCountReleased; + BaseType_t xReturn = pdFAIL; + uint8_t * pucData; + size_t uxBytesAvailable = uxStreamBufferGetPtr( xSocket->u.xTCP.rxStream, &( pucData ) ); + + /* Make sure the pointer is correct. */ + configASSERT( pucData == ( uint8_t * ) pvBuffer ); + + /* Avoid releasing more bytes than available. */ + configASSERT( uxBytesAvailable >= ( size_t ) xByteCount ); + + if( ( pucData == pvBuffer ) && ( uxBytesAvailable >= ( size_t ) xByteCount ) ) + { + /* Call recv with NULL pointer to advance the circular buffer. */ + xByteCountReleased = FreeRTOS_recv( xSocket, + NULL, + ( size_t ) xByteCount, + FREERTOS_MSG_DONTWAIT ); + + configASSERT( xByteCountReleased == xByteCount ); + + if( xByteCountReleased == xByteCount ) + { + xReturn = pdPASS; + } + } + + return xReturn; + } +#endif /* ( ipconfigUSE_TCP == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) + +/** + * @brief Send a ping request to the given IP address. After receiving a reply, + * IP-task will call a user-supplied function 'vApplicationPingReplyHook()'. + * + * @param[in] ulIPAddress: The IP address to which the ping is to be sent. + * @param[in] uxNumberOfBytesToSend: Number of bytes in the ping request. + * @param[in] uxBlockTimeTicks: Maximum number of ticks to wait. + * + * @return If successfully sent to IP task for processing then the sequence + * number of the ping packet or else, pdFAIL. + */ + BaseType_t FreeRTOS_SendPingRequest( uint32_t ulIPAddress, + size_t uxNumberOfBytesToSend, + TickType_t uxBlockTimeTicks ) + { + NetworkBufferDescriptor_t * pxNetworkBuffer; + ICMPHeader_t * pxICMPHeader; + EthernetHeader_t * pxEthernetHeader; + BaseType_t xReturn = pdFAIL; + static uint16_t usSequenceNumber = 0; + uint8_t * pucChar; + size_t uxTotalLength; + IPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL }; + + uxTotalLength = uxNumberOfBytesToSend + sizeof( ICMPPacket_t ); + BaseType_t xEnoughSpace; + + if( uxNumberOfBytesToSend < ( ipconfigNETWORK_MTU - ( sizeof( IPHeader_t ) + sizeof( ICMPHeader_t ) ) ) ) + { + xEnoughSpace = pdTRUE; + } + else + { + xEnoughSpace = pdFALSE; + } + + if( ( uxGetNumberOfFreeNetworkBuffers() >= 4U ) && ( uxNumberOfBytesToSend >= 1U ) && ( xEnoughSpace != pdFALSE ) ) + { + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxTotalLength, uxBlockTimeTicks ); + + if( pxNetworkBuffer != NULL ) + { + pxEthernetHeader = ( ( EthernetHeader_t * ) pxNetworkBuffer->pucEthernetBuffer ); + pxEthernetHeader->usFrameType = ipIPv4_FRAME_TYPE; + + pxICMPHeader = ( ( ICMPHeader_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipIP_PAYLOAD_OFFSET ] ) ); + usSequenceNumber++; + + /* Fill in the basic header information. */ + pxICMPHeader->ucTypeOfMessage = ipICMP_ECHO_REQUEST; + pxICMPHeader->ucTypeOfService = 0; + pxICMPHeader->usIdentifier = usSequenceNumber; + pxICMPHeader->usSequenceNumber = usSequenceNumber; + + /* Find the start of the data. */ + pucChar = ( uint8_t * ) pxICMPHeader; + pucChar = &( pucChar[ sizeof( ICMPHeader_t ) ] ); + + /* Just memset the data to a fixed value. */ + ( void ) memset( pucChar, ( int ) ipECHO_DATA_FILL_BYTE, uxNumberOfBytesToSend ); + + /* The message is complete, IP and checksum's are handled by + * vProcessGeneratedUDPPacket */ + pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] = FREERTOS_SO_UDPCKSUM_OUT; + pxNetworkBuffer->ulIPAddress = ulIPAddress; + pxNetworkBuffer->usPort = ipPACKET_CONTAINS_ICMP_DATA; + /* xDataLength is the size of the total packet, including the Ethernet header. */ + pxNetworkBuffer->xDataLength = uxTotalLength; + + /* Send to the stack. */ + xStackTxEvent.pvData = pxNetworkBuffer; + + if( xSendEventStructToIPTask( &( xStackTxEvent ), uxBlockTimeTicks ) != pdPASS ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT ); + } + else + { + xReturn = ( BaseType_t ) usSequenceNumber; + } + } + } + else + { + /* The requested number of bytes will not fit in the available space + * in the network buffer. */ + } + + return xReturn; + } + +#endif /* ipconfigSUPPORT_OUTGOING_PINGS == 1 */ +/*-----------------------------------------------------------*/ + +/** + * @brief Send an event to the IP task. It calls 'xSendEventStructToIPTask' internally. + * + * @param[in] eEvent: The event to be sent. + * + * @return pdPASS if the event was sent (or the desired effect was achieved). Else, pdFAIL. + */ +BaseType_t xSendEventToIPTask( eIPEvent_t eEvent ) +{ + IPStackEvent_t xEventMessage; + const TickType_t xDontBlock = ( TickType_t ) 0; + + xEventMessage.eEventType = eEvent; + xEventMessage.pvData = ( void * ) NULL; + + return xSendEventStructToIPTask( &xEventMessage, xDontBlock ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Send an event (in form of struct) to the IP task to be processed. + * + * @param[in] pxEvent: The event to be sent. + * @param[in] uxTimeout: Timeout for waiting in case the queue is full. 0 for non-blocking calls. + * + * @return pdPASS if the event was sent (or the desired effect was achieved). Else, pdFAIL. + */ +BaseType_t xSendEventStructToIPTask( const IPStackEvent_t * pxEvent, + TickType_t uxTimeout ) +{ + BaseType_t xReturn, xSendMessage; + TickType_t uxUseTimeout = uxTimeout; + + if( ( xIPIsNetworkTaskReady() == pdFALSE ) && ( pxEvent->eEventType != eNetworkDownEvent ) ) + { + /* Only allow eNetworkDownEvent events if the IP task is not ready + * yet. Not going to attempt to send the message so the send failed. */ + xReturn = pdFAIL; + } + else + { + xSendMessage = pdTRUE; + + #if ( ipconfigUSE_TCP == 1 ) + { + if( pxEvent->eEventType == eTCPTimerEvent ) + { + /* TCP timer events are sent to wake the timer task when + * xTCPTimer has expired, but there is no point sending them if the + * IP task is already awake processing other message. */ + vIPSetTCPTimerExpiredState( pdTRUE ); + + if( uxQueueMessagesWaiting( xNetworkEventQueue ) != 0U ) + { + /* Not actually going to send the message but this is not a + * failure as the message didn't need to be sent. */ + xSendMessage = pdFALSE; + } + } + } + #endif /* ipconfigUSE_TCP */ + + if( xSendMessage != pdFALSE ) + { + /* The IP task cannot block itself while waiting for itself to + * respond. */ + if( ( xIsCallingFromIPTask() == pdTRUE ) && ( uxUseTimeout > ( TickType_t ) 0U ) ) + { + uxUseTimeout = ( TickType_t ) 0; + } + + xReturn = xQueueSendToBack( xNetworkEventQueue, pxEvent, uxUseTimeout ); + + if( xReturn == pdFAIL ) + { + /* A message should have been sent to the IP task, but wasn't. */ + FreeRTOS_debug_printf( ( "xSendEventStructToIPTask: CAN NOT ADD %d\n", pxEvent->eEventType ) ); + iptraceSTACK_TX_EVENT_LOST( pxEvent->eEventType ); + } + } + else + { + /* It was not necessary to send the message to process the event so + * even though the message was not sent the call was successful. */ + xReturn = pdPASS; + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Decide whether this packet should be processed or not based on the IP address in the packet. + * + * @param[in] pucEthernetBuffer: The ethernet packet under consideration. + * + * @return Enum saying whether to release or to process the packet. + */ +eFrameProcessingResult_t eConsiderFrameForProcessing( const uint8_t * const pucEthernetBuffer ) +{ + eFrameProcessingResult_t eReturn; + const EthernetHeader_t * pxEthernetHeader; + + /* Map the buffer onto Ethernet Header struct for easy access to fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxEthernetHeader = ( ( const EthernetHeader_t * ) pucEthernetBuffer ); + + if( memcmp( ipLOCAL_MAC_ADDRESS, pxEthernetHeader->xDestinationAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 ) + { + /* The packet was directed to this node - process it. */ + eReturn = eProcessBuffer; + } + else if( memcmp( xBroadcastMACAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 ) + { + /* The packet was a broadcast - process it. */ + eReturn = eProcessBuffer; + } + else + #if ( ipconfigUSE_LLMNR == 1 ) + if( memcmp( xLLMNR_MacAdress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 ) + { + /* The packet is a request for LLMNR - process it. */ + eReturn = eProcessBuffer; + } + else + #endif /* ipconfigUSE_LLMNR */ + { + /* The packet was not a broadcast, or for this node, just release + * the buffer without taking any other action. */ + eReturn = eReleaseBuffer; + } + + #if ( ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES == 1 ) + { + uint16_t usFrameType; + + if( eReturn == eProcessBuffer ) + { + usFrameType = pxEthernetHeader->usFrameType; + usFrameType = FreeRTOS_ntohs( usFrameType ); + + if( usFrameType <= 0x600U ) + { + /* Not an Ethernet II frame. */ + eReturn = eReleaseBuffer; + } + } + } + #endif /* ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES == 1 */ + + return eReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Process the Ethernet packet. + * + * @param[in,out] pxNetworkBuffer: the network buffer containing the ethernet packet. If the + * buffer is large enough, it may be reused to send a reply. + */ +static void prvProcessEthernetPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ + const EthernetHeader_t * pxEthernetHeader; + eFrameProcessingResult_t eReturned = eReleaseBuffer; + + configASSERT( pxNetworkBuffer != NULL ); + + iptraceNETWORK_INTERFACE_INPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer ); + + /* Interpret the Ethernet frame. */ + if( pxNetworkBuffer->xDataLength >= sizeof( EthernetHeader_t ) ) + { + eReturned = ipCONSIDER_FRAME_FOR_PROCESSING( pxNetworkBuffer->pucEthernetBuffer ); + + /* Map the buffer onto the Ethernet Header struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxEthernetHeader = ( ( const EthernetHeader_t * ) pxNetworkBuffer->pucEthernetBuffer ); + + /* The condition "eReturned == eProcessBuffer" must be true. */ + #if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) + if( eReturned == eProcessBuffer ) + #endif + { + /* Interpret the received Ethernet packet. */ + switch( pxEthernetHeader->usFrameType ) + { + case ipARP_FRAME_TYPE: + + /* The Ethernet frame contains an ARP packet. */ + if( pxNetworkBuffer->xDataLength >= sizeof( ARPPacket_t ) ) + { + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + eReturned = eARPProcessPacket( ( ( ARPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ) ); + } + else + { + eReturned = eReleaseBuffer; + } + + break; + + case ipIPv4_FRAME_TYPE: + + /* The Ethernet frame contains an IP packet. */ + if( pxNetworkBuffer->xDataLength >= sizeof( IPPacket_t ) ) + { + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + eReturned = prvProcessIPPacket( ( ( IPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ), pxNetworkBuffer ); + } + else + { + eReturned = eReleaseBuffer; + } + + break; + + default: + #if ( ipconfigPROCESS_CUSTOM_ETHERNET_FRAMES != 0 ) + /* Custom frame handler. */ + eReturned = eApplicationProcessCustomFrameHook( pxNetworkBuffer ); + #else + /* No other packet types are handled. Nothing to do. */ + eReturned = eReleaseBuffer; + #endif + break; + } + } + } + + /* Perform any actions that resulted from processing the Ethernet frame. */ + switch( eReturned ) + { + case eReturnEthernetFrame: + + /* The Ethernet frame will have been updated (maybe it was + * an ARP request or a PING request?) and should be sent back to + * its source. */ + vReturnEthernetFrame( pxNetworkBuffer, pdTRUE ); + + /* parameter pdTRUE: the buffer must be released once + * the frame has been transmitted */ + break; + + case eFrameConsumed: + + /* The frame is in use somewhere, don't release the buffer + * yet. */ + break; + + case eWaitingARPResolution: + + if( pxARPWaitingNetworkBuffer == NULL ) + { + pxARPWaitingNetworkBuffer = pxNetworkBuffer; + vIPTimerStartARPResolution( ipARP_RESOLUTION_MAX_DELAY ); + + iptraceDELAYED_ARP_REQUEST_STARTED(); + } + else + { + /* We are already waiting on one ARP resolution. This frame will be dropped. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + + iptraceDELAYED_ARP_BUFFER_FULL(); + } + + break; + + case eReleaseBuffer: + case eProcessBuffer: + default: + + /* The frame is not being used anywhere, and the + * NetworkBufferDescriptor_t structure containing the frame should + * just be released back to the list of free buffers. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + break; + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Is the IP address an IPv4 multicast address. + * + * @param[in] ulIPAddress: The IP address being checked. + * + * @return pdTRUE if the IP address is a multicast address or else, pdFALSE. + */ +BaseType_t xIsIPv4Multicast( uint32_t ulIPAddress ) +{ + BaseType_t xReturn; + uint32_t ulIP = FreeRTOS_ntohl( ulIPAddress ); + + if( ( ulIP >= ipFIRST_MULTI_CAST_IPv4 ) && ( ulIP < ipLAST_MULTI_CAST_IPv4 ) ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Check whether this IP packet is to be allowed or to be dropped. + * + * @param[in] pxIPPacket: The IP packet under consideration. + * @param[in] pxNetworkBuffer: The whole network buffer. + * @param[in] uxHeaderLength: The length of the header. + * + * @return Whether the packet should be processed or dropped. + */ +static eFrameProcessingResult_t prvAllowIPPacket( const IPPacket_t * const pxIPPacket, + const NetworkBufferDescriptor_t * const pxNetworkBuffer, + UBaseType_t uxHeaderLength ) +{ + eFrameProcessingResult_t eReturn = eProcessBuffer; + + #if ( ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 0 ) || ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) ) + const IPHeader_t * pxIPHeader = &( pxIPPacket->xIPHeader ); + #else + + /* or else, the parameter won't be used and the function will be optimised + * away */ + ( void ) pxIPPacket; + #endif + + #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 0 ) + { + /* In systems with a very small amount of RAM, it might be advantageous + * to have incoming messages checked earlier, by the network card driver. + * This method may decrease the usage of sparse network buffers. */ + uint32_t ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress; + uint32_t ulSourceIPAddress = pxIPHeader->ulSourceIPAddress; + + /* Ensure that the incoming packet is not fragmented because the stack + * doesn't not support IP fragmentation. All but the last fragment coming in will have their + * "more fragments" flag set and the last fragment will have a non-zero offset. + * We need to drop the packet in either of those cases. */ + if( ( ( pxIPHeader->usFragmentOffset & ipFRAGMENT_OFFSET_BIT_MASK ) != 0U ) || ( ( pxIPHeader->usFragmentOffset & ipFRAGMENT_FLAGS_MORE_FRAGMENTS ) != 0U ) ) + { + /* Can not handle, fragmented packet. */ + eReturn = eReleaseBuffer; + } + + /* Test if the length of the IP-header is between 20 and 60 bytes, + * and if the IP-version is 4. */ + else if( ( pxIPHeader->ucVersionHeaderLength < ipIPV4_VERSION_HEADER_LENGTH_MIN ) || + ( pxIPHeader->ucVersionHeaderLength > ipIPV4_VERSION_HEADER_LENGTH_MAX ) ) + { + /* Can not handle, unknown or invalid header version. */ + eReturn = eReleaseBuffer; + } + /* Is the packet for this IP address? */ + else if( ( ulDestinationIPAddress != *ipLOCAL_IP_ADDRESS_POINTER ) && + /* Is it the global broadcast address 255.255.255.255 ? */ + ( ulDestinationIPAddress != ipBROADCAST_IP_ADDRESS ) && + /* Is it a specific broadcast address 192.168.1.255 ? */ + ( ulDestinationIPAddress != xNetworkAddressing.ulBroadcastAddress ) && + #if ( ipconfigUSE_LLMNR == 1 ) + /* Is it the LLMNR multicast address? */ + ( ulDestinationIPAddress != ipLLMNR_IP_ADDR ) && + #endif + /* Or (during DHCP negotiation) we have no IP-address yet? */ + ( *ipLOCAL_IP_ADDRESS_POINTER != 0U ) ) + { + /* Packet is not for this node, release it */ + eReturn = eReleaseBuffer; + } + /* Is the source address correct? */ + else if( ( FreeRTOS_ntohl( ulSourceIPAddress ) & 0xffU ) == 0xffU ) + { + /* The source address cannot be broadcast address. Replying to this + * packet may cause network storms. Drop the packet. */ + eReturn = eReleaseBuffer; + } + else if( ( memcmp( xBroadcastMACAddress.ucBytes, + pxIPPacket->xEthernetHeader.xDestinationAddress.ucBytes, + sizeof( MACAddress_t ) ) == 0 ) && + ( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xffU ) != 0xffU ) ) + { + /* Ethernet address is a broadcast address, but the IP address is not a + * broadcast address. */ + eReturn = eReleaseBuffer; + } + else if( memcmp( xBroadcastMACAddress.ucBytes, + pxIPPacket->xEthernetHeader.xSourceAddress.ucBytes, + sizeof( MACAddress_t ) ) == 0 ) + { + /* Ethernet source is a broadcast address. Drop the packet. */ + eReturn = eReleaseBuffer; + } + else if( xIsIPv4Multicast( ulSourceIPAddress ) == pdTRUE ) + { + /* Source is a multicast IP address. Drop the packet in conformity with RFC 1112 section 7.2. */ + eReturn = eReleaseBuffer; + } + else + { + /* Packet is not fragmented, destination is this device, source IP and MAC + * addresses are correct. */ + } + } + #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ + + #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) + { + /* Some drivers of NIC's with checksum-offloading will enable the above + * define, so that the checksum won't be checked again here */ + if( eReturn == eProcessBuffer ) + { + /* Is the IP header checksum correct? + * + * NOTE: When the checksum of IP header is calculated while not omitting + * the checksum field, the resulting value of the checksum always is 0xffff + * which is denoted by ipCORRECT_CRC. See this wiki for more information: + * https://en.wikipedia.org/wiki/IPv4_header_checksum#Verifying_the_IPv4_header_checksum + * and this RFC: https://tools.ietf.org/html/rfc1624#page-4 + */ + if( usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ( size_t ) uxHeaderLength ) != ipCORRECT_CRC ) + { + /* Check sum in IP-header not correct. */ + eReturn = eReleaseBuffer; + } + /* Is the upper-layer checksum (TCP/UDP/ICMP) correct? */ + else if( usGenerateProtocolChecksum( ( uint8_t * ) ( pxNetworkBuffer->pucEthernetBuffer ), pxNetworkBuffer->xDataLength, pdFALSE ) != ipCORRECT_CRC ) + { + /* Protocol checksum not accepted. */ + eReturn = eReleaseBuffer; + } + else + { + /* The checksum of the received packet is OK. */ + } + } + } + #else /* if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) */ + { + if( eReturn == eProcessBuffer ) + { + if( xCheckSizeFields( ( uint8_t * ) ( pxNetworkBuffer->pucEthernetBuffer ), pxNetworkBuffer->xDataLength ) != pdPASS ) + { + /* Some of the length checks were not successful. */ + eReturn = eReleaseBuffer; + } + } + + #if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) + { + /* Check if this is a UDP packet without a checksum. */ + if( eReturn == eProcessBuffer ) + { + /* ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS is defined as 0, + * and so UDP packets carrying a protocol checksum of 0, will + * be dropped. */ + + /* Identify the next protocol. */ + if( pxIPPacket->xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) + { + const ProtocolPacket_t * pxProtPack; + + /* pxProtPack will point to the offset were the protocols begin. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxProtPack = ( ( ProtocolPacket_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ uxHeaderLength - ipSIZE_OF_IPv4_HEADER ] ) ); + + if( pxProtPack->xUDPPacket.xUDPHeader.usChecksum == ( uint16_t ) 0U ) + { + #if ( ipconfigHAS_PRINTF != 0 ) + { + static BaseType_t xCount = 0; + + /* Exclude this from branch coverage as this is only used for debugging. */ + if( xCount < 5 ) /* LCOV_EXCL_BR_LINE */ + { + FreeRTOS_printf( ( "prvAllowIPPacket: UDP packet from %xip without CRC dropped\n", + FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ) ) ); + xCount++; + } + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + + /* Protocol checksum not accepted. */ + eReturn = eReleaseBuffer; + } + } + } + } + #endif /* ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */ + + /* to avoid warning unused parameters */ + ( void ) uxHeaderLength; + } + #endif /* ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 */ + + return eReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Process an IP-packet. + * + * @param[in] pxIPPacket: The IP packet to be processed. + * @param[in] pxNetworkBuffer: The networkbuffer descriptor having the IP packet. + * + * @return An enum to show whether the packet should be released/kept/processed etc. + */ +static eFrameProcessingResult_t prvProcessIPPacket( IPPacket_t * pxIPPacket, + NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ + eFrameProcessingResult_t eReturn; + IPHeader_t * pxIPHeader = &( pxIPPacket->xIPHeader ); + size_t uxLength = ( size_t ) pxIPHeader->ucVersionHeaderLength; + UBaseType_t uxHeaderLength = ( UBaseType_t ) ( ( uxLength & 0x0FU ) << 2 ); + uint8_t ucProtocol; + + /* Bound the calculated header length: take away the Ethernet header size, + * then check if the IP header is claiming to be longer than the remaining + * total packet size. Also check for minimal header field length. */ + if( ( uxHeaderLength > ( pxNetworkBuffer->xDataLength - ipSIZE_OF_ETH_HEADER ) ) || + ( uxHeaderLength < ipSIZE_OF_IPv4_HEADER ) ) + { + eReturn = eReleaseBuffer; + } + else + { + ucProtocol = pxIPPacket->xIPHeader.ucProtocol; + /* Check if the IP headers are acceptable and if it has our destination. */ + eReturn = prvAllowIPPacket( pxIPPacket, pxNetworkBuffer, uxHeaderLength ); + + /* MISRA Ref 14.3.1 [Configuration dependent invariant] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-143 */ + /* coverity[misra_c_2012_rule_14_3_violation] */ + /* coverity[cond_const] */ + if( eReturn == eProcessBuffer ) + { + /* Are there IP-options. */ + if( uxHeaderLength > ipSIZE_OF_IPv4_HEADER ) + { + /* The size of the IP-header is larger than 20 bytes. + * The extra space is used for IP-options. */ + #if ( ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS != 0 ) + { + /* All structs of headers expect a IP header size of 20 bytes + * IP header options were included, we'll ignore them and cut them out. */ + const size_t optlen = ( ( size_t ) uxHeaderLength ) - ipSIZE_OF_IPv4_HEADER; + /* From: the previous start of UDP/ICMP/TCP data. */ + const uint8_t * pucSource = ( const uint8_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( EthernetHeader_t ) + uxHeaderLength ] ); + /* To: the usual start of UDP/ICMP/TCP data at offset 20 (decimal ) from IP header. */ + uint8_t * pucTarget = ( uint8_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( EthernetHeader_t ) + ipSIZE_OF_IPv4_HEADER ] ); + /* How many: total length minus the options and the lower headers. */ + const size_t xMoveLen = pxNetworkBuffer->xDataLength - ( optlen + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_ETH_HEADER ); + + ( void ) memmove( pucTarget, pucSource, xMoveLen ); + pxNetworkBuffer->xDataLength -= optlen; + pxIPHeader->usLength = FreeRTOS_htons( FreeRTOS_ntohs( pxIPHeader->usLength ) - optlen ); + + /* Rewrite the Version/IHL byte to indicate that this packet has no IP options. */ + pxIPHeader->ucVersionHeaderLength = ( pxIPHeader->ucVersionHeaderLength & 0xF0U ) | /* High nibble is the version. */ + ( ( ipSIZE_OF_IPv4_HEADER >> 2 ) & 0x0FU ); + } + #else /* if ( ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS != 0 ) */ + { + /* 'ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS' is not set, so packets carrying + * IP-options will be dropped. */ + eReturn = eReleaseBuffer; + } + #endif /* if ( ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS != 0 ) */ + } + + /* MISRA Ref 14.3.1 [Configuration dependent invariant] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-143 */ + /* coverity[misra_c_2012_rule_14_3_violation] */ + /* coverity[const] */ + if( eReturn != eReleaseBuffer ) + { + /* Add the IP and MAC addresses to the ARP table if they are not + * already there - otherwise refresh the age of the existing + * entry. */ + if( ucProtocol != ( uint8_t ) ipPROTOCOL_UDP ) + { + if( xCheckRequiresARPResolution( pxNetworkBuffer ) == pdTRUE ) + { + eReturn = eWaitingARPResolution; + } + else + { + /* IP address is not on the same subnet, ARP table can be updated. + * Refresh the ARP cache with the IP/MAC-address of the received + * packet. For UDP packets, this will be done later in + * xProcessReceivedUDPPacket(), as soon as it's know that the message + * will be handled. This will prevent the ARP cache getting + * overwritten with the IP address of useless broadcast packets. + */ + vARPRefreshCacheEntry( &( pxIPPacket->xEthernetHeader.xSourceAddress ), pxIPHeader->ulSourceIPAddress ); + } + } + + if( eReturn != eWaitingARPResolution ) + { + switch( ucProtocol ) + { + case ipPROTOCOL_ICMP: + + /* The IP packet contained an ICMP frame. Don't bother checking + * the ICMP checksum, as if it is wrong then the wrong data will + * also be returned, and the source of the ping will know something + * went wrong because it will not be able to validate what it + * receives. */ + #if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) + { + if( pxIPHeader->ulDestinationIPAddress == *ipLOCAL_IP_ADDRESS_POINTER ) + { + eReturn = ProcessICMPPacket( pxNetworkBuffer ); + } + } + #endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ + break; + + case ipPROTOCOL_UDP: + { + /* The IP packet contained a UDP frame. */ + + /* Map the buffer onto a UDP-Packet struct to easily access the + * fields of UDP packet. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const UDPPacket_t * pxUDPPacket = ( ( const UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); + uint16_t usLength; + BaseType_t xIsWaitingARPResolution = pdFALSE; + + /* Note the header values required prior to the checksum + * generation as the checksum pseudo header may clobber some of + * these values. */ + usLength = FreeRTOS_ntohs( pxUDPPacket->xUDPHeader.usLength ); + + if( ( pxNetworkBuffer->xDataLength < sizeof( UDPPacket_t ) ) || + ( ( ( size_t ) usLength ) < sizeof( UDPHeader_t ) ) ) + { + eReturn = eReleaseBuffer; + } + else if( usLength > ( FreeRTOS_ntohs( pxIPHeader->usLength ) - ipSIZE_OF_IPv4_HEADER ) ) + { + /* The UDP packet is bigger than the IP-payload. Something is wrong, drop the packet. */ + eReturn = eReleaseBuffer; + } + else + { + size_t uxPayloadSize_1, uxPayloadSize_2; + + /* Ensure that downstream UDP packet handling has the lesser + * of: the actual network buffer Ethernet frame length, or + * the sender's UDP packet header payload length, minus the + * size of the UDP header. + * + * The size of the UDP packet structure in this implementation + * includes the size of the Ethernet header, the size of + * the IP header, and the size of the UDP header. */ + uxPayloadSize_1 = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t ); + uxPayloadSize_2 = ( ( size_t ) usLength ) - sizeof( UDPHeader_t ); + + if( uxPayloadSize_1 > uxPayloadSize_2 ) + { + pxNetworkBuffer->xDataLength = uxPayloadSize_2 + sizeof( UDPPacket_t ); + } + + /* Fields in pxNetworkBuffer (usPort, ulIPAddress) are network order. */ + pxNetworkBuffer->usPort = pxUDPPacket->xUDPHeader.usSourcePort; + pxNetworkBuffer->ulIPAddress = pxUDPPacket->xIPHeader.ulSourceIPAddress; + + /* ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM: + * In some cases, the upper-layer checksum has been calculated + * by the NIC driver. */ + + /* Pass the packet payload to the UDP sockets + * implementation. */ + if( xProcessReceivedUDPPacket( pxNetworkBuffer, + pxUDPPacket->xUDPHeader.usDestinationPort, + &( xIsWaitingARPResolution ) ) == pdPASS ) + { + eReturn = eFrameConsumed; + } + else + { + /* Is this packet to be set aside for ARP resolution. */ + if( xIsWaitingARPResolution == pdTRUE ) + { + eReturn = eWaitingARPResolution; + } + } + } + } + break; + + #if ipconfigUSE_TCP == 1 + case ipPROTOCOL_TCP: + + if( xProcessReceivedTCPPacket( pxNetworkBuffer ) == pdPASS ) + { + eReturn = eFrameConsumed; + } + + /* Setting this variable will cause xTCPTimerCheck() + * to be called just before the IP-task blocks. */ + xProcessedTCPMessage++; + break; + #endif /* if ipconfigUSE_TCP == 1 */ + default: + /* Not a supported frame type. */ + eReturn = eReleaseBuffer; + break; + } + } + } + } + } + + return eReturn; +} + +/*-----------------------------------------------------------*/ + +#if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) + +/** + * @brief Although the driver will take care of checksum calculations, the IP-task + * will still check if the length fields are OK. + * + * @param[in] pucEthernetBuffer: The Ethernet packet received. + * @param[in] uxBufferLength: The total number of bytes received. + * + * @return pdPASS when the length fields in the packet OK, pdFAIL when the packet + * should be dropped. + */ + static BaseType_t xCheckSizeFields( const uint8_t * const pucEthernetBuffer, + size_t uxBufferLength ) + { + size_t uxLength; + const IPPacket_t * pxIPPacket; + UBaseType_t uxIPHeaderLength; + uint8_t ucProtocol; + uint16_t usLength; + uint16_t ucVersionHeaderLength; + size_t uxMinimumLength; + BaseType_t xResult = pdFAIL; + + DEBUG_DECLARE_TRACE_VARIABLE( BaseType_t, xLocation, 0 ); + + do + { + /* Check for minimum packet size: Ethernet header and an IP-header, 34 bytes */ + if( uxBufferLength < sizeof( IPPacket_t ) ) + { + DEBUG_SET_TRACE_VARIABLE( xLocation, 1 ); + break; + } + + /* Map the buffer onto a IP-Packet struct to easily access the + * fields of the IP packet. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxIPPacket = ( ( const IPPacket_t * ) pucEthernetBuffer ); + + ucVersionHeaderLength = pxIPPacket->xIPHeader.ucVersionHeaderLength; + + /* Test if the length of the IP-header is between 20 and 60 bytes, + * and if the IP-version is 4. */ + if( ( ucVersionHeaderLength < ipIPV4_VERSION_HEADER_LENGTH_MIN ) || + ( ucVersionHeaderLength > ipIPV4_VERSION_HEADER_LENGTH_MAX ) ) + { + DEBUG_SET_TRACE_VARIABLE( xLocation, 2 ); + break; + } + + ucVersionHeaderLength = ( ucVersionHeaderLength & ( uint8_t ) 0x0FU ) << 2; + uxIPHeaderLength = ( UBaseType_t ) ucVersionHeaderLength; + + /* Check if the complete IP-header is transferred. */ + if( uxBufferLength < ( ipSIZE_OF_ETH_HEADER + uxIPHeaderLength ) ) + { + DEBUG_SET_TRACE_VARIABLE( xLocation, 3 ); + break; + } + + /* Check if the complete IP-header plus protocol data have been transferred: */ + usLength = pxIPPacket->xIPHeader.usLength; + usLength = FreeRTOS_ntohs( usLength ); + + if( uxBufferLength < ( size_t ) ( ipSIZE_OF_ETH_HEADER + ( size_t ) usLength ) ) + { + DEBUG_SET_TRACE_VARIABLE( xLocation, 4 ); + break; + } + + /* Identify the next protocol. */ + ucProtocol = pxIPPacket->xIPHeader.ucProtocol; + + /* Switch on the Layer 3/4 protocol. */ + if( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) + { + /* Expect at least a complete UDP header. */ + uxMinimumLength = uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_UDP_HEADER; + } + else if( ucProtocol == ( uint8_t ) ipPROTOCOL_TCP ) + { + uxMinimumLength = uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_TCP_HEADER; + } + else if( ( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) || + ( ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) ) + { + uxMinimumLength = uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_ICMP_HEADER; + } + else + { + /* Unhandled protocol, other than ICMP, IGMP, UDP, or TCP. */ + DEBUG_SET_TRACE_VARIABLE( xLocation, 5 ); + break; + } + + if( uxBufferLength < uxMinimumLength ) + { + DEBUG_SET_TRACE_VARIABLE( xLocation, 6 ); + break; + } + + uxLength = ( size_t ) usLength; + uxLength -= ( ( uint16_t ) uxIPHeaderLength ); /* normally, minus 20. */ + + if( ( uxLength < ( ( size_t ) sizeof( UDPHeader_t ) ) ) || + ( uxLength > ( ( size_t ) ipconfigNETWORK_MTU - ( size_t ) uxIPHeaderLength ) ) ) + { + /* For incoming packets, the length is out of bound: either + * too short or too long. For outgoing packets, there is a + * serious problem with the format/length. */ + DEBUG_SET_TRACE_VARIABLE( xLocation, 7 ); + break; + } + + xResult = pdPASS; + } while( ipFALSE_BOOL ); + + if( xResult != pdPASS ) + { + /* NOP if ipconfigHAS_PRINTF != 1 */ + FreeRTOS_printf( ( "xCheckSizeFields: location %ld\n", xLocation ) ); + } + + return xResult; + } +#endif /* ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) */ +/*-----------------------------------------------------------*/ + +/* This function is used in other files, has external linkage e.g. in + * FreeRTOS_DNS.c. Not to be made static. */ + +/** + * @brief Send the Ethernet frame after checking for some conditions. + * + * @param[in,out] pxNetworkBuffer: The network buffer which is to be sent. + * @param[in] xReleaseAfterSend: Whether this network buffer is to be released or not. + */ +void vReturnEthernetFrame( NetworkBufferDescriptor_t * pxNetworkBuffer, + BaseType_t xReleaseAfterSend ) +{ + EthernetHeader_t * pxEthernetHeader; +/* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + NetworkBufferDescriptor_t * pxNewBuffer; + #endif + + #if ( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) + { + if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES ) + { + BaseType_t xIndex; + + FreeRTOS_printf( ( "vReturnEthernetFrame: length %u\n", ( unsigned ) pxNetworkBuffer->xDataLength ) ); + + for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ ) + { + pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0U; + } + + pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; + } + } + #endif /* if( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) */ + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + if( xReleaseAfterSend == pdFALSE ) + { + pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, pxNetworkBuffer->xDataLength ); + + if( pxNewBuffer != NULL ) + { + xReleaseAfterSend = pdTRUE; + /* Want no rounding up. */ + pxNewBuffer->xDataLength = pxNetworkBuffer->xDataLength; + } + + pxNetworkBuffer = pxNewBuffer; + } + + if( pxNetworkBuffer != NULL ) + #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + { + /* Map the Buffer to Ethernet Header struct for easy access to fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxEthernetHeader = ( ( EthernetHeader_t * ) pxNetworkBuffer->pucEthernetBuffer ); + + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + /* Swap source and destination MAC addresses. */ + pvCopySource = &pxEthernetHeader->xSourceAddress; + pvCopyDest = &pxEthernetHeader->xDestinationAddress; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxEthernetHeader->xDestinationAddress ) ); + + pvCopySource = ipLOCAL_MAC_ADDRESS; + pvCopyDest = &pxEthernetHeader->xSourceAddress; + ( void ) memcpy( pvCopyDest, pvCopySource, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); + + /* Send! */ + iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer ); + ( void ) xNetworkInterfaceOutput( pxNetworkBuffer, xReleaseAfterSend ); + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Returns the IP address of the NIC. + * + * @return The IP address of the NIC. + */ +uint32_t FreeRTOS_GetIPAddress( void ) +{ + return *ipLOCAL_IP_ADDRESS_POINTER; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Sets the IP address of the NIC. + * + * @param[in] ulIPAddress: IP address of the NIC to be set. + */ +void FreeRTOS_SetIPAddress( uint32_t ulIPAddress ) +{ + *ipLOCAL_IP_ADDRESS_POINTER = ulIPAddress; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the gateway address of the subnet. + * + * @return The IP-address of the gateway, zero if a gateway is + * not used/defined. + */ +uint32_t FreeRTOS_GetGatewayAddress( void ) +{ + return xNetworkAddressing.ulGatewayAddress; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the DNS server address. + * + * @return The IP address of the DNS server. + */ +uint32_t FreeRTOS_GetDNSServerAddress( void ) +{ + return xNetworkAddressing.ulDNSServerAddress; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the netmask for the subnet. + * + * @return The 32 bit netmask for the subnet. + */ +uint32_t FreeRTOS_GetNetmask( void ) +{ + return xNetworkAddressing.ulNetMask; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Update the MAC address. + * + * @param[in] ucMACAddress: the MAC address to be set. + */ +void FreeRTOS_UpdateMACAddress( const uint8_t ucMACAddress[ ipMAC_ADDRESS_LENGTH_BYTES ] ) +{ + /* Copy the MAC address at the start of the default packet header fragment. */ + ( void ) memcpy( ipLOCAL_MAC_ADDRESS, ucMACAddress, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the MAC address. + * + * @return The pointer to MAC address. + */ +const uint8_t * FreeRTOS_GetMACAddress( void ) +{ + return ipLOCAL_MAC_ADDRESS; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Set the netmask for the subnet. + * + * @param[in] ulNetmask: The 32 bit netmask of the subnet. + */ +void FreeRTOS_SetNetmask( uint32_t ulNetmask ) +{ + xNetworkAddressing.ulNetMask = ulNetmask; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Set the gateway address. + * + * @param[in] ulGatewayAddress: The gateway address. + */ +void FreeRTOS_SetGatewayAddress( uint32_t ulGatewayAddress ) +{ + xNetworkAddressing.ulGatewayAddress = ulGatewayAddress; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Returns whether the IP task is ready. + * + * @return pdTRUE if IP task is ready, else pdFALSE. + */ +BaseType_t xIPIsNetworkTaskReady( void ) +{ + return xIPTaskInitialised; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Returns whether this node is connected to network or not. + * + * @return pdTRUE if network is connected, else pdFALSE. + */ +BaseType_t FreeRTOS_IsNetworkUp( void ) +{ + return xNetworkUp; +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + +/** + * @brief Get the minimum space in the IP task queue. + * + * @return The minimum possible space in the IP task queue. + */ + UBaseType_t uxGetMinimumIPQueueSpace( void ) + { + return uxQueueMinimumSpace; + } +#endif +/*-----------------------------------------------------------*/ + +/* Provide access to private members for verification. */ +#ifdef FREERTOS_TCP_ENABLE_VERIFICATION + #include "aws_freertos_ip_verification_access_ip_define.h" +#endif diff --git a/FreeRTOS/source/FreeRTOS_IP_Timers.c b/FreeRTOS/source/FreeRTOS_IP_Timers.c new file mode 100644 index 0000000..d2b1c6c --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_IP_Timers.c @@ -0,0 +1,483 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_ICMP.c + * @brief Implements the Internet Control Message Protocol for the FreeRTOS+TCP network stack. + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Timers.h" +#include "FreeRTOS_IP_Utils.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_DNS.h" + +/* + * Utility functions for the light weight IP timers. + */ +static void prvIPTimerStart( IPTimer_t * pxTimer, + TickType_t xTime ); +static BaseType_t prvIPTimerCheck( IPTimer_t * pxTimer ); +static void prvIPTimerReload( IPTimer_t * pxTimer, + TickType_t xTime ); + +/* + * A timer for each of the following processes, all of which need attention on a + * regular basis + */ + +/** @brief Timer to limit the maximum time a packet should be stored while + * awaiting an ARP resolution. */ +static IPTimer_t xARPResolutionTimer; + +/** @brief ARP timer, to check its table entries. */ +static IPTimer_t xARPTimer; +#if ( ipconfigUSE_DHCP != 0 ) + /** @brief DHCP timer, to send requests and to renew a reservation. */ + static IPTimer_t xDHCPTimer; +#endif +#if ( ipconfigUSE_TCP != 0 ) + /** @brief TCP timer, to check for timeouts, resends. */ + static IPTimer_t xTCPTimer; +#endif +#if ( ipconfigDNS_USE_CALLBACKS != 0 ) + /** @brief DNS timer, to check for timeouts when looking-up a domain. */ + static IPTimer_t xDNSTimer; +#endif + +/** + * @brief Calculate the maximum sleep time remaining. It will go through all + * timers to see which timer will expire first. That will be the amount + * of time to block in the next call to xQueueReceive(). + * + * @return The maximum sleep time or ipconfigMAX_IP_TASK_SLEEP_TIME, + * whichever is smaller. + */ +TickType_t xCalculateSleepTime( void ) +{ + TickType_t uxMaximumSleepTime; + + /* Start with the maximum sleep time, then check this against the remaining + * time in any other timers that are active. */ + uxMaximumSleepTime = ipconfigMAX_IP_TASK_SLEEP_TIME; + + if( xARPTimer.bActive != pdFALSE_UNSIGNED ) + { + if( xARPTimer.ulRemainingTime < uxMaximumSleepTime ) + { + uxMaximumSleepTime = xARPTimer.ulRemainingTime; + } + } + + #if ( ipconfigUSE_DHCP == 1 ) + { + if( xDHCPTimer.bActive != pdFALSE_UNSIGNED ) + { + if( xDHCPTimer.ulRemainingTime < uxMaximumSleepTime ) + { + uxMaximumSleepTime = xDHCPTimer.ulRemainingTime; + } + } + } + #endif /* ipconfigUSE_DHCP */ + + #if ( ipconfigUSE_TCP == 1 ) + { + if( xTCPTimer.bActive != pdFALSE_UNSIGNED ) + { + if( xTCPTimer.ulRemainingTime < uxMaximumSleepTime ) + { + uxMaximumSleepTime = xTCPTimer.ulRemainingTime; + } + } + } + #endif + + #if ( ipconfigDNS_USE_CALLBACKS != 0 ) + { + if( xDNSTimer.bActive != pdFALSE_UNSIGNED ) + { + if( xDNSTimer.ulRemainingTime < uxMaximumSleepTime ) + { + uxMaximumSleepTime = xDNSTimer.ulRemainingTime; + } + } + } + #endif + + return uxMaximumSleepTime; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Check the network timers (ARP/DHCP/DNS/TCP) and if they are + * expired, send an event to the IP-Task. + */ +void vCheckNetworkTimers( void ) +{ + /* Is it time for ARP processing? */ + if( prvIPTimerCheck( &xARPTimer ) != pdFALSE ) + { + ( void ) xSendEventToIPTask( eARPTimerEvent ); + } + + /* Is the ARP resolution timer expired? */ + if( prvIPTimerCheck( &xARPResolutionTimer ) != pdFALSE ) + { + if( pxARPWaitingNetworkBuffer != NULL ) + { + /* Disable the ARP resolution timer. */ + vIPSetARPResolutionTimerEnableState( pdFALSE ); + + /* We have waited long enough for the ARP response. Now, free the network + * buffer. */ + vReleaseNetworkBufferAndDescriptor( pxARPWaitingNetworkBuffer ); + + /* Clear the pointer. */ + pxARPWaitingNetworkBuffer = NULL; + + iptraceDELAYED_ARP_TIMER_EXPIRED(); + } + } + + #if ( ipconfigUSE_DHCP == 1 ) + { + /* Is it time for DHCP processing? */ + if( prvIPTimerCheck( &xDHCPTimer ) != pdFALSE ) + { + ( void ) xSendDHCPEvent(); + } + } + #endif /* ipconfigUSE_DHCP */ + + #if ( ipconfigDNS_USE_CALLBACKS != 0 ) + { + /* Is it time for DNS processing? */ + if( prvIPTimerCheck( &xDNSTimer ) != pdFALSE ) + { + vDNSCheckCallBack( NULL ); + } + } + #endif /* ipconfigDNS_USE_CALLBACKS */ + + #if ( ipconfigUSE_TCP == 1 ) + { + BaseType_t xWillSleep; + TickType_t xNextTime; + BaseType_t xCheckTCPSockets; + + /* If the IP task has messages waiting to be processed then + * it will not sleep in any case. */ + if( uxQueueMessagesWaiting( xNetworkEventQueue ) == 0U ) + { + xWillSleep = pdTRUE; + } + else + { + xWillSleep = pdFALSE; + } + + /* Sockets need to be checked if the TCP timer has expired. */ + xCheckTCPSockets = prvIPTimerCheck( &xTCPTimer ); + + /* Sockets will also be checked if there are TCP messages but the + * message queue is empty (indicated by xWillSleep being true). */ + if( ( xProcessedTCPMessage != pdFALSE ) && ( xWillSleep != pdFALSE ) ) + { + xCheckTCPSockets = pdTRUE; + } + + if( xCheckTCPSockets != pdFALSE ) + { + /* Attend to the sockets, returning the period after which the + * check must be repeated. */ + xNextTime = xTCPTimerCheck( xWillSleep ); + prvIPTimerStart( &xTCPTimer, xNextTime ); + xProcessedTCPMessage = 0; + } + } + + /* See if any socket was planned to be closed. */ + vSocketCloseNextTime( NULL ); + + /* See if any reusable socket needs to go back to 'eTCP_LISTEN' state. */ + vSocketListenNextTime( NULL ); + #endif /* ipconfigUSE_TCP == 1 */ +} +/*-----------------------------------------------------------*/ + +/** + * @brief Start an IP timer. The IP-task has its own implementation of a timer + * called 'IPTimer_t', which is based on the FreeRTOS 'TimeOut_t'. + * + * @param[in] pxTimer: Pointer to the IP timer. When zero, the timer is marked + * as expired. + * @param[in] xTime: Time to be loaded into the IP timer. + */ +static void prvIPTimerStart( IPTimer_t * pxTimer, + TickType_t xTime ) +{ + vTaskSetTimeOutState( &pxTimer->xTimeOut ); + pxTimer->ulRemainingTime = xTime; + + if( xTime == ( TickType_t ) 0 ) + { + pxTimer->bExpired = pdTRUE_UNSIGNED; + } + else + { + pxTimer->bExpired = pdFALSE_UNSIGNED; + } + + pxTimer->bActive = pdTRUE_UNSIGNED; +} +/*-----------------------------------------------------------*/ + +void vIPTimerStartARPResolution( TickType_t xTime ) +{ + prvIPTimerStart( &( xARPResolutionTimer ), xTime ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Sets the reload time of an IP timer and restarts it. + * + * @param[in] pxTimer: Pointer to the IP timer. + * @param[in] xTime: Time to be reloaded into the IP timer. + */ +static void prvIPTimerReload( IPTimer_t * pxTimer, + TickType_t xTime ) +{ + pxTimer->ulReloadTime = xTime; + prvIPTimerStart( pxTimer, xTime ); +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + void vTCPTimerReload( TickType_t xTime ) + { + prvIPTimerReload( &xTCPTimer, xTime ); + } +#endif +/*-----------------------------------------------------------*/ + +void vARPTimerReload( TickType_t xTime ) +{ + prvIPTimerReload( &xARPTimer, xTime ); +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_DHCP == 1 ) + +/** + * @brief Reload the DHCP timer. + * + * @param[in] ulLeaseTime: The reload value. + */ + void vDHCPTimerReload( TickType_t xLeaseTime ) + { + prvIPTimerReload( &xDHCPTimer, xLeaseTime ); + } +#endif /* ipconfigUSE_DHCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigDNS_USE_CALLBACKS != 0 ) + +/** + * @brief Reload the DNS timer. + * + * @param[in] ulCheckTime: The reload value. + */ + void vDNSTimerReload( uint32_t ulCheckTime ) + { + prvIPTimerReload( &xDNSTimer, ulCheckTime ); + } +#endif /* ipconfigDNS_USE_CALLBACKS != 0 */ +/*-----------------------------------------------------------*/ + +/** + * @brief Check the IP timer to see whether an IP event should be processed or not. + * + * @param[in] pxTimer: Pointer to the IP timer. + * + * @return If the timer is expired then pdTRUE is returned. Else pdFALSE. + */ +static BaseType_t prvIPTimerCheck( IPTimer_t * pxTimer ) +{ + BaseType_t xReturn; + + if( pxTimer->bActive == pdFALSE_UNSIGNED ) + { + /* The timer is not enabled. */ + xReturn = pdFALSE; + } + else + { + /* The timer might have set the bExpired flag already, if not, check the + * value of xTimeOut against ulRemainingTime. */ + if( pxTimer->bExpired == pdFALSE_UNSIGNED ) + { + if( xTaskCheckForTimeOut( &( pxTimer->xTimeOut ), &( pxTimer->ulRemainingTime ) ) != pdFALSE ) + { + pxTimer->bExpired = pdTRUE_UNSIGNED; + } + } + + if( pxTimer->bExpired != pdFALSE_UNSIGNED ) + { + prvIPTimerStart( pxTimer, pxTimer->ulReloadTime ); + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Enable/disable the TCP timer. + * + * @param[in] xExpiredState: pdTRUE - set as expired; pdFALSE - set as non-expired. + */ + void vIPSetTCPTimerExpiredState( BaseType_t xExpiredState ) + { + xTCPTimer.bActive = pdTRUE_UNSIGNED; + + if( xExpiredState != pdFALSE ) + { + xTCPTimer.bExpired = pdTRUE_UNSIGNED; + } + else + { + xTCPTimer.bExpired = pdFALSE_UNSIGNED; + } + } +/*-----------------------------------------------------------*/ +#endif /* if ( ipconfigUSE_TCP == 1 ) */ + +/** + * @brief Enable/disable the ARP timer. + * + * @param[in] xEnableState: pdTRUE - enable timer; pdFALSE - disable timer. + */ +void vIPSetARPTimerEnableState( BaseType_t xEnableState ) +{ + if( xEnableState != pdFALSE ) + { + xARPTimer.bActive = pdTRUE_UNSIGNED; + } + else + { + xARPTimer.bActive = pdFALSE_UNSIGNED; + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Enable or disable the ARP resolution timer. + * + * @param[in] xEnableState: pdTRUE if the timer must be enabled, pdFALSE otherwise. + */ +void vIPSetARPResolutionTimerEnableState( BaseType_t xEnableState ) +{ + if( xEnableState != pdFALSE ) + { + xARPResolutionTimer.bActive = pdTRUE_UNSIGNED; + } + else + { + xARPResolutionTimer.bActive = pdFALSE_UNSIGNED; + } +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_DHCP == 1 ) + +/** + * @brief Enable/disable the DHCP timer. + * + * @param[in] xEnableState: pdTRUE - enable timer; pdFALSE - disable timer. + */ + void vIPSetDHCPTimerEnableState( BaseType_t xEnableState ) + { + if( xEnableState != pdFALSE ) + { + xDHCPTimer.bActive = pdTRUE_UNSIGNED; + } + else + { + xDHCPTimer.bActive = pdFALSE_UNSIGNED; + } + } +#endif /* ipconfigUSE_DHCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigDNS_USE_CALLBACKS == 1 ) + +/** + * @brief Enable/disable the DNS timer. + * + * @param[in] xEnableState: pdTRUE - enable timer; pdFALSE - disable timer. + */ + void vIPSetDNSTimerEnableState( BaseType_t xEnableState ) + { + if( xEnableState != 0 ) + { + xDNSTimer.bActive = pdTRUE_UNSIGNED; + } + else + { + xDNSTimer.bActive = pdFALSE_UNSIGNED; + } + } + +#endif /* ipconfigDNS_USE_CALLBACKS == 1 */ +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/FreeRTOS_IP_Utils.c b/FreeRTOS/source/FreeRTOS_IP_Utils.c new file mode 100644 index 0000000..2283c67 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_IP_Utils.c @@ -0,0 +1,1338 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_IP_Utils.c + * @brief Implements the basic functionality for the FreeRTOS+TCP network stack. + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Utils.h" +#include "FreeRTOS_IP_Timers.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_DNS.h" + +/* Used to ensure the structure packing is having the desired effect. The + * 'volatile' is used to prevent compiler warnings about comparing a constant with + * a constant. */ +#ifndef _lint + #define ipEXPECTED_EthernetHeader_t_SIZE ( ( size_t ) 14 ) /**< Ethernet Header size in bytes. */ + #define ipEXPECTED_ARPHeader_t_SIZE ( ( size_t ) 28 ) /**< ARP header size in bytes. */ + #define ipEXPECTED_IPHeader_t_SIZE ( ( size_t ) 20 ) /**< IP header size in bytes. */ + #define ipEXPECTED_IGMPHeader_t_SIZE ( ( size_t ) 8 ) /**< IGMP header size in bytes. */ + #define ipEXPECTED_ICMPHeader_t_SIZE ( ( size_t ) 8 ) /**< ICMP header size in bytes. */ + #define ipEXPECTED_UDPHeader_t_SIZE ( ( size_t ) 8 ) /**< UDP header size in bytes. */ + #define ipEXPECTED_TCPHeader_t_SIZE ( ( size_t ) 20 ) /**< TCP header size in bytes. */ +#endif + +/** @brief Time delay between repeated attempts to initialise the network hardware. */ +#ifndef ipINITIALISATION_RETRY_DELAY + #define ipINITIALISATION_RETRY_DELAY ( pdMS_TO_TICKS( 3000U ) ) +#endif + +#if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 ) + /* used for unit testing */ + +/* MISRA Ref 8.9.1 [File scoped variables] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */ +/* coverity[misra_c_2012_rule_8_9_violation] */ +/* coverity[single_use] */ + static BaseType_t xCallEventHook = pdFALSE; +#endif + +#if ( ipconfigHAS_PRINTF != 0 ) + /** @brief Last value of minimum buffer count. */ + static UBaseType_t uxLastMinBufferCount = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; + +/** @brief Last value of minimum size. Used in printing resource stats. */ + static size_t uxMinLastSize = 0u; +#endif + +#if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) && ( ipconfigHAS_PRINTF != 0 ) + static UBaseType_t uxLastMinQueueSpace = 0; +#endif + +/** + * Used in checksum calculation. + */ +typedef union _xUnion32 +{ + uint32_t u32; /**< The 32-bit member of the union. */ + uint16_t u16[ 2 ]; /**< The array of 2 16-bit members of the union. */ + uint8_t u8[ 4 ]; /**< The array of 4 8-bit members of the union. */ +} xUnion32; + +/** + * Used in checksum calculation. + */ +typedef union _xUnionPtr +{ + const uint32_t * u32ptr; /**< The pointer member to a 32-bit variable. */ + const uint16_t * u16ptr; /**< The pointer member to a 16-bit variable. */ + const uint8_t * u8ptr; /**< The pointer member to an 8-bit variable. */ +} xUnionPtr; + +/* + * Returns the network buffer descriptor that owns a given packet buffer. + */ +static NetworkBufferDescriptor_t * prvPacketBuffer_to_NetworkBuffer( const void * pvBuffer, + size_t uxOffset ); + +#if ( ipconfigUSE_DHCP != 0 ) + +/** + * @brief Create a DHCP event. + * + * @return pdPASS or pdFAIL, depending on whether xSendEventStructToIPTask() + * succeeded. + */ + BaseType_t xSendDHCPEvent( void ) + { + IPStackEvent_t xEventMessage; + const TickType_t uxDontBlock = 0U; + uintptr_t uxOption = ( uintptr_t ) eGetDHCPState(); + + xEventMessage.eEventType = eDHCPEvent; + + /* MISRA Ref 11.6.1 [DHCP events and conversion to void] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-116 */ + /* coverity[misra_c_2012_rule_11_6_violation] */ + xEventMessage.pvData = ( void * ) uxOption; + + return xSendEventStructToIPTask( &xEventMessage, uxDontBlock ); + } +/*-----------------------------------------------------------*/ +#endif /* ( ipconfigUSE_DHCP != 0 ) */ + +/** + * @brief Set multicast MAC address. + * + * @param[in] ulIPAddress: IP address. + * @param[out] pxMACAddress: Pointer to MAC address. + */ +void vSetMultiCastIPv4MacAddress( uint32_t ulIPAddress, + MACAddress_t * pxMACAddress ) +{ + uint32_t ulIP = FreeRTOS_ntohl( ulIPAddress ); + + pxMACAddress->ucBytes[ 0 ] = ( uint8_t ) 0x01U; + pxMACAddress->ucBytes[ 1 ] = ( uint8_t ) 0x00U; + pxMACAddress->ucBytes[ 2 ] = ( uint8_t ) 0x5EU; + pxMACAddress->ucBytes[ 3 ] = ( uint8_t ) ( ( ulIP >> 16 ) & 0x7fU ); /* Use 7 bits. */ + pxMACAddress->ucBytes[ 4 ] = ( uint8_t ) ( ( ulIP >> 8 ) & 0xffU ); /* Use 8 bits. */ + pxMACAddress->ucBytes[ 5 ] = ( uint8_t ) ( ( ulIP ) & 0xffU ); /* Use 8 bits. */ +} +/*-----------------------------------------------------------*/ + + +/** + * @brief Duplicate the given network buffer descriptor with a modified length. + * + * @param[in] pxNetworkBuffer: The network buffer to be duplicated. + * @param[in] uxNewLength: The length for the new buffer. + * + * @return If properly duplicated, then the duplicate network buffer or else, NULL. + */ +NetworkBufferDescriptor_t * pxDuplicateNetworkBufferWithDescriptor( const NetworkBufferDescriptor_t * const pxNetworkBuffer, + size_t uxNewLength ) +{ + NetworkBufferDescriptor_t * pxNewBuffer; + size_t uxLengthToCopy = uxNewLength; + + /* This function is only used when 'ipconfigZERO_COPY_TX_DRIVER' is set to 1. + * The transmit routine wants to have ownership of the network buffer + * descriptor, because it will pass the buffer straight to DMA. */ + pxNewBuffer = pxGetNetworkBufferWithDescriptor( uxNewLength, ( TickType_t ) 0 ); + + if( pxNewBuffer != NULL ) + { + /* Get the minimum of both values to copy the data. */ + if( uxLengthToCopy > pxNetworkBuffer->xDataLength ) + { + uxLengthToCopy = pxNetworkBuffer->xDataLength; + } + + /* Set the actual packet size in case a bigger buffer than requested + * was returned. */ + pxNewBuffer->xDataLength = uxNewLength; + + /* Copy the original packet information. */ + pxNewBuffer->ulIPAddress = pxNetworkBuffer->ulIPAddress; + pxNewBuffer->usPort = pxNetworkBuffer->usPort; + pxNewBuffer->usBoundPort = pxNetworkBuffer->usBoundPort; + ( void ) memcpy( pxNewBuffer->pucEthernetBuffer, pxNetworkBuffer->pucEthernetBuffer, uxLengthToCopy ); + } + + return pxNewBuffer; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the network buffer descriptor from the packet buffer. + * + * @param[in] pvBuffer: The pointer to packet buffer. + * @param[in] uxOffset: Additional offset (such as the packet length of UDP packet etc.). + * + * @return The network buffer descriptor if the alignment is correct. Else a NULL is returned. + */ +static NetworkBufferDescriptor_t * prvPacketBuffer_to_NetworkBuffer( const void * pvBuffer, + size_t uxOffset ) +{ + uintptr_t uxBuffer; + NetworkBufferDescriptor_t * pxResult; + + if( pvBuffer == NULL ) + { + pxResult = NULL; + } + else + { + /* Obtain the network buffer from the zero copy pointer. */ + + /* MISRA Ref 11.6.2 [Pointer arithmetic and hidden pointer] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-116 */ + /* coverity[misra_c_2012_rule_11_6_violation] */ + uxBuffer = ( uintptr_t ) pvBuffer; + + /* The input here is a pointer to a packet buffer plus some offset. Subtract + * this offset, and also the size of the header in the network buffer, usually + * 8 + 2 bytes. */ + uxBuffer -= ( uxOffset + ipBUFFER_PADDING ); + + /* Here a pointer was placed to the network descriptor. As a + * pointer is dereferenced, make sure it is well aligned. */ + if( ( uxBuffer & ( ( ( uintptr_t ) sizeof( uxBuffer ) ) - 1U ) ) == ( uintptr_t ) 0U ) + { + /* MISRA Ref 11.4.2 [Validation of pointer alignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + pxResult = *( ( NetworkBufferDescriptor_t ** ) uxBuffer ); + } + else + { + pxResult = NULL; + } + } + + return pxResult; +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) || ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + +/** + * @brief Get the network buffer from the packet buffer. + * + * @param[in] pvBuffer: Pointer to the packet buffer. + * + * @return The network buffer if the alignment is correct. Else a NULL is returned. + */ + NetworkBufferDescriptor_t * pxPacketBuffer_to_NetworkBuffer( const void * pvBuffer ) + { + return prvPacketBuffer_to_NetworkBuffer( pvBuffer, 0U ); + } + +#endif /* ( ipconfigZERO_COPY_TX_DRIVER != 0 ) || ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ +/*-----------------------------------------------------------*/ + +/** + * @brief Get the network buffer from the UDP Payload buffer. + * + * @param[in] pvBuffer: Pointer to the UDP payload buffer. + * + * @return The network buffer if the alignment is correct. Else a NULL is returned. + */ +NetworkBufferDescriptor_t * pxUDPPayloadBuffer_to_NetworkBuffer( const void * pvBuffer ) +{ + return prvPacketBuffer_to_NetworkBuffer( pvBuffer, sizeof( UDPPacket_t ) ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Function to check whether the current context belongs to + * the IP-task. + * + * @return If the current context belongs to the IP-task, then pdTRUE is + * returned. Else pdFALSE is returned. + * + * @note Very important: the IP-task is not allowed to call its own API's, + * because it would easily get into a dead-lock. + */ +BaseType_t xIsCallingFromIPTask( void ) +{ + BaseType_t xReturn; + const struct tskTaskControlBlock * const xCurrentHandle = xTaskGetCurrentTaskHandle(); + const struct tskTaskControlBlock * const xCurrentIPTaskHandle = FreeRTOS_GetIPTaskHandle(); + + if( xCurrentHandle == xCurrentIPTaskHandle ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Process a 'Network down' event and complete required processing. + */ +/* MISRA Ref 8.9.1 [File scoped variables] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */ +/* coverity[misra_c_2012_rule_8_9_violation] */ +/* coverity[single_use] */ +void prvProcessNetworkDownEvent( void ) +{ + /* Stop the ARP timer while there is no network. */ + vIPSetARPTimerEnableState( pdFALSE ); + + #if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 ) + { + /* The first network down event is generated by the IP stack itself to + * initialise the network hardware, so do not call the network down event + * the first time through. */ + if( xCallEventHook == pdTRUE ) + { + vApplicationIPNetworkEventHook( eNetworkDown ); + } + + xCallEventHook = pdTRUE; + } + #endif /* if ipconfigUSE_NETWORK_EVENT_HOOK == 1 */ + + /* Per the ARP Cache Validation section of https://tools.ietf.org/html/rfc1122, + * treat network down as a "delivery problem" and flush the ARP cache for this + * interface. */ + FreeRTOS_ClearARP(); + + /* The network has been disconnected (or is being initialised for the first + * time). Perform whatever hardware processing is necessary to bring it up + * again, or wait for it to be available again. This is hardware dependent. */ + if( xNetworkInterfaceInitialise() != pdPASS ) + { + /* Ideally the network interface initialisation function will only + * return when the network is available. In case this is not the case, + * wait a while before retrying the initialisation. */ + vTaskDelay( ipINITIALISATION_RETRY_DELAY ); + FreeRTOS_NetworkDown(); + } + else + { + /* Set remaining time to 0 so it will become active immediately. */ + #if ipconfigUSE_DHCP == 1 + { + /* The network is not up until DHCP has completed. */ + vDHCPProcess( pdTRUE, eInitialWait ); + } + #else + { + /* Perform any necessary 'network up' processing. */ + vIPNetworkUpCalls(); + } + #endif + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Check the values of configuration options and assert on it. Also verify that the IP-task + * has not already been initialized. + */ +void vPreCheckConfigs( void ) +{ + /* This function should only be called once. */ + configASSERT( xIPIsNetworkTaskReady() == pdFALSE ); + configASSERT( xNetworkEventQueue == NULL ); + configASSERT( FreeRTOS_GetIPTaskHandle() == NULL ); + + #if ( configASSERT_DEFINED == 1 ) + { + volatile size_t uxSize = sizeof( uintptr_t ); + + if( uxSize == 8U ) + { + /* This is a 64-bit platform, make sure there is enough space in + * pucEthernetBuffer to store a pointer. */ + configASSERT( ipconfigBUFFER_PADDING >= 14 ); + /* But it must have this strange alignment: */ + configASSERT( ( ( ( ipconfigBUFFER_PADDING ) + 2 ) % 4 ) == 0 ); + } + + /* LCOV_EXCL_BR_START */ + uxSize = ipconfigNETWORK_MTU; + /* Check if MTU is big enough. */ + configASSERT( uxSize >= ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + ipconfigTCP_MSS ) ); + + uxSize = sizeof( EthernetHeader_t ); + /* Check structure packing is correct. */ + configASSERT( uxSize == ipEXPECTED_EthernetHeader_t_SIZE ); + + uxSize = sizeof( ARPHeader_t ); + configASSERT( uxSize == ipEXPECTED_ARPHeader_t_SIZE ); + + uxSize = sizeof( IPHeader_t ); + configASSERT( uxSize == ipEXPECTED_IPHeader_t_SIZE ); + + uxSize = sizeof( ICMPHeader_t ); + configASSERT( uxSize == ipEXPECTED_ICMPHeader_t_SIZE ); + + uxSize = sizeof( UDPHeader_t ); + configASSERT( uxSize == ipEXPECTED_UDPHeader_t_SIZE ); + /* LCOV_EXCL_BR_STOP */ + } + #endif /* if ( configASSERT_DEFINED == 1 ) */ +} + +/** + * @brief Generate or check the protocol checksum of the data sent in the first parameter. + * At the same time, the length of the packet and the length of the different layers + * will be checked. + * + * @param[in] pucEthernetBuffer: The Ethernet buffer for which the checksum is to be calculated + * or checked. + * @param[in] uxBufferLength: the total number of bytes received, or the number of bytes written + * in the packet buffer. + * @param[in] xOutgoingPacket: Whether this is an outgoing packet or not. + * + * @return When xOutgoingPacket is false: the error code can be either: ipINVALID_LENGTH, + * ipUNHANDLED_PROTOCOL, ipWRONG_CRC, or ipCORRECT_CRC. + * When xOutgoingPacket is true: either ipINVALID_LENGTH, ipUNHANDLED_PROTOCOL, + * or ipCORRECT_CRC. + */ +uint16_t usGenerateProtocolChecksum( uint8_t * pucEthernetBuffer, + size_t uxBufferLength, + BaseType_t xOutgoingPacket ) +{ + uint32_t ulLength; + uint16_t usChecksum; /* The checksum as calculated. */ + uint16_t usChecksumFound = 0U; /* The checksum as found in the incoming packet. */ + const IPPacket_t * pxIPPacket; + UBaseType_t uxIPHeaderLength; + ProtocolPacket_t * pxProtPack; + uint8_t ucProtocol; + + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + const char * pcType; + #endif + uint16_t usLength; + uint16_t ucVersionHeaderLength; + DEBUG_DECLARE_TRACE_VARIABLE( BaseType_t, xLocation, 0 ); + + /* Introduce a do-while loop to allow use of break statements. + * Note: MISRA prohibits use of 'goto', thus replaced with breaks. */ + do + { + /* Check for minimum packet size. */ + if( uxBufferLength < sizeof( IPPacket_t ) ) + { + usChecksum = ipINVALID_LENGTH; + DEBUG_SET_TRACE_VARIABLE( xLocation, 1 ); + break; + } + + /* Parse the packet length. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxIPPacket = ( ( const IPPacket_t * ) pucEthernetBuffer ); + + /* Per https://tools.ietf.org/html/rfc791, the four-bit Internet Header + * Length field contains the length of the internet header in 32-bit words. */ + ucVersionHeaderLength = pxIPPacket->xIPHeader.ucVersionHeaderLength; + ucVersionHeaderLength = ( ucVersionHeaderLength & ( uint8_t ) 0x0FU ) << 2; + uxIPHeaderLength = ( UBaseType_t ) ucVersionHeaderLength; + + /* Check for minimum packet size. */ + if( uxBufferLength < ( sizeof( IPPacket_t ) + ( uxIPHeaderLength - ipSIZE_OF_IPv4_HEADER ) ) ) + { + usChecksum = ipINVALID_LENGTH; + DEBUG_SET_TRACE_VARIABLE( xLocation, 2 ); + break; + } + + usLength = pxIPPacket->xIPHeader.usLength; + usLength = FreeRTOS_ntohs( usLength ); + + if( usLength < uxIPHeaderLength ) + { + usChecksum = ipINVALID_LENGTH; + DEBUG_SET_TRACE_VARIABLE( xLocation, 3 ); + break; + } + + if( uxBufferLength < ( size_t ) ( ipSIZE_OF_ETH_HEADER + ( size_t ) usLength ) ) + { + usChecksum = ipINVALID_LENGTH; + DEBUG_SET_TRACE_VARIABLE( xLocation, 4 ); + break; + } + + /* Identify the next protocol. */ + ucProtocol = pxIPPacket->xIPHeader.ucProtocol; + + /* N.B., if this IP packet header includes Options, then the following + * assignment results in a pointer into the protocol packet with the Ethernet + * and IP headers incorrectly aligned. However, either way, the "third" + * protocol (Layer 3 or 4) header will be aligned, which is the convenience + * of this calculation. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxProtPack = ( ( ProtocolPacket_t * ) &( pucEthernetBuffer[ uxIPHeaderLength - ipSIZE_OF_IPv4_HEADER ] ) ); + + /* Switch on the Layer 3/4 protocol. */ + if( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) + { + if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_UDP_HEADER ) ) + { + usChecksum = ipINVALID_LENGTH; + DEBUG_SET_TRACE_VARIABLE( xLocation, 5 ); + break; + } + + if( xOutgoingPacket != pdFALSE ) + { + /* Clear the UDP checksum field before calculating it. */ + pxProtPack->xUDPPacket.xUDPHeader.usChecksum = 0U; + } + else + { + usChecksumFound = pxProtPack->xUDPPacket.xUDPHeader.usChecksum; + } + + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + pcType = "UDP"; + } + #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ + } + else if( ucProtocol == ( uint8_t ) ipPROTOCOL_TCP ) + { + if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_TCP_HEADER ) ) + { + usChecksum = ipINVALID_LENGTH; + DEBUG_SET_TRACE_VARIABLE( xLocation, 6 ); + break; + } + + if( xOutgoingPacket != pdFALSE ) + { + /* Clear the TCP checksum field before calculating it. */ + pxProtPack->xTCPPacket.xTCPHeader.usChecksum = 0U; + } + else + { + usChecksumFound = pxProtPack->xTCPPacket.xTCPHeader.usChecksum; + } + + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + pcType = "TCP"; + } + #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ + } + else if( ( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) || + ( ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) ) + { + if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_ICMP_HEADER ) ) + { + usChecksum = ipINVALID_LENGTH; + DEBUG_SET_TRACE_VARIABLE( xLocation, 7 ); + break; + } + + if( xOutgoingPacket != pdFALSE ) + { + /* Clear the ICMP/IGMP checksum field before calculating it. */ + pxProtPack->xICMPPacket.xICMPHeader.usChecksum = 0U; + } + else + { + usChecksumFound = pxProtPack->xICMPPacket.xICMPHeader.usChecksum; + } + + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + if( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) + { + pcType = "ICMP"; + } + else + { + pcType = "IGMP"; + } + } + #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ + } + else + { + /* Unhandled protocol, other than ICMP, IGMP, UDP, or TCP. */ + usChecksum = ipUNHANDLED_PROTOCOL; + DEBUG_SET_TRACE_VARIABLE( xLocation, 8 ); + break; + } + + /* The protocol and checksum field have been identified. Check the direction + * of the packet. */ + if( xOutgoingPacket != pdFALSE ) + { + /* This is an outgoing packet. The CRC-field has been cleared. */ + } + else if( ( usChecksumFound == 0U ) && ( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) ) + { + #if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) + { + /* Sender hasn't set the checksum, drop the packet because + * ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS is not set. */ + usChecksum = ipWRONG_CRC; + #if ( ipconfigHAS_PRINTF != 0 ) + { + static BaseType_t xCount = 0; + + /* Exclude this from branch coverage as this is only used for debugging. */ + if( xCount < 5 ) /* LCOV_EXCL_BR_LINE */ + { + FreeRTOS_printf( ( "usGenerateProtocolChecksum: UDP packet from %xip without CRC dropped\n", + FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ) ) ); + xCount++; + } + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + } + #else /* if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */ + { + /* Sender hasn't set the checksum, no use to calculate it. */ + usChecksum = ipCORRECT_CRC; + } + #endif /* if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */ + DEBUG_SET_TRACE_VARIABLE( xLocation, 9 ); + break; + } + else + { + /* Other incoming packet than UDP. */ + } + + usLength = pxIPPacket->xIPHeader.usLength; + usLength = FreeRTOS_ntohs( usLength ); + ulLength = ( uint32_t ) usLength; + ulLength -= ( ( uint16_t ) uxIPHeaderLength ); /* normally minus 20 */ + + if( ( ulLength < ( ( uint32_t ) sizeof( pxProtPack->xUDPPacket.xUDPHeader ) ) ) || + ( ulLength > ( ( uint32_t ) ipconfigNETWORK_MTU - ( uint32_t ) uxIPHeaderLength ) ) ) + { + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: len invalid: %lu\n", pcType, ulLength ) ); + } + #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ + + /* Again, in a 16-bit return value there is no space to indicate an + * error. For incoming packets, 0x1234 will cause dropping of the packet. + * For outgoing packets, there is a serious problem with the + * format/length */ + usChecksum = ipINVALID_LENGTH; + DEBUG_SET_TRACE_VARIABLE( xLocation, 10 ); + break; + } + + if( ucProtocol <= ( uint8_t ) ipPROTOCOL_IGMP ) + { + /* ICMP/IGMP do not have a pseudo header for CRC-calculation. */ + usChecksum = ( uint16_t ) + ( ~usGenerateChecksum( 0U, + ( const uint8_t * ) &( pxProtPack->xICMPPacket.xICMPHeader ), ( size_t ) ulLength ) ); + } + else + { + /* For UDP and TCP, sum the pseudo header, i.e. IP protocol + length + * fields */ + usChecksum = ( uint16_t ) ( ulLength + ( ( uint16_t ) ucProtocol ) ); + + /* And then continue at the IPv4 source and destination addresses. */ + usChecksum = ( uint16_t ) + ( ~usGenerateChecksum( usChecksum, + ( const uint8_t * ) &( pxIPPacket->xIPHeader.ulSourceIPAddress ), + ( size_t ) ( ( 2U * ( size_t ) ipSIZE_OF_IPv4_ADDRESS ) + ulLength ) ) ); + /* Sum TCP header and data. */ + } + + if( xOutgoingPacket == pdFALSE ) + { + /* This is in incoming packet. If the CRC is correct, it should be zero. */ + if( usChecksum == 0U ) + { + usChecksum = ( uint16_t ) ipCORRECT_CRC; + } + else + { + usChecksum = ( uint16_t ) ipWRONG_CRC; + } + } + else + { + if( ( usChecksum == 0U ) && ( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) ) + { + /* In case of UDP, a calculated checksum of 0x0000 is transmitted + * as 0xffff. A value of zero would mean that the checksum is not used. */ + usChecksum = ( uint16_t ) 0xffffu; + } + } + + usChecksum = FreeRTOS_htons( usChecksum ); + + if( xOutgoingPacket != pdFALSE ) + { + switch( ucProtocol ) + { + case ipPROTOCOL_UDP: + pxProtPack->xUDPPacket.xUDPHeader.usChecksum = usChecksum; + break; + + case ipPROTOCOL_TCP: + pxProtPack->xTCPPacket.xTCPHeader.usChecksum = usChecksum; + break; + + case ipPROTOCOL_ICMP: + pxProtPack->xICMPPacket.xICMPHeader.usChecksum = usChecksum; + break; + + default: /* ipPROTOCOL_IGMP */ + pxProtPack->xICMPPacket.xICMPHeader.usChecksum = usChecksum; + break; + } + + usChecksum = ( uint16_t ) ipCORRECT_CRC; + } + + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + else if( ( xOutgoingPacket == pdFALSE ) && ( usChecksum != ipCORRECT_CRC ) ) + { + FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: ID %04X: from %lxip to %lxip bad crc: %04X\n", + pcType, + FreeRTOS_ntohs( pxIPPacket->xIPHeader.usIdentification ), + FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ), + FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulDestinationIPAddress ), + FreeRTOS_ntohs( usChecksumFound ) ) ); + } + else + { + /* Nothing. */ + } + #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ + } while( ipFALSE_BOOL ); + + if( ( usChecksum == ipUNHANDLED_PROTOCOL ) || + ( usChecksum == ipINVALID_LENGTH ) ) + { + /* NOP if ipconfigHAS_PRINTF != 0 */ + FreeRTOS_printf( ( "CRC error: %04x location %ld\n", usChecksum, xLocation ) ); + } + + return usChecksum; +} +/*-----------------------------------------------------------*/ + +/** + * This method generates a checksum for a given IPv4 header, per RFC791 (page 14). + * The checksum algorithm is described as: + * "[T]he 16 bit one's complement of the one's complement sum of all 16 bit words in the + * header. For purposes of computing the checksum, the value of the checksum field is zero." + * + * In a nutshell, that means that each 16-bit 'word' must be summed, after which + * the number of 'carries' (overflows) is added to the result. If that addition + * produces an overflow, that 'carry' must also be added to the final result. The final checksum + * should be the bitwise 'not' (ones-complement) of the result if the packet is + * meant to be transmitted, but this method simply returns the raw value, probably + * because when a packet is received, the checksum is verified by checking that + * ((received & calculated) == 0) without applying a bitwise 'not' to the 'calculated' checksum. + * + * This logic is optimized for microcontrollers which have limited resources, so the logic looks odd. + * It iterates over the full range of 16-bit words, but it does so by processing several 32-bit + * words at once whenever possible. Its first step is to align the memory pointer to a 32-bit boundary, + * after which it runs a fast loop to process multiple 32-bit words at once and adding their 'carries'. + * Finally, it finishes up by processing any remaining 16-bit words, and adding up all of the 'carries'. + * With 32-bit arithmetic, the number of 16-bit 'carries' produced by sequential additions can be found + * by looking at the 16 most-significant bits of the 32-bit integer, since a 32-bit int will continue + * counting up instead of overflowing after 16 bits. That is why the actual checksum calculations look like: + * union.u32 = ( uint32_t ) union.u16[ 0 ] + union.u16[ 1 ]; + * + * Arguments: + * ulSum: This argument provides a value to initialise the progressive summation + * of the header's values to. It is often 0, but protocols like TCP or UDP + * can have pseudo-header fields which need to be included in the checksum. + * pucNextData: This argument contains the address of the first byte which this + * method should process. The method's memory iterator is initialised to this value. + * uxDataLengthBytes: This argument contains the number of bytes that this method + * should process. + */ + +/** + * @brief Calculates the 16-bit checksum of an array of bytes + * + * @param[in] usSum: The initial sum, obtained from earlier data. + * @param[in] pucNextData: The actual data. + * @param[in] uxByteCount: The number of bytes. + * + * @return The 16-bit one's complement of the one's complement sum of all 16-bit + * words in the header + */ +uint16_t usGenerateChecksum( uint16_t usSum, + const uint8_t * pucNextData, + size_t uxByteCount ) +{ +/* MISRA/PC-lint doesn't like the use of unions. Here, they are a great + * aid though to optimise the calculations. */ + xUnion32 xSum2; + xUnion32 xSum; + xUnion32 xTerm; + xUnionPtr xSource; + uintptr_t uxAlignBits; + uint32_t ulCarry = 0U; + uint16_t usTemp; + size_t uxDataLengthBytes = uxByteCount; + size_t uxSize; + uintptr_t ulX; + + /* Small MCUs often spend up to 30% of the time doing checksum calculations + * This function is optimised for 32-bit CPUs; Each time it will try to fetch + * 32-bits, sums it with an accumulator and counts the number of carries. */ + + /* Swap the input (little endian platform only). */ + usTemp = FreeRTOS_ntohs( usSum ); + xSum.u32 = ( uint32_t ) usTemp; + xTerm.u32 = 0U; + + xSource.u8ptr = pucNextData; + + /* MISRA Ref 11.4.3 [Casting pointer to int for verification] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + uxAlignBits = ( ( ( uintptr_t ) pucNextData ) & 0x03U ); + + /* + * If pucNextData is non-aligned then the checksum is starting at an + * odd position and we need to make sure the usSum value now in xSum is + * as if it had been "aligned" in the same way. + */ + if( ( uxAlignBits & 1U ) != 0U ) + { + xSum.u32 = ( ( xSum.u32 & 0xffU ) << 8 ) | ( ( xSum.u32 & 0xff00U ) >> 8 ); + } + + /* If byte (8-bit) aligned... */ + if( ( ( uxAlignBits & 1U ) != 0U ) && ( uxDataLengthBytes >= ( size_t ) 1U ) ) + { + xTerm.u8[ 1 ] = *( xSource.u8ptr ); + xSource.u8ptr++; + uxDataLengthBytes--; + /* Now xSource is word (16-bit) aligned. */ + } + + /* If half-word (16-bit) aligned... */ + if( ( ( uxAlignBits == 1U ) || ( uxAlignBits == 2U ) ) && ( uxDataLengthBytes >= 2U ) ) + { + xSum.u32 += *( xSource.u16ptr ); + xSource.u16ptr++; + uxDataLengthBytes -= 2U; + /* Now xSource is word (32-bit) aligned. */ + } + + /* Word (32-bit) aligned, do the most part. */ + + uxSize = ( size_t ) ( ( uxDataLengthBytes / 4U ) * 4U ); + + if( uxSize >= ( 3U * sizeof( uint32_t ) ) ) + { + uxSize -= ( 3U * sizeof( uint32_t ) ); + } + else + { + uxSize = 0U; + } + + /* In this loop, four 32-bit additions will be done, in total 16 bytes. + * Indexing with constants (0,1,2,3) gives faster code than using + * post-increments. */ + for( ulX = 0U; ulX < uxSize; ulX += 4U * sizeof( uint32_t ) ) + { + /* Use a secondary Sum2, just to see if the addition produced an + * overflow. */ + xSum2.u32 = xSum.u32 + xSource.u32ptr[ 0 ]; + + if( xSum2.u32 < xSum.u32 ) + { + ulCarry++; + } + + /* Now add the secondary sum to the major sum, and remember if there was + * a carry. */ + xSum.u32 = xSum2.u32 + xSource.u32ptr[ 1 ]; + + if( xSum2.u32 > xSum.u32 ) + { + ulCarry++; + } + + /* And do the same trick once again for indexes 2 and 3 */ + xSum2.u32 = xSum.u32 + xSource.u32ptr[ 2 ]; + + if( xSum2.u32 < xSum.u32 ) + { + ulCarry++; + } + + xSum.u32 = xSum2.u32 + xSource.u32ptr[ 3 ]; + + if( xSum2.u32 > xSum.u32 ) + { + ulCarry++; + } + + /* And finally advance the pointer 4 * 4 = 16 bytes. */ + xSource.u32ptr = &( xSource.u32ptr[ 4 ] ); + } + + /* Now add all carries. */ + xSum.u32 = ( uint32_t ) xSum.u16[ 0 ] + xSum.u16[ 1 ] + ulCarry; + + uxDataLengthBytes %= 16U; + + /* Half-word aligned. */ + uxSize = ( ( uxDataLengthBytes & ~( ( size_t ) 1U ) ) ); + + for( ulX = 0U; ulX < uxSize; ulX += 1U * sizeof( uint16_t ) ) + { + /* At least one more short. */ + xSum.u32 += xSource.u16ptr[ 0 ]; + xSource.u16ptr = &xSource.u16ptr[ 1 ]; + } + + if( ( uxDataLengthBytes & ( size_t ) 1U ) != 0U ) /* Maybe one more ? */ + { + xTerm.u8[ 0 ] = xSource.u8ptr[ 0 ]; + } + + /* MISRA Ref 2.2.1 [Unions and dead code] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-22 */ + /* coverity[misra_c_2012_rule_2_2_violation] */ + /* coverity[assigned_value] */ + xSum.u32 += xTerm.u32; + + /* Now add all carries again. */ + + /* Assigning value from "xTerm.u32" to "xSum.u32" here, but that stored value is overwritten before it can be used. */ + /* MISRA Ref 2.2.1 [Unions and dead code] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-22 */ + /* coverity[misra_c_2012_rule_2_2_violation] */ + /* coverity[value_overwrite] */ + xSum.u32 = ( uint32_t ) xSum.u16[ 0 ] + xSum.u16[ 1 ]; + + /* MISRA Ref 2.2.1 [Unions and dead code] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-22 */ + /* coverity[misra_c_2012_rule_2_2_violation] */ + /* coverity[value_overwrite] */ + xSum.u32 = ( uint32_t ) xSum.u16[ 0 ] + xSum.u16[ 1 ]; + + if( ( uxAlignBits & 1U ) != 0U ) + { + /* Quite unlikely, but pucNextData might be non-aligned, which would + * mean that a checksum is calculated starting at an odd position. */ + xSum.u32 = ( ( xSum.u32 & 0xffU ) << 8 ) | ( ( xSum.u32 & 0xff00U ) >> 8 ); + } + + /* swap the output (little endian platform only). */ + return FreeRTOS_htons( ( ( uint16_t ) xSum.u32 ) ); +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigHAS_PRINTF != 0 ) + + #ifndef ipMONITOR_MAX_HEAP + +/* As long as the heap has more space than e.g. 1 MB, there + * will be no messages. */ + #define ipMONITOR_MAX_HEAP ( 1024U * 1024U ) + #endif /* ipMONITOR_MAX_HEAP */ + + #ifndef ipMONITOR_PERCENTAGE_90 + /* Make this number lower to get less logging messages. */ + #define ipMONITOR_PERCENTAGE_90 ( 90U ) + #endif + + #define ipMONITOR_PERCENTAGE_100 ( 100U ) + +/** + * @brief A function that monitors a three resources: the heap, the space in the message + * queue of the IP-task, the number of available network buffer descriptors. + */ + void vPrintResourceStats( void ) + { + UBaseType_t uxCurrentBufferCount; + size_t uxMinSize; + + /* When setting up and testing a project with FreeRTOS+TCP, it is + * can be helpful to monitor a few resources: the number of network + * buffers and the amount of available heap. + * This function will issue some logging when a minimum value has + * changed. */ + uxCurrentBufferCount = uxGetMinimumFreeNetworkBuffers(); + + if( uxLastMinBufferCount > uxCurrentBufferCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinBufferCount = uxCurrentBufferCount; + FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", + uxGetNumberOfFreeNetworkBuffers(), + uxCurrentBufferCount ) ); + } + + uxMinSize = xPortGetMinimumEverFreeHeapSize(); + + if( uxMinLastSize == 0U ) + { + /* Probably the first time this function is called. */ + uxMinLastSize = uxMinSize; + } + else if( uxMinSize >= ipMONITOR_MAX_HEAP ) + { + /* There is more than enough heap space. No need for logging. */ + } + /* Write logging if there is a 10% decrease since the last time logging was written. */ + else if( ( uxMinLastSize * ipMONITOR_PERCENTAGE_90 ) > ( uxMinSize * ipMONITOR_PERCENTAGE_100 ) ) + { + uxMinLastSize = uxMinSize; + FreeRTOS_printf( ( "Heap: current %lu lowest %lu\n", xPortGetFreeHeapSize(), uxMinSize ) ); + } + else + { + /* Nothing to log. */ + } + + #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + { + UBaseType_t uxCurrentCount = 0u; + + uxCurrentCount = uxGetMinimumIPQueueSpace(); + + if( uxLastMinQueueSpace != uxCurrentCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinQueueSpace = uxCurrentCount; + FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); + } + } + #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ + } +#endif /* ( ipconfigHAS_PRINTF != 0 ) */ +/*-----------------------------------------------------------*/ + +/** + * @brief Utility function: Convert error number to a human readable + * string. Declaration in FreeRTOS_errno_TCP.h. + * + * @param[in] xErrnum: The error number. + * @param[in] pcBuffer: Buffer big enough to be filled with the human readable message. + * @param[in] uxLength: Maximum length of the buffer. + * + * @return The buffer filled with human readable error string. + */ +const char * FreeRTOS_strerror_r( BaseType_t xErrnum, + char * pcBuffer, + size_t uxLength ) +{ + const char * pcName; + + switch( xErrnum ) + { + case pdFREERTOS_ERRNO_EADDRINUSE: + pcName = "EADDRINUSE"; + break; + + case pdFREERTOS_ERRNO_ENOMEM: + pcName = "ENOMEM"; + break; + + case pdFREERTOS_ERRNO_EADDRNOTAVAIL: + pcName = "EADDRNOTAVAIL"; + break; + + case pdFREERTOS_ERRNO_ENOPROTOOPT: + pcName = "ENOPROTOOPT"; + break; + + case pdFREERTOS_ERRNO_EBADF: + pcName = "EBADF"; + break; + + case pdFREERTOS_ERRNO_ENOSPC: + pcName = "ENOSPC"; + break; + + case pdFREERTOS_ERRNO_ECANCELED: + pcName = "ECANCELED"; + break; + + case pdFREERTOS_ERRNO_ENOTCONN: + pcName = "ENOTCONN"; + break; + + case pdFREERTOS_ERRNO_EINPROGRESS: + pcName = "EINPROGRESS"; + break; + + case pdFREERTOS_ERRNO_EOPNOTSUPP: + pcName = "EOPNOTSUPP"; + break; + + case pdFREERTOS_ERRNO_EINTR: + pcName = "EINTR"; + break; + + case pdFREERTOS_ERRNO_ETIMEDOUT: + pcName = "ETIMEDOUT"; + break; + + case pdFREERTOS_ERRNO_EINVAL: + pcName = "EINVAL"; + break; + + case pdFREERTOS_ERRNO_EWOULDBLOCK: + pcName = "EWOULDBLOCK"; + break; /* same as EAGAIN */ + + case pdFREERTOS_ERRNO_EISCONN: + pcName = "EISCONN"; + break; + + default: + /* MISRA Ref 21.6.1 [snprintf and logging] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-216 */ + /* coverity[misra_c_2012_rule_21_6_violation] */ + ( void ) snprintf( pcBuffer, uxLength, "Errno %d", ( int ) xErrnum ); + pcName = NULL; + break; + } + + if( pcName != NULL ) + { + /* MISRA Ref 21.6.1 [snprintf and logging] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-216 */ + /* coverity[misra_c_2012_rule_21_6_violation] */ + ( void ) snprintf( pcBuffer, uxLength, "%s", pcName ); + } + + if( uxLength > 0U ) + { + pcBuffer[ uxLength - 1U ] = '\0'; + } + + return pcBuffer; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the highest value of two int32's. + * @param[in] a: the first value. + * @param[in] b: the second value. + * @return The highest of the two values. + */ +int32_t FreeRTOS_max_int32( int32_t a, + int32_t b ) +{ + return ( a >= b ) ? a : b; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the highest value of two uint32_t's. + * @param[in] a: the first value. + * @param[in] b: the second value. + * @return The highest of the two values. + */ +uint32_t FreeRTOS_max_uint32( uint32_t a, + uint32_t b ) +{ + return ( a >= b ) ? a : b; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the highest value of two size_t's. + * @param[in] a: the first value. + * @param[in] b: the second value. + * @return The highest of the two values. + */ +size_t FreeRTOS_max_size_t( size_t a, + size_t b ) +{ + return ( a >= b ) ? a : b; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the lowest value of two int32_t's. + * @param[in] a: the first value. + * @param[in] b: the second value. + * @return The lowest of the two values. + */ +int32_t FreeRTOS_min_int32( int32_t a, + int32_t b ) +{ + return ( a <= b ) ? a : b; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the lowest value of two uint32_t's. + * @param[in] a: the first value. + * @param[in] b: the second value. + * @return The lowest of the two values. + */ +uint32_t FreeRTOS_min_uint32( uint32_t a, + uint32_t b ) +{ + return ( a <= b ) ? a : b; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the lowest value of two size_t's. + * @param[in] a: the first value. + * @param[in] b: the second value. + * @return The lowest of the two values. + */ +size_t FreeRTOS_min_size_t( size_t a, + size_t b ) +{ + return ( a <= b ) ? a : b; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Round-up a number to a multiple of 'd'. + * @param[in] a: the first value. + * @param[in] d: the second value. + * @return A multiple of d. + */ +uint32_t FreeRTOS_round_up( uint32_t a, + uint32_t d ) +{ + uint32_t ulResult = a; + + configASSERT( d != 0U ); + + if( d != 0U ) + { + ulResult = d * ( ( a + d - 1U ) / d ); + } + + return ulResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Round-down a number to a multiple of 'd'. + * @param[in] a: the first value. + * @param[in] d: the second value. + * @return A multiple of d. + */ +uint32_t FreeRTOS_round_down( uint32_t a, + uint32_t d ) +{ + uint32_t ulResult = 0; + + configASSERT( d != 0U ); + + if( d != 0U ) + { + ulResult = d * ( a / d ); + } + + return ulResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Convert character array (of size 4) to equivalent 32-bit value. + * @param[in] pucPtr: The character array. + * @return 32-bit equivalent value extracted from the character array. + * + * @note Going by MISRA rules, these utility functions should not be defined + * if they are not being used anywhere. But their use depends on the + * application and hence these functions are defined unconditionally. + */ +uint32_t ulChar2u32( const uint8_t * pucPtr ) +{ + return ( ( ( uint32_t ) pucPtr[ 0 ] ) << 24 ) | + ( ( ( uint32_t ) pucPtr[ 1 ] ) << 16 ) | + ( ( ( uint32_t ) pucPtr[ 2 ] ) << 8 ) | + ( ( ( uint32_t ) pucPtr[ 3 ] ) ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Convert character array (of size 2) to equivalent 16-bit value. + * @param[in] pucPtr: The character array. + * @return 16-bit equivalent value extracted from the character array. + * + * @note Going by MISRA rules, these utility functions should not be defined + * if they are not being used anywhere. But their use depends on the + * application and hence these functions are defined unconditionally. + */ +uint16_t usChar2u16( const uint8_t * pucPtr ) +{ + return ( uint16_t ) + ( ( ( ( uint32_t ) pucPtr[ 0 ] ) << 8 ) | + ( ( ( uint32_t ) pucPtr[ 1 ] ) ) ); +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/FreeRTOS_Sockets.c b/FreeRTOS/source/FreeRTOS_Sockets.c new file mode 100644 index 0000000..80bc71c --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_Sockets.c @@ -0,0 +1,5251 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_Sockets.c + * @brief Implements the Sockets API based on Berkeley sockets for the FreeRTOS+TCP network stack. + * Sockets are used by the application processes to interact with the IP-task which in turn + * interacts with the hardware. + */ + +/* Standard includes. */ +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_DNS.h" +#include "NetworkBufferManagement.h" + +/* The ItemValue of the sockets xBoundSocketListItem member holds the socket's + * port number. */ +/** @brief Set the port number for the socket in the xBoundSocketListItem. */ +#define socketSET_SOCKET_PORT( pxSocket, usPort ) listSET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ), ( usPort ) ) +/** @brief Get the port number for the socket in the xBoundSocketListItem. */ +#define socketGET_SOCKET_PORT( pxSocket ) listGET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ) ) + +/** @brief Test if a socket it bound which means it is either included in + * xBoundUDPSocketsList or xBoundTCPSocketsList + */ +#define socketSOCKET_IS_BOUND( pxSocket ) ( listLIST_ITEM_CONTAINER( &( pxSocket )->xBoundSocketListItem ) != NULL ) + +/** @brief If FreeRTOS_sendto() is called on a socket that is not bound to a port + * number then, depending on the FreeRTOSIPConfig.h settings, it might be + * that a port number is automatically generated for the socket. + * Automatically generated port numbers will be between + * socketAUTO_PORT_ALLOCATION_START_NUMBER and 0xffff. + * + * @note Per https://tools.ietf.org/html/rfc6056, "the dynamic ports consist of + * the range 49152-65535. However, ephemeral port selection algorithms should + * use the whole range 1024-65535" excluding those already in use (inbound + * or outbound). + */ +#if !defined( socketAUTO_PORT_ALLOCATION_START_NUMBER ) + #define socketAUTO_PORT_ALLOCATION_START_NUMBER ( ( uint16_t ) 0x0400 ) +#endif + +/** @brief Maximum value of port number which can be auto assigned. */ +#define socketAUTO_PORT_ALLOCATION_MAX_NUMBER ( ( uint16_t ) 0xffff ) + +/** @brief The number of octets that make up an IP address. */ +#define socketMAX_IP_ADDRESS_OCTETS 4U + +/** @brief A block time of 0 simply means "don't block". */ +#define socketDONT_BLOCK ( ( TickType_t ) 0 ) + +/** @brief TCP timer period in milliseconds. */ +#if ( ( ipconfigUSE_TCP == 1 ) && !defined( ipTCP_TIMER_PERIOD_MS ) ) + #define ipTCP_TIMER_PERIOD_MS ( 1000U ) +#endif + +/* Some helper macro's for defining the 20/80 % limits of uxLittleSpace / uxEnoughSpace. */ +#define sock20_PERCENT 20U /**< 20% of the defined limit. */ +#define sock80_PERCENT 80U /**< 80% of the defined limit. */ +#define sock100_PERCENT 100U /**< 100% of the defined limit. */ + +/** @brief When ucASCIIToHex() can not convert a character, + * the value 255 will be returned. + */ +#define socketINVALID_HEX_CHAR 0xffU + +/* + * Allocate the next port number from the private allocation range. + * TCP and UDP each have their own series of port numbers + * ulProtocol is either ipPROTOCOL_UDP or ipPROTOCOL_TCP + */ +static uint16_t prvGetPrivatePortNumber( BaseType_t xProtocol ); + +/* + * Return the list item from within pxList that has an item value of + * xWantedItemValue. If there is no such list item return NULL. + */ +static const ListItem_t * pxListFindListItemWithValue( const List_t * pxList, + TickType_t xWantedItemValue ); + +/* + * Return pdTRUE only if pxSocket is valid and bound, as far as can be + * determined. + */ +static BaseType_t prvValidSocket( const FreeRTOS_Socket_t * pxSocket, + BaseType_t xProtocol, + BaseType_t xIsBound ); + +#if ( ipconfigUSE_TCP == 1 ) + +/* + * Internal function prvSockopt_so_buffer(): sets FREERTOS_SO_SNDBUF or + * FREERTOS_SO_RCVBUF properties of a socket. + */ + static BaseType_t prvSockopt_so_buffer( FreeRTOS_Socket_t * pxSocket, + int32_t lOptionName, + const void * pvOptionValue ); +#endif /* ipconfigUSE_TCP == 1 */ + +/* + * Before creating a socket, check the validity of the parameters used + * and find the size of the socket space, which is different for UDP and TCP + */ +static BaseType_t prvDetermineSocketSize( BaseType_t xDomain, + BaseType_t xType, + BaseType_t xProtocol, + size_t * pxSocketSize ); + +static uint8_t ucASCIIToHex( char cChar ); + +#if ( ipconfigUSE_TCP == 1 ) + +/* + * Create a txStream or a rxStream, depending on the parameter 'xIsInputStream' + */ + static StreamBuffer_t * prvTCPCreateStream( FreeRTOS_Socket_t * pxSocket, + BaseType_t xIsInputStream ); +#endif /* ipconfigUSE_TCP == 1 */ + +#if ( ipconfigUSE_TCP == 1 ) + +/* + * Called from FreeRTOS_send(): some checks which will be done before + * sending a TCP packed. + */ + static int32_t prvTCPSendCheck( FreeRTOS_Socket_t * pxSocket, + size_t uxDataLength ); +#endif /* ipconfigUSE_TCP */ + +#if ( ipconfigUSE_TCP == 1 ) + +/* + * When a child socket gets closed, make sure to update the child-count of the parent + */ + static void prvTCPSetSocketCount( FreeRTOS_Socket_t const * pxSocketToDelete ); +#endif /* ipconfigUSE_TCP == 1 */ + +#if ( ipconfigUSE_TCP == 1 ) + +/* + * Called from FreeRTOS_connect(): make some checks and if allowed, send a + * message to the IP-task to start connecting to a remote socket + */ + static BaseType_t prvTCPConnectStart( FreeRTOS_Socket_t * pxSocket, + struct freertos_sockaddr const * pxAddress ); +#endif /* ipconfigUSE_TCP */ + +#if ( ipconfigUSE_TCP == 1 ) + +/* + * Check if it makes any sense to wait for a connect event. + * It may return: -EINPROGRESS, -EAGAIN, or 0 for OK. + */ + static BaseType_t bMayConnect( FreeRTOS_Socket_t const * pxSocket ); +#endif /* ipconfigUSE_TCP */ + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/* Executed by the IP-task, it will check all sockets belonging to a set */ + static void prvFindSelectedSocket( SocketSelect_t * pxSocketSet ); + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ +/*-----------------------------------------------------------*/ + +/** @brief The list that contains mappings between sockets and port numbers. + * Accesses to this list must be protected by critical sections of + * some kind. + */ +List_t xBoundUDPSocketsList; + +#if ipconfigUSE_TCP == 1 + +/** @brief The list that contains mappings between sockets and port numbers. + * Accesses to this list must be protected by critical sections of + * some kind. + */ + List_t xBoundTCPSocketsList; + +#endif /* ipconfigUSE_TCP == 1 */ + +/*-----------------------------------------------------------*/ + +/** + * @brief Check whether the socket is valid or not. + * + * @param[in] pxSocket: The socket being checked. + * @param[in] xProtocol: The protocol for which the socket was created. + * @param[in] xIsBound: pdTRUE when the socket should be bound, otherwise pdFALSE. + * + * @return If the socket is valid, then pdPASS is returned or else, pdFAIL + * is returned. + */ +static BaseType_t prvValidSocket( const FreeRTOS_Socket_t * pxSocket, + BaseType_t xProtocol, + BaseType_t xIsBound ) +{ + BaseType_t xReturn; + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + if( ( pxSocket == NULL ) || ( pxSocket == FREERTOS_INVALID_SOCKET ) ) + { + xReturn = pdFALSE; + } + else if( ( xIsBound != pdFALSE ) && !socketSOCKET_IS_BOUND( pxSocket ) ) + { + /* The caller expects the socket to be bound, but it isn't. */ + xReturn = pdFALSE; + } + else if( pxSocket->ucProtocol != ( uint8_t ) xProtocol ) + { + /* Socket has a wrong type (UDP != TCP). */ + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Initialise the bound TCP/UDP socket lists. + */ +void vNetworkSocketsInit( void ) +{ + vListInitialise( &xBoundUDPSocketsList ); + + #if ( ipconfigUSE_TCP == 1 ) + { + vListInitialise( &xBoundTCPSocketsList ); + } + #endif /* ipconfigUSE_TCP == 1 */ +} +/*-----------------------------------------------------------*/ + +/** + * @brief Determine the socket size for the given protocol. + * + * @param[in] xDomain: The domain for which the size of socket is being determined. + * @param[in] xType: Is this a datagram socket or a stream socket. + * @param[in] xProtocol: The protocol being used. + * @param[out] pxSocketSize: Pointer to a variable in which the size shall be returned + * if all checks pass. + * + * @return pdPASS if socket size was determined and put in the parameter pxSocketSize + * correctly, else pdFAIL. + */ +static BaseType_t prvDetermineSocketSize( BaseType_t xDomain, + BaseType_t xType, + BaseType_t xProtocol, + size_t * pxSocketSize ) +{ + BaseType_t xReturn = pdPASS; + FreeRTOS_Socket_t const * pxSocket = NULL; + + /* Asserts must not appear before it has been determined that the network + * task is ready - otherwise the asserts will fail. */ + if( xIPIsNetworkTaskReady() == pdFALSE ) + { + xReturn = pdFAIL; + } + else + { + /* Only Ethernet is currently supported. */ + configASSERT( xDomain == FREERTOS_AF_INET ); + + /* Check if the UDP socket-list has been initialised. */ + configASSERT( listLIST_IS_INITIALISED( &xBoundUDPSocketsList ) ); + #if ( ipconfigUSE_TCP == 1 ) + { + /* Check if the TCP socket-list has been initialised. */ + configASSERT( listLIST_IS_INITIALISED( &xBoundTCPSocketsList ) ); + } + #endif /* ipconfigUSE_TCP == 1 */ + + if( xProtocol == FREERTOS_IPPROTO_UDP ) + { + if( xType != FREERTOS_SOCK_DGRAM ) + { + xReturn = pdFAIL; + configASSERT( xReturn == pdPASS ); /* LCOV_EXCL_BR_LINE Exclude this line from branch coverage as the not-taken condition will never happen. */ + } + + /* In case a UDP socket is created, do not allocate space for TCP data. */ + *pxSocketSize = ( sizeof( *pxSocket ) - sizeof( pxSocket->u ) ) + sizeof( pxSocket->u.xUDP ); + } + + #if ( ipconfigUSE_TCP == 1 ) + else if( xProtocol == FREERTOS_IPPROTO_TCP ) + { + if( xType != FREERTOS_SOCK_STREAM ) + { + xReturn = pdFAIL; + configASSERT( xReturn == pdPASS ); /* LCOV_EXCL_BR_LINE Exclude this line from branch coverage as the not-taken condition will never happen. */ + } + + *pxSocketSize = ( sizeof( *pxSocket ) - sizeof( pxSocket->u ) ) + sizeof( pxSocket->u.xTCP ); + } + #endif /* ipconfigUSE_TCP == 1 */ + else + { + xReturn = pdFAIL; + configASSERT( xReturn == pdPASS ); /* LCOV_EXCL_BR_LINE Exclude this line from branch coverage as the not-taken condition will never happen. */ + } + } + + /* In case configASSERT() is not used */ + ( void ) xDomain; + ( void ) pxSocket; /* Was only used for sizeof. */ + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief allocate and initialise a socket. + * + * @param[in] xDomain: The domain in which the socket should be created. + * @param[in] xType: The type of the socket. + * @param[in] xProtocol: The protocol of the socket. + * + * @return FREERTOS_INVALID_SOCKET if the allocation failed, or if there was + * a parameter error, otherwise a valid socket. + */ +Socket_t FreeRTOS_socket( BaseType_t xDomain, + BaseType_t xType, + BaseType_t xProtocol ) +{ + FreeRTOS_Socket_t * pxSocket; + +/* Note that this value will be over-written by the call to prvDetermineSocketSize. */ + size_t uxSocketSize = 1; + EventGroupHandle_t xEventGroup; + Socket_t xReturn; + BaseType_t xProtocolCpy = xProtocol; + + /* The special protocol FREERTOS_SOCK_DEPENDENT_PROTO, which is equivalent + * to passing 0 as defined by POSIX, indicates to the socket layer that it + * should pick a sensible default protocol based off the given socket type. + * If we can't, prvDetermineSocketSize will catch it as an invalid + * type/protocol combo. + */ + if( xProtocol == FREERTOS_SOCK_DEPENDENT_PROTO ) + { + switch( xType ) + { + case FREERTOS_SOCK_DGRAM: + xProtocolCpy = FREERTOS_IPPROTO_UDP; + break; + + case FREERTOS_SOCK_STREAM: + xProtocolCpy = FREERTOS_IPPROTO_TCP; + break; + + default: + + /* incorrect xType. this will be caught by + * prvDetermineSocketSize. + */ + break; + } + } + + if( prvDetermineSocketSize( xDomain, xType, xProtocolCpy, &uxSocketSize ) == pdFAIL ) + { + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + xReturn = FREERTOS_INVALID_SOCKET; + } + else + { + /* Allocate the structure that will hold the socket information. The + * size depends on the type of socket: UDP sockets need less space. A + * define 'pvPortMallocSocket' will used to allocate the necessary space. + * By default it points to the FreeRTOS function 'pvPortMalloc()'. */ + pxSocket = ( ( FreeRTOS_Socket_t * ) pvPortMallocSocket( uxSocketSize ) ); + + if( pxSocket == NULL ) + { + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + xReturn = FREERTOS_INVALID_SOCKET; + iptraceFAILED_TO_CREATE_SOCKET(); + } + else + { + xEventGroup = xEventGroupCreate(); + + if( xEventGroup == NULL ) + { + vPortFreeSocket( pxSocket ); + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + xReturn = FREERTOS_INVALID_SOCKET; + iptraceFAILED_TO_CREATE_EVENT_GROUP(); + } + else + { + if( xProtocolCpy == FREERTOS_IPPROTO_UDP ) + { + iptraceMEM_STATS_CREATE( tcpSOCKET_UDP, pxSocket, uxSocketSize + sizeof( StaticEventGroup_t ) ); + } + else + { + /* Lint wants at least a comment, in case the macro is empty. */ + iptraceMEM_STATS_CREATE( tcpSOCKET_TCP, pxSocket, uxSocketSize + sizeof( StaticEventGroup_t ) ); + } + + /* Clear the entire space to avoid nulling individual entries. */ + ( void ) memset( pxSocket, 0, uxSocketSize ); + + pxSocket->xEventGroup = xEventGroup; + + /* Initialise the socket's members. The semaphore will be created + * if the socket is bound to an address, for now the pointer to the + * semaphore is just set to NULL to show it has not been created. */ + if( xProtocolCpy == FREERTOS_IPPROTO_UDP ) + { + vListInitialise( &( pxSocket->u.xUDP.xWaitingPacketsList ) ); + + #if ( ipconfigUDP_MAX_RX_PACKETS > 0U ) + { + pxSocket->u.xUDP.uxMaxPackets = ( UBaseType_t ) ipconfigUDP_MAX_RX_PACKETS; + } + #endif /* ipconfigUDP_MAX_RX_PACKETS > 0 */ + } + + vListInitialiseItem( &( pxSocket->xBoundSocketListItem ) ); + listSET_LIST_ITEM_OWNER( &( pxSocket->xBoundSocketListItem ), ( void * ) pxSocket ); + + pxSocket->xReceiveBlockTime = ipconfigSOCK_DEFAULT_RECEIVE_BLOCK_TIME; + pxSocket->xSendBlockTime = ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME; + pxSocket->ucSocketOptions = ( uint8_t ) FREERTOS_SO_UDPCKSUM_OUT; + pxSocket->ucProtocol = ( uint8_t ) xProtocolCpy; /* protocol: UDP or TCP */ + + #if ( ipconfigUSE_TCP == 1 ) + { + if( xProtocolCpy == FREERTOS_IPPROTO_TCP ) + { + /* StreamSize is expressed in number of bytes */ + /* Round up buffer sizes to nearest multiple of MSS */ + pxSocket->u.xTCP.usMSS = ( uint16_t ) ipconfigTCP_MSS; + pxSocket->u.xTCP.uxRxStreamSize = ( size_t ) ipconfigTCP_RX_BUFFER_LENGTH; + pxSocket->u.xTCP.uxTxStreamSize = ( size_t ) FreeRTOS_round_up( ipconfigTCP_TX_BUFFER_LENGTH, ipconfigTCP_MSS ); + /* Use half of the buffer size of the TCP windows */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + pxSocket->u.xTCP.uxRxWinSize = FreeRTOS_max_uint32( 1U, ( uint32_t ) ( pxSocket->u.xTCP.uxRxStreamSize / 2U ) / ipconfigTCP_MSS ); + pxSocket->u.xTCP.uxTxWinSize = FreeRTOS_max_uint32( 1U, ( uint32_t ) ( pxSocket->u.xTCP.uxTxStreamSize / 2U ) / ipconfigTCP_MSS ); + } + #else + { + pxSocket->u.xTCP.uxRxWinSize = 1U; + pxSocket->u.xTCP.uxTxWinSize = 1U; + } + #endif + + /* The above values are just defaults, and can be overridden by + * calling FreeRTOS_setsockopt(). No buffers will be allocated until a + * socket is connected and data is exchanged. */ + } + } + #endif /* ipconfigUSE_TCP == 1 */ + xReturn = pxSocket; + } + } + } + + /* Remove compiler warnings in the case the configASSERT() is not defined. */ + ( void ) xDomain; + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/** + * @brief Create a socket set. + * + * @return The new socket set which was created, or NULL when allocation has failed. + */ + SocketSet_t FreeRTOS_CreateSocketSet( void ) + { + SocketSelect_t * pxSocketSet; + + pxSocketSet = ( ( SocketSelect_t * ) pvPortMalloc( sizeof( *pxSocketSet ) ) ); + + if( pxSocketSet != NULL ) + { + ( void ) memset( pxSocketSet, 0, sizeof( *pxSocketSet ) ); + pxSocketSet->xSelectGroup = xEventGroupCreate(); + + if( pxSocketSet->xSelectGroup == NULL ) + { + vPortFree( pxSocketSet ); + pxSocketSet = NULL; + } + else + { + /* Lint wants at least a comment, in case the macro is empty. */ + iptraceMEM_STATS_CREATE( tcpSOCKET_SET, pxSocketSet, sizeof( *pxSocketSet ) + sizeof( StaticEventGroup_t ) ); + } + } + + return ( SocketSet_t ) pxSocketSet; + } + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/** + * @brief Delete a given socket set. + * + * @param[in] xSocketSet: The socket set being deleted. + */ + void FreeRTOS_DeleteSocketSet( SocketSet_t xSocketSet ) + { + IPStackEvent_t xCloseEvent; + + + xCloseEvent.eEventType = eSocketSetDeleteEvent; + xCloseEvent.pvData = ( void * ) xSocketSet; + + if( xSendEventStructToIPTask( &xCloseEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL ) + { + FreeRTOS_printf( ( "FreeRTOS_DeleteSocketSet: xSendEventStructToIPTask failed\n" ) ); + } + } + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/** + * @brief Add a socket to a set. + * + * @param[in] xSocket: The socket being added. + * @param[in] xSocketSet: The socket set being added to. + * @param[in] xBitsToSet: The event bits to set, a combination of the values defined + * in 'eSelectEvent_t', for read, write, exception, etc. + */ + void FreeRTOS_FD_SET( Socket_t xSocket, + SocketSet_t xSocketSet, + EventBits_t xBitsToSet ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + SocketSelect_t * pxSocketSet = ( SocketSelect_t * ) xSocketSet; + + + configASSERT( pxSocket != NULL ); + configASSERT( xSocketSet != NULL ); + + /* Make sure we're not adding bits which are reserved for internal use, + * such as eSELECT_CALL_IP */ + pxSocket->xSelectBits |= xBitsToSet & ( ( EventBits_t ) eSELECT_ALL ); + + if( ( pxSocket->xSelectBits & ( ( EventBits_t ) eSELECT_ALL ) ) != ( EventBits_t ) 0U ) + { + /* Adding a socket to a socket set. */ + pxSocket->pxSocketSet = ( SocketSelect_t * ) xSocketSet; + + /* Now have the IP-task call vSocketSelect() to see if the set contains + * any sockets which are 'ready' and set the proper bits. */ + prvFindSelectedSocket( pxSocketSet ); + } + } + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/** + * @brief Clear select bits for a socket. If the mask becomes 0, + * remove the socket from the set. + * + * @param[in] xSocket: The socket whose select bits are being cleared. + * @param[in] xSocketSet: The socket set of the socket. + * @param[in] xBitsToClear: The bits to be cleared. Every '1' means that the + * corresponding bit will be cleared. See 'eSelectEvent_t' for + * the possible values. + */ + void FreeRTOS_FD_CLR( Socket_t xSocket, + SocketSet_t xSocketSet, + EventBits_t xBitsToClear ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + + configASSERT( pxSocket != NULL ); + configASSERT( xSocketSet != NULL ); + + pxSocket->xSelectBits &= ~( xBitsToClear & ( ( EventBits_t ) eSELECT_ALL ) ); + + if( ( pxSocket->xSelectBits & ( ( EventBits_t ) eSELECT_ALL ) ) != ( EventBits_t ) 0U ) + { + pxSocket->pxSocketSet = ( SocketSelect_t * ) xSocketSet; + } + else + { + /* disconnect it from the socket set */ + pxSocket->pxSocketSet = NULL; + } + } + + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ +/*-----------------------------------------------------------*/ + + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/** + * @brief Test if a socket belongs to a socket-set and if so, which event bit(s) + * are set. + * + * @param[in] xSocket: The socket of interest. + * @param[in] xSocketSet: The socket set to which the socket belongs. + * + * @return If the socket belongs to the socket set: the event bits, otherwise zero. + */ + EventBits_t FreeRTOS_FD_ISSET( const ConstSocket_t xSocket, + const ConstSocketSet_t xSocketSet ) + { + EventBits_t xReturn; + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + + configASSERT( pxSocket != NULL ); + configASSERT( xSocketSet != NULL ); + + if( xSocketSet == ( SocketSet_t ) pxSocket->pxSocketSet ) + { + /* Make sure we're not adding bits which are reserved for internal + * use. */ + xReturn = pxSocket->xSocketBits & ( ( EventBits_t ) eSELECT_ALL ); + } + else + { + xReturn = 0; + } + + return xReturn; + } + + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/** + * @brief The select() statement: wait for an event to occur on any of the sockets + * included in a socket set. + * + * @param[in] xSocketSet: The socket set including the sockets on which we are + * waiting for an event to occur. + * @param[in] xBlockTimeTicks: Maximum time ticks to wait for an event to occur. + * If the value is 'portMAX_DELAY' then the function will wait + * indefinitely for an event to occur. + * + * @return The socket which might have triggered the event bit. + */ + BaseType_t FreeRTOS_select( SocketSet_t xSocketSet, + TickType_t xBlockTimeTicks ) + { + TimeOut_t xTimeOut; + TickType_t xRemainingTime; + SocketSelect_t * pxSocketSet = ( SocketSelect_t * ) xSocketSet; + EventBits_t uxResult; + + configASSERT( xSocketSet != NULL ); + + /* Only in the first round, check for non-blocking */ + xRemainingTime = xBlockTimeTicks; + + /* Fetch the current time */ + vTaskSetTimeOutState( &xTimeOut ); + + for( ; ; ) + { + /* Find a socket which might have triggered the bit + * This function might return immediately or block for a limited time */ + uxResult = xEventGroupWaitBits( pxSocketSet->xSelectGroup, ( ( EventBits_t ) eSELECT_ALL ), pdFALSE, pdFALSE, xRemainingTime ); + + #if ( ipconfigSUPPORT_SIGNALS != 0 ) + { + if( ( uxResult & ( ( EventBits_t ) eSELECT_INTR ) ) != 0U ) + { + ( void ) xEventGroupClearBits( pxSocketSet->xSelectGroup, ( EventBits_t ) eSELECT_INTR ); + FreeRTOS_debug_printf( ( "FreeRTOS_select: interrupted\n" ) ); + break; + } + } + #endif /* ipconfigSUPPORT_SIGNALS */ + + /* Have the IP-task find the socket which had an event */ + prvFindSelectedSocket( pxSocketSet ); + + uxResult = xEventGroupGetBits( pxSocketSet->xSelectGroup ); + + if( uxResult != 0U ) + { + break; + } + + /* Has the timeout been reached? */ + if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) + { + break; + } + } + + return ( BaseType_t ) uxResult; + } + + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/** + * @brief Send a message to the IP-task to have it check all sockets belonging to + * 'pxSocketSet' + * + * @param[in] pxSocketSet: The socket set being asked to check. + */ + static void prvFindSelectedSocket( SocketSelect_t * pxSocketSet ) + { + IPStackEvent_t xSelectEvent; + + #if ( ipconfigSELECT_USES_NOTIFY != 0 ) + SocketSelectMessage_t xSelectMessage; + #endif + + xSelectEvent.eEventType = eSocketSelectEvent; + #if ( ipconfigSELECT_USES_NOTIFY != 0 ) + { + xSelectMessage.pxSocketSet = pxSocketSet; + xSelectMessage.xTaskhandle = xTaskGetCurrentTaskHandle(); + xSelectEvent.pvData = &( xSelectMessage ); + } + #else + { + xSelectEvent.pvData = pxSocketSet; + + /* while the IP-task works on the request, the API will block on + * 'eSELECT_CALL_IP'. So clear it first. */ + ( void ) xEventGroupClearBits( pxSocketSet->xSelectGroup, ( BaseType_t ) eSELECT_CALL_IP ); + } + #endif /* if ( ipconfigSELECT_USES_NOTIFY != 0 ) */ + + /* Now send the socket select event */ + if( xSendEventStructToIPTask( &xSelectEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL ) + { + /* Oops, we failed to wake-up the IP task. No use to wait for it. */ + FreeRTOS_debug_printf( ( "prvFindSelectedSocket: failed\n" ) ); + } + else + { + /* As soon as the IP-task is ready, it will set 'eSELECT_CALL_IP' to + * wakeup the calling API */ + #if ( ipconfigSELECT_USES_NOTIFY != 0 ) + { + ( void ) ulTaskNotifyTake( pdFALSE, portMAX_DELAY ); + } + #else + { + ( void ) xEventGroupWaitBits( pxSocketSet->xSelectGroup, ( BaseType_t ) eSELECT_CALL_IP, pdTRUE, pdFALSE, portMAX_DELAY ); + } + #endif + } + } + + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ +/*-----------------------------------------------------------*/ + +/** + * @brief Receive data from a bound socket. In this library, the function + * can only be used with connection-less sockets (UDP). For TCP sockets, + * please use FreeRTOS_recv(). + * + * @param[in] xSocket: The socket to which the data is sent i.e. the + * listening socket. + * @param[out] pvBuffer: The buffer in which the data being received is to + * be stored. + * @param[in] uxBufferLength: The length of the buffer. + * @param[in] xFlags: The flags to indicate preferences while calling this function. + * @param[out] pxSourceAddress: The source address from which the data is being sent. + * @param[out] pxSourceAddressLength: This parameter is used only to adhere to Berkeley + * sockets standard. It is not used internally. + * + * @return The number of bytes received. Or else, an error code is returned. When it + * returns a negative value, the cause can be looked-up in + * 'FreeRTOS_errno_TCP.h'. + */ +int32_t FreeRTOS_recvfrom( const ConstSocket_t xSocket, + void * pvBuffer, + size_t uxBufferLength, + BaseType_t xFlags, + struct freertos_sockaddr * pxSourceAddress, + const socklen_t * pxSourceAddressLength ) +{ + BaseType_t lPacketCount; + NetworkBufferDescriptor_t * pxNetworkBuffer; + const void * pvCopySource; + FreeRTOS_Socket_t const * pxSocket = xSocket; + TickType_t xRemainingTime = ( TickType_t ) 0; /* Obsolete assignment, but some compilers output a warning if its not done. */ + BaseType_t xTimed = pdFALSE; + TimeOut_t xTimeOut; + int32_t lReturn; + EventBits_t xEventBits = ( EventBits_t ) 0; + size_t uxPayloadLength; + + if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_UDP, pdTRUE ) == pdFALSE ) + { + lReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + lPacketCount = ( BaseType_t ) listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ); + + /* The function prototype is designed to maintain the expected Berkeley + * sockets standard, but this implementation does not use all the parameters. */ + ( void ) pxSourceAddressLength; + + while( lPacketCount == 0 ) + { + if( xTimed == pdFALSE ) + { + /* Check to see if the socket is non blocking on the first + * iteration. */ + xRemainingTime = pxSocket->xReceiveBlockTime; + + if( xRemainingTime == ( TickType_t ) 0 ) + { + #if ( ipconfigSUPPORT_SIGNALS != 0 ) + { + /* Just check for the interrupt flag. */ + xEventBits = xEventGroupWaitBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_INTR, + pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, socketDONT_BLOCK ); + } + #endif /* ipconfigSUPPORT_SIGNALS */ + break; + } + + if( ( ( ( UBaseType_t ) xFlags ) & ( ( UBaseType_t ) FREERTOS_MSG_DONTWAIT ) ) != 0U ) + { + break; + } + + /* To ensure this part only executes once. */ + xTimed = pdTRUE; + + /* Fetch the current time. */ + vTaskSetTimeOutState( &xTimeOut ); + } + + /* Wait for arrival of data. While waiting, the IP-task may set the + * 'eSOCKET_RECEIVE' bit in 'xEventGroup', if it receives data for this + * socket, thus unblocking this API call. */ + xEventBits = xEventGroupWaitBits( pxSocket->xEventGroup, ( ( EventBits_t ) eSOCKET_RECEIVE ) | ( ( EventBits_t ) eSOCKET_INTR ), + pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime ); + + #if ( ipconfigSUPPORT_SIGNALS != 0 ) + { + if( ( xEventBits & ( EventBits_t ) eSOCKET_INTR ) != 0U ) + { + if( ( xEventBits & ( EventBits_t ) eSOCKET_RECEIVE ) != 0U ) + { + /* Shouldn't have cleared the eSOCKET_RECEIVE flag. */ + ( void ) xEventGroupSetBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_RECEIVE ); + } + + break; + } + } + #else /* if ( ipconfigSUPPORT_SIGNALS != 0 ) */ + { + ( void ) xEventBits; + } + #endif /* ipconfigSUPPORT_SIGNALS */ + + lPacketCount = ( BaseType_t ) listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ); + + if( lPacketCount != 0 ) + { + break; + } + + /* Has the timeout been reached ? */ + if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) + { + break; + } + } /* while( lPacketCount == 0 ) */ + + if( lPacketCount != 0 ) + { + taskENTER_CRITICAL(); + { + /* The owner of the list item is the network buffer. */ + pxNetworkBuffer = ( ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) ); + + if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_MSG_PEEK ) == 0U ) + { + /* Remove the network buffer from the list of buffers waiting to + * be processed by the socket. */ + ( void ) uxListRemove( &( pxNetworkBuffer->xBufferListItem ) ); + } + } + taskEXIT_CRITICAL(); + + /* The returned value is the length of the payload data, which is + * calculated at the total packet size minus the headers. + * The validity of `xDataLength` prvProcessIPPacket has been confirmed + * in 'prvProcessIPPacket()'. */ + uxPayloadLength = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t ); + lReturn = ( int32_t ) uxPayloadLength; + + if( pxSourceAddress != NULL ) + { + pxSourceAddress->sin_port = pxNetworkBuffer->usPort; + pxSourceAddress->sin_addr = pxNetworkBuffer->ulIPAddress; + } + + if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_ZERO_COPY ) == 0U ) + { + /* The zero copy flag is not set. Truncate the length if it won't + * fit in the provided buffer. */ + if( lReturn > ( int32_t ) uxBufferLength ) + { + iptraceRECVFROM_DISCARDING_BYTES( ( uxBufferLength - lReturn ) ); + lReturn = ( int32_t ) uxBufferLength; + } + + /* Copy the received data into the provided buffer, then release the + * network buffer. */ + pvCopySource = ( const void * ) &pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ]; + ( void ) memcpy( pvBuffer, pvCopySource, ( size_t ) lReturn ); + + if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_MSG_PEEK ) == 0U ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + } + else + { + /* The zero copy flag was set. pvBuffer is not a buffer into which + * the received data can be copied, but a pointer that must be set to + * point to the buffer in which the received data has already been + * placed. */ + *( ( void ** ) pvBuffer ) = ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ); + } + } + + #if ( ipconfigSUPPORT_SIGNALS != 0 ) + else if( ( xEventBits & ( EventBits_t ) eSOCKET_INTR ) != 0U ) + { + lReturn = -pdFREERTOS_ERRNO_EINTR; + iptraceRECVFROM_INTERRUPTED(); + } + #endif /* ipconfigSUPPORT_SIGNALS */ + else + { + lReturn = -pdFREERTOS_ERRNO_EWOULDBLOCK; + iptraceRECVFROM_TIMEOUT(); + } + } + + return lReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Check if a socket is a valid UDP socket. In case it is not + * yet bound, bind it to port 0 ( random port ). + * @param[in] pxSocket: The socket that must be bound to a port number. + * @return Returns pdTRUE if the socket was already bound, or if the + * socket has been bound successfully. + */ +static BaseType_t prvMakeSureSocketIsBound( FreeRTOS_Socket_t * pxSocket ) +{ + /* Check if this is a valid UDP socket, does not have to be bound yet. */ + BaseType_t xReturn = prvValidSocket( pxSocket, FREERTOS_IPPROTO_UDP, pdFALSE ); + + if( ( xReturn == pdTRUE ) && ( !socketSOCKET_IS_BOUND( pxSocket ) ) ) + { + /* The socket is valid but it is not yet bound. */ + if( FreeRTOS_bind( pxSocket, NULL, 0U ) != 0 ) + { + /* The socket was not yet bound, and binding it has failed. */ + xReturn = pdFALSE; + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Send data to a socket. The socket must have already been created by a + * successful call to FreeRTOS_socket(). It works for UDP-sockets only. + * + * @param[in] xSocket: The socket being sent to. + * @param[in] pvBuffer: Pointer to the data being sent. + * @param[in] uxTotalDataLength: Length (in bytes) of the data being sent. + * @param[in] xFlags: Flags used to communicate preferences to the function. + * Possibly FREERTOS_MSG_DONTWAIT and/or FREERTOS_ZERO_COPY. + * @param[in] pxDestinationAddress: The address to which the data is to be sent. + * @param[in] xDestinationAddressLength: This parameter is present to adhere to the + * Berkeley sockets standard. Else, it is not used. + * + * @return When positive: the total number of bytes sent, when negative an error + * has occurred: it can be looked-up in 'FreeRTOS_errno_TCP.h'. + */ +int32_t FreeRTOS_sendto( Socket_t xSocket, + const void * pvBuffer, + size_t uxTotalDataLength, + BaseType_t xFlags, + const struct freertos_sockaddr * pxDestinationAddress, + socklen_t xDestinationAddressLength ) +{ + NetworkBufferDescriptor_t * pxNetworkBuffer; + void * pvCopyDest; + IPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL }; + TimeOut_t xTimeOut; + TickType_t xTicksToWait; + int32_t lReturn = 0; + FreeRTOS_Socket_t * pxSocket; + const size_t uxMaxPayloadLength = ipMAX_UDP_PAYLOAD_LENGTH; + const size_t uxPayloadOffset = ipUDP_PAYLOAD_OFFSET_IPv4; + + + pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + + /* The function prototype is designed to maintain the expected Berkeley + * sockets standard, but this implementation does not use all the + * parameters. */ + ( void ) xDestinationAddressLength; + configASSERT( pvBuffer != NULL ); + + if( uxTotalDataLength <= ( size_t ) uxMaxPayloadLength ) + { + /* If the socket is not already bound to an address, bind it now. + * Passing NULL as the address parameter tells FreeRTOS_bind() to select + * the address to bind to. */ + if( prvMakeSureSocketIsBound( pxSocket ) == pdTRUE ) + { + xTicksToWait = pxSocket->xSendBlockTime; + + #if ( ipconfigUSE_CALLBACKS != 0 ) + { + if( xIsCallingFromIPTask() != pdFALSE ) + { + /* If this send function is called from within a call-back + * handler it may not block, otherwise chances would be big to + * get a deadlock: the IP-task waiting for itself. */ + xTicksToWait = ( TickType_t ) 0; + } + } + #endif /* ipconfigUSE_CALLBACKS */ + + if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_MSG_DONTWAIT ) != 0U ) + { + xTicksToWait = ( TickType_t ) 0; + } + + if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_ZERO_COPY ) == 0U ) + { + /* Zero copy is not set, so obtain a network buffer into + * which the payload will be copied. */ + vTaskSetTimeOutState( &xTimeOut ); + + /* Block until a buffer becomes available, or until a + * timeout has been reached */ + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxPayloadOffset + uxTotalDataLength, xTicksToWait ); + + if( pxNetworkBuffer != NULL ) + { + pvCopyDest = ( void * ) &pxNetworkBuffer->pucEthernetBuffer[ uxPayloadOffset ]; + ( void ) memcpy( pvCopyDest, pvBuffer, uxTotalDataLength ); + + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdTRUE ) + { + /* The entire block time has been used up. */ + xTicksToWait = ( TickType_t ) 0; + } + } + } + else + { + /* When zero copy is used, pvBuffer is a pointer to the + * payload of a buffer that has already been obtained from the + * stack. Obtain the network buffer pointer from the buffer. */ + pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pvBuffer ); + } + + if( pxNetworkBuffer != NULL ) + { + /* xDataLength is the size of the total packet, including the Ethernet header. */ + pxNetworkBuffer->xDataLength = uxTotalDataLength + sizeof( UDPPacket_t ); + pxNetworkBuffer->usPort = pxDestinationAddress->sin_port; + pxNetworkBuffer->usBoundPort = ( uint16_t ) socketGET_SOCKET_PORT( pxSocket ); + pxNetworkBuffer->ulIPAddress = pxDestinationAddress->sin_addr; + + /* The socket options are passed to the IP layer in the + * space that will eventually get used by the Ethernet header. */ + pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] = pxSocket->ucSocketOptions; + + /* Tell the networking task that the packet needs sending. */ + xStackTxEvent.pvData = pxNetworkBuffer; + + /* Ask the IP-task to send this packet */ + if( xSendEventStructToIPTask( &xStackTxEvent, xTicksToWait ) == pdPASS ) + { + /* The packet was successfully sent to the IP task. */ + lReturn = ( int32_t ) uxTotalDataLength; + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xUDP.pxHandleSent ) ) + { + pxSocket->u.xUDP.pxHandleSent( xSocket, uxTotalDataLength ); + } + } + #endif /* ipconfigUSE_CALLBACKS */ + } + else + { + /* If the buffer was allocated in this function, release + * it. */ + if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_ZERO_COPY ) == 0U ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT ); + } + } + else + { + /* If errno was available, errno would be set to + * FREERTOS_ENOPKTS. As it is, the function must return the + * number of transmitted bytes, so the calling function knows + * how much data was actually sent. */ + iptraceNO_BUFFER_FOR_SENDTO(); + } + } + else + { + /* No comment. */ + iptraceSENDTO_SOCKET_NOT_BOUND(); + } + } + else + { + /* The data is longer than the available buffer space. */ + iptraceSENDTO_DATA_TOO_LONG(); + } + + return lReturn; +} /* Tested */ +/*-----------------------------------------------------------*/ + +/** + * @brief binds a socket to a local port number. If port 0 is provided, + * a system provided port number will be assigned. This function + * can be used for both UDP and TCP sockets. The actual binding + * will be performed by the IP-task to avoid mutual access to the + * bound-socket-lists (xBoundUDPSocketsList or xBoundTCPSocketsList). + * + * @param[in] xSocket: The socket being bound. + * @param[in] pxAddress: The address struct carrying the port number to which + * this socket is to be bound. + * @param[in] xAddressLength: This parameter is not used internally. The + * function signature is used to adhere to standard + * Berkeley sockets API. + * + * @return The return value is 0 if the bind is successful. + * If some error occurred, then a negative value is returned. + */ +BaseType_t FreeRTOS_bind( Socket_t xSocket, + struct freertos_sockaddr const * pxAddress, + socklen_t xAddressLength ) +{ + IPStackEvent_t xBindEvent; + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + BaseType_t xReturn = 0; + + ( void ) xAddressLength; + + configASSERT( xIsCallingFromIPTask() == pdFALSE ); + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + if( ( pxSocket == NULL ) || ( pxSocket == FREERTOS_INVALID_SOCKET ) ) + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + + /* Once a socket is bound to a port, it can not be bound to a different + * port number */ + else if( socketSOCKET_IS_BOUND( pxSocket ) ) + { + /* The socket is already bound. */ + FreeRTOS_debug_printf( ( "vSocketBind: Socket already bound to %d\n", pxSocket->usLocalPort ) ); + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + /* Prepare a messages to the IP-task in order to perform the binding. + * The desired port number will be passed in usLocalPort. */ + xBindEvent.eEventType = eSocketBindEvent; + xBindEvent.pvData = xSocket; + + if( pxAddress != NULL ) + { + pxSocket->usLocalPort = FreeRTOS_ntohs( pxAddress->sin_port ); + } + else + { + /* Caller wants to bind to a random port number. */ + pxSocket->usLocalPort = 0U; + } + + /* portMAX_DELAY is used as a the time-out parameter, as binding *must* + * succeed before the socket can be used. _RB_ The use of an infinite + * block time needs be changed as it could result in the task hanging. */ + if( xSendEventStructToIPTask( &xBindEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL ) + { + /* Failed to wake-up the IP-task, no use to wait for it */ + FreeRTOS_debug_printf( ( "FreeRTOS_bind: send event failed\n" ) ); + xReturn = -pdFREERTOS_ERRNO_ECANCELED; + } + else + { + /* The IP-task will set the 'eSOCKET_BOUND' bit when it has done its + * job. */ + ( void ) xEventGroupWaitBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_BOUND, pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, portMAX_DELAY ); + + if( !socketSOCKET_IS_BOUND( pxSocket ) ) + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Internal version of bind() that should not be called directly. + * 'xInternal' is used for TCP sockets only: it allows to have several + * (connected) child sockets bound to the same server port. + * + * @param[in] pxSocket: The socket is to be bound. + * @param[in] pxBindAddress: The port to which this socket should be bound. + * @param[in] uxAddressLength: The address length. + * @param[in] xInternal: pdTRUE is calling internally, else pdFALSE. + * + * @return If the socket was bound to a port successfully, then a 0 is returned. + * Or else, an error code is returned. + */ +BaseType_t vSocketBind( FreeRTOS_Socket_t * pxSocket, + struct freertos_sockaddr * pxBindAddress, + size_t uxAddressLength, + BaseType_t xInternal ) +{ + BaseType_t xReturn = 0; /* In Berkeley sockets, 0 means pass for bind(). */ + List_t * pxSocketList; + struct freertos_sockaddr * pxAddress = pxBindAddress; + + #if ( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 ) + struct freertos_sockaddr xAddress; + #endif /* ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND */ + + configASSERT( pxSocket != NULL ); + configASSERT( pxSocket != FREERTOS_INVALID_SOCKET ); + + #if ( ipconfigUSE_TCP == 1 ) + if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + pxSocketList = &xBoundTCPSocketsList; + } + else + #endif /* ipconfigUSE_TCP == 1 */ + { + pxSocketList = &xBoundUDPSocketsList; + } + + /* The function prototype is designed to maintain the expected Berkeley + * sockets standard, but this implementation does not use all the parameters. */ + ( void ) uxAddressLength; + + #if ( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 ) + { + /* pxAddress will be NULL if sendto() was called on a socket without the + * socket being bound to an address. In this case, automatically allocate + * an address to the socket. There is a small chance that the allocated + * port will already be in use - if that is the case, then the check below + * [pxListFindListItemWithValue()] will result in an error being returned. */ + if( pxAddress == NULL ) + { + pxAddress = &xAddress; + /* Put the port to zero to be assigned later. */ + pxAddress->sin_port = 0U; + } + } + #endif /* ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 */ + + /* Sockets must be bound before calling FreeRTOS_sendto() if + * ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND is not set to 1. */ + configASSERT( pxAddress != NULL ); + + #if ( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 ) + /* pxAddress is not NULL, no testing needed. */ + #else + if( pxAddress != NULL ) + #endif + { + /* Add a do-while loop to facilitate use of 'break' statements. */ + do + { + if( pxAddress->sin_port == 0U ) + { + pxAddress->sin_port = prvGetPrivatePortNumber( ( BaseType_t ) pxSocket->ucProtocol ); + + if( pxAddress->sin_port == ( uint16_t ) 0U ) + { + xReturn = -pdFREERTOS_ERRNO_EADDRNOTAVAIL; + break; + } + } + + /* If vSocketBind() is called from the API FreeRTOS_bind() it has been + * confirmed that the socket was not yet bound to a port. If it is called + * from the IP-task, no such check is necessary. */ + + /* Check to ensure the port is not already in use. If the bind is + * called internally, a port MAY be used by more than one socket. */ + if( ( ( xInternal == pdFALSE ) || ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) ) && + ( pxListFindListItemWithValue( pxSocketList, ( TickType_t ) pxAddress->sin_port ) != NULL ) ) + { + FreeRTOS_debug_printf( ( "vSocketBind: %sP port %d in use\n", + ( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) ? "TC" : "UD", + FreeRTOS_ntohs( pxAddress->sin_port ) ) ); + xReturn = -pdFREERTOS_ERRNO_EADDRINUSE; + } + else + { + /* Allocate the port number to the socket. + * This macro will set 'xBoundSocketListItem->xItemValue' */ + socketSET_SOCKET_PORT( pxSocket, pxAddress->sin_port ); + + /* And also store it in a socket field 'usLocalPort' in host-byte-order, + * mostly used for logging and debugging purposes */ + pxSocket->usLocalPort = FreeRTOS_ntohs( pxAddress->sin_port ); + + /* Add the socket to the list of bound ports. */ + { + /* If the network driver can iterate through 'xBoundUDPSocketsList', + * by calling xPortHasUDPSocket() then the IP-task must temporarily + * suspend the scheduler to keep the list in a consistent state. */ + #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) + { + vTaskSuspendAll(); + } + #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ + + /* Add the socket to 'xBoundUDPSocketsList' or 'xBoundTCPSocketsList' */ + vListInsertEnd( pxSocketList, &( pxSocket->xBoundSocketListItem ) ); + + #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) + { + ( void ) xTaskResumeAll(); + } + #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ + } + } + } while( ipFALSE_BOOL ); + } + + #if ( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 0 ) + else + { + xReturn = -pdFREERTOS_ERRNO_EADDRNOTAVAIL; + FreeRTOS_debug_printf( ( "vSocketBind: Socket no addr\n" ) ); + } + #endif + + if( xReturn != 0 ) + { + iptraceBIND_FAILED( xSocket, ( FreeRTOS_ntohs( pxAddress->sin_port ) ) ); + } + + return xReturn; +} /* Tested */ +/*-----------------------------------------------------------*/ + +/** + * @brief Close a socket and free the allocated space. In case of a TCP socket: + * the connection will not be closed automatically. Subsequent messages + * for the closed socket will be responded to with a RST. The IP-task + * will actually close the socket, after receiving a 'eSocketCloseEvent' + * message. + * + * @param[in] xSocket: the socket being closed. + * + * @return There are three distinct values which can be returned: + * 0: If the xSocket is NULL/invalid. + * 1: If the socket was successfully closed (read the brief above). + * -1: If the socket was valid but could not be closed because the message + * could not be delivered to the IP-task. Try again later. + */ +BaseType_t FreeRTOS_closesocket( Socket_t xSocket ) +{ + BaseType_t xResult; + + #if ( ipconfigUSE_CALLBACKS == 1 ) + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + #endif /* ipconfigUSE_CALLBACKS == 1 */ + IPStackEvent_t xCloseEvent; + xCloseEvent.eEventType = eSocketCloseEvent; + xCloseEvent.pvData = xSocket; + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + if( ( xSocket == NULL ) || ( xSocket == FREERTOS_INVALID_SOCKET ) ) + { + xResult = 0; + } + else + { + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + #if ( ipconfigUSE_TCP == 1 ) + if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + /* Make sure that IP-task won't call the user callback's anymore */ + pxSocket->u.xTCP.pxHandleConnected = NULL; + pxSocket->u.xTCP.pxHandleReceive = NULL; + pxSocket->u.xTCP.pxHandleSent = NULL; + } + else + #endif /* ipconfigUSE_TCP == 1 */ + + if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_UDP ) + { + /* Clear the two UDP handlers. */ + pxSocket->u.xUDP.pxHandleReceive = NULL; + pxSocket->u.xUDP.pxHandleSent = NULL; + } + } + #endif /* ipconfigUSE_CALLBACKS == 1 */ + + /* Let the IP task close the socket to keep it synchronised with the + * packet handling. */ + + /* The timeout value below is only used if this function is called from + * a user task. If this function is called by the IP-task, it may fail + * to close the socket when the event queue is full. + * This should only happen in case of a user call-back. */ + if( xSendEventStructToIPTask( &xCloseEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL ) + { + FreeRTOS_debug_printf( ( "FreeRTOS_closesocket: failed\n" ) ); + xResult = -1; + } + else + { + xResult = 1; + } + } + + return xResult; +} + +/** + * @brief This is the internal version of FreeRTOS_closesocket(). It will + * be called by the IPtask only to avoid problems with synchronicity. + * + * @param[in] pxSocket: The socket descriptor of the socket being closed. + * + * @return Returns NULL, always. + */ +/* MISRA Ref 17.2.1 [Sockets and limited recursion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-172 */ +/* coverity[misra_c_2012_rule_17_2_violation] */ +void * vSocketClose( FreeRTOS_Socket_t * pxSocket ) +{ + NetworkBufferDescriptor_t * pxNetworkBuffer; + + #if ( ipconfigUSE_TCP == 1 ) + { + /* For TCP: clean up a little more. */ + if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + if( pxSocket->u.xTCP.pxAckMessage != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); + } + + /* Free the resources which were claimed by the tcpWin member */ + vTCPWindowDestroy( &pxSocket->u.xTCP.xTCPWindow ); + } + #endif /* ipconfigUSE_TCP_WIN */ + + /* Free the input and output streams */ + if( pxSocket->u.xTCP.rxStream != NULL ) + { + iptraceMEM_STATS_DELETE( pxSocket->u.xTCP.rxStream ); + vPortFreeLarge( pxSocket->u.xTCP.rxStream ); + } + + if( pxSocket->u.xTCP.txStream != NULL ) + { + iptraceMEM_STATS_DELETE( pxSocket->u.xTCP.txStream ); + vPortFreeLarge( pxSocket->u.xTCP.txStream ); + } + + /* In case this is a child socket, make sure the child-count of the + * parent socket is decreased. */ + prvTCPSetSocketCount( pxSocket ); + } + } + #endif /* ipconfigUSE_TCP == 1 */ + + /* Socket must be unbound first, to ensure no more packets are queued on + * it. */ + if( socketSOCKET_IS_BOUND( pxSocket ) ) + { + /* If the network driver can iterate through 'xBoundUDPSocketsList', + * by calling xPortHasUDPSocket(), then the IP-task must temporarily + * suspend the scheduler to keep the list in a consistent state. */ + #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) + { + vTaskSuspendAll(); + } + #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ + + ( void ) uxListRemove( &( pxSocket->xBoundSocketListItem ) ); + + #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) + { + ( void ) xTaskResumeAll(); + } + #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ + } + + /* Now the socket is not bound the list of waiting packets can be + * drained. */ + if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_UDP ) + { + while( listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) > 0U ) + { + pxNetworkBuffer = ( ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) ); + ( void ) uxListRemove( &( pxNetworkBuffer->xBufferListItem ) ); + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + } + + if( pxSocket->xEventGroup != NULL ) + { + vEventGroupDelete( pxSocket->xEventGroup ); + } + + #if ( ipconfigUSE_TCP == 1 ) && ( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + FreeRTOS_debug_printf( ( "FreeRTOS_closesocket[%u to %xip:%u]: buffers %u socks %u\n", + pxSocket->usLocalPort, + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort, + ( unsigned ) uxGetNumberOfFreeNetworkBuffers(), + ( unsigned ) listCURRENT_LIST_LENGTH( &xBoundTCPSocketsList ) ) ); + } + } + #endif /* ( ipconfigUSE_TCP == 1 ) && ( ipconfigHAS_DEBUG_PRINTF != 0 ) */ + + /* And finally, after all resources have been freed, free the socket space */ + iptraceMEM_STATS_DELETE( pxSocket ); + vPortFreeSocket( pxSocket ); + + return NULL; +} /* Tested */ + +/*-----------------------------------------------------------*/ + +#if ipconfigUSE_TCP == 1 + +/** + * @brief When a child socket gets closed, make sure to update the child-count of the + * parent. When a listening parent socket is closed, make sure to close also + * all orphaned child-sockets. + * + * @param[in] pxSocketToDelete: The socket being closed. + */ + /* MISRA Ref 17.2.1 [Sockets and limited recursion] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-172 */ + /* coverity[misra_c_2012_rule_17_2_violation] */ + /* coverity[recursive_step] */ + static void prvTCPSetSocketCount( FreeRTOS_Socket_t const * pxSocketToDelete ) + { + const ListItem_t * pxIterator; + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( xBoundTCPSocketsList.xListEnd ) ); + FreeRTOS_Socket_t * pxOtherSocket; + uint16_t usLocalPort = pxSocketToDelete->usLocalPort; + + if( pxSocketToDelete->u.xTCP.eTCPState == eTCP_LISTEN ) + { + pxIterator = listGET_NEXT( pxEnd ); + + while( pxIterator != pxEnd ) + { + pxOtherSocket = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + + /* This needs to be done here, before calling vSocketClose. */ + pxIterator = listGET_NEXT( pxIterator ); + + if( ( pxOtherSocket->u.xTCP.eTCPState != eTCP_LISTEN ) && + ( pxOtherSocket->usLocalPort == usLocalPort ) && + ( ( pxOtherSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED ) || + ( pxOtherSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) ) ) + { + /* MISRA Ref 17.2.1 [Sockets and limited recursion] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-172 */ + /* coverity[misra_c_2012_rule_17_2_violation] */ + /* coverity[recursive_step] */ + ( void ) vSocketClose( pxOtherSocket ); + } + } + } + else + { + for( pxIterator = listGET_NEXT( pxEnd ); + pxIterator != pxEnd; + pxIterator = listGET_NEXT( pxIterator ) ) + { + pxOtherSocket = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + + if( ( pxOtherSocket->u.xTCP.eTCPState == eTCP_LISTEN ) && + ( pxOtherSocket->usLocalPort == usLocalPort ) && + ( pxOtherSocket->u.xTCP.usChildCount != 0U ) ) + { + pxOtherSocket->u.xTCP.usChildCount--; + FreeRTOS_debug_printf( ( "Lost: Socket %u now has %u / %u child%s\n", + pxOtherSocket->usLocalPort, + pxOtherSocket->u.xTCP.usChildCount, + pxOtherSocket->u.xTCP.usBacklog, + ( pxOtherSocket->u.xTCP.usChildCount == 1U ) ? "" : "ren" ) ); + break; + } + } + } + } + + +#endif /* ipconfigUSE_TCP == 1 */ + +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Set the value of receive/send buffer after some preliminary checks. + * + * @param[in] pxSocket: The socket whose options are being set. + * @param[in] lOptionName: The option name: either FREERTOS_SO_SNDBUF or + * FREERTOS_SO_SNDBUF. + * @param[in] pvOptionValue: The value of the option being set. + * + * @return If there is no error, then 0 is returned. Or a negative errno + * value is returned. + */ + static BaseType_t prvSockopt_so_buffer( FreeRTOS_Socket_t * pxSocket, + int32_t lOptionName, + const void * pvOptionValue ) + { + uint32_t ulNewValue; + BaseType_t xReturn; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + FreeRTOS_debug_printf( ( "Set SO_%sBUF: wrong socket type\n", + ( lOptionName == FREERTOS_SO_SNDBUF ) ? "SND" : "RCV" ) ); + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else if( ( ( lOptionName == FREERTOS_SO_SNDBUF ) && ( pxSocket->u.xTCP.txStream != NULL ) ) || + ( ( lOptionName == FREERTOS_SO_RCVBUF ) && ( pxSocket->u.xTCP.rxStream != NULL ) ) ) + { + FreeRTOS_debug_printf( ( "Set SO_%sBUF: buffer already created\n", + ( lOptionName == FREERTOS_SO_SNDBUF ) ? "SND" : "RCV" ) ); + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + ulNewValue = *( ( const uint32_t * ) pvOptionValue ); + + if( lOptionName == FREERTOS_SO_SNDBUF ) + { + /* Round up to nearest MSS size */ + ulNewValue = FreeRTOS_round_up( ulNewValue, ( uint32_t ) pxSocket->u.xTCP.usMSS ); + pxSocket->u.xTCP.uxTxStreamSize = ulNewValue; + } + else + { + pxSocket->u.xTCP.uxRxStreamSize = ulNewValue; + } + + xReturn = 0; + } + + return xReturn; + } +#endif /* ipconfigUSE_TCP == 1 */ +/*-----------------------------------------------------------*/ + +/* FreeRTOS_setsockopt calls itself, but in a very limited way, + * only when FREERTOS_SO_WIN_PROPERTIES is being set. */ + +/** + * @brief Set the socket options for the given socket. + * + * @param[in] xSocket: The socket for which the options are to be set. + * @param[in] lLevel: Not used. Parameter is used to maintain the Berkeley sockets + * standard. + * @param[in] lOptionName: The name of the option to be set. + * @param[in] pvOptionValue: The value of the option to be set. + * @param[in] uxOptionLength: Not used. Parameter is used to maintain the Berkeley + * sockets standard. + * + * @return If the option can be set with the given value, then 0 is returned. Else, + * an error code is returned. + */ +BaseType_t FreeRTOS_setsockopt( Socket_t xSocket, + int32_t lLevel, + int32_t lOptionName, + const void * pvOptionValue, + size_t uxOptionLength ) +{ +/* The standard Berkeley function returns 0 for success. */ + BaseType_t xReturn = -pdFREERTOS_ERRNO_EINVAL; + FreeRTOS_Socket_t * pxSocket; + + pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + + /* The function prototype is designed to maintain the expected Berkeley + * sockets standard, but this implementation does not use all the parameters. */ + ( void ) lLevel; + ( void ) uxOptionLength; + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + if( ( pxSocket != NULL ) && ( pxSocket != FREERTOS_INVALID_SOCKET ) ) + { + switch( lOptionName ) + { + case FREERTOS_SO_RCVTIMEO: + /* Receive time out. */ + pxSocket->xReceiveBlockTime = *( ( const TickType_t * ) pvOptionValue ); + xReturn = 0; + break; + + case FREERTOS_SO_SNDTIMEO: + pxSocket->xSendBlockTime = *( ( const TickType_t * ) pvOptionValue ); + + if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_UDP ) + { + /* The send time out is capped for the reason stated in the + * comments where ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS is defined + * in FreeRTOSIPConfig.h (assuming an official configuration file + * is being used. */ + if( pxSocket->xSendBlockTime > ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS ) + { + pxSocket->xSendBlockTime = ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS; + } + } + else + { + /* For TCP socket, it isn't necessary to limit the blocking time + * because the FreeRTOS_send() function does not wait for a network + * buffer to become available. */ + } + + xReturn = 0; + break; + + #if ( ipconfigUDP_MAX_RX_PACKETS > 0U ) + case FREERTOS_SO_UDP_MAX_RX_PACKETS: + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) + { + break; /* will return -pdFREERTOS_ERRNO_EINVAL */ + } + + pxSocket->u.xUDP.uxMaxPackets = *( ( const UBaseType_t * ) pvOptionValue ); + xReturn = 0; + break; + #endif /* ipconfigUDP_MAX_RX_PACKETS */ + + case FREERTOS_SO_UDPCKSUM_OUT: + + /* Turn calculating of the UDP checksum on/off for this socket. If pvOptionValue + * is anything else than NULL, the checksum generation will be turned on. */ + + if( pvOptionValue == NULL ) + { + pxSocket->ucSocketOptions &= ~( ( uint8_t ) FREERTOS_SO_UDPCKSUM_OUT ); + } + else + { + pxSocket->ucSocketOptions |= ( uint8_t ) FREERTOS_SO_UDPCKSUM_OUT; + } + + xReturn = 0; + break; + + #if ( ipconfigUSE_CALLBACKS == 1 ) + #if ( ipconfigUSE_TCP == 1 ) + case FREERTOS_SO_TCP_CONN_HANDLER: /* Set a callback for (dis)connection events */ + case FREERTOS_SO_TCP_RECV_HANDLER: /* Install a callback for receiving TCP data. Supply pointer to 'F_TCP_UDP_Handler_t' (see below) */ + case FREERTOS_SO_TCP_SENT_HANDLER: /* Install a callback for sending TCP data. Supply pointer to 'F_TCP_UDP_Handler_t' (see below) */ + #endif /* ipconfigUSE_TCP */ + case FREERTOS_SO_UDP_RECV_HANDLER: /* Install a callback for receiving UDP data. Supply pointer to 'F_TCP_UDP_Handler_t' (see below) */ + case FREERTOS_SO_UDP_SENT_HANDLER: /* Install a callback for sending UDP data. Supply pointer to 'F_TCP_UDP_Handler_t' (see below) */ + { + #if ( ipconfigUSE_TCP == 1 ) + { + UBaseType_t uxProtocol; + + if( ( lOptionName == FREERTOS_SO_UDP_RECV_HANDLER ) || + ( lOptionName == FREERTOS_SO_UDP_SENT_HANDLER ) ) + { + uxProtocol = ( UBaseType_t ) FREERTOS_IPPROTO_UDP; + } + else + { + uxProtocol = ( UBaseType_t ) FREERTOS_IPPROTO_TCP; + } + + if( pxSocket->ucProtocol != ( uint8_t ) uxProtocol ) + { + break; /* will return -pdFREERTOS_ERRNO_EINVAL */ + } + } + #else /* if ( ipconfigUSE_TCP == 1 ) */ + { + /* No need to check if the socket has the right + * protocol, because only UDP socket can be created. */ + } + #endif /* ipconfigUSE_TCP */ + + switch( lOptionName ) + { + #if ipconfigUSE_TCP == 1 + case FREERTOS_SO_TCP_CONN_HANDLER: + pxSocket->u.xTCP.pxHandleConnected = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnTCPConnected; + break; + + case FREERTOS_SO_TCP_RECV_HANDLER: + pxSocket->u.xTCP.pxHandleReceive = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnTCPReceive; + break; + + case FREERTOS_SO_TCP_SENT_HANDLER: + pxSocket->u.xTCP.pxHandleSent = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnTCPSent; + break; + #endif /* ipconfigUSE_TCP */ + case FREERTOS_SO_UDP_RECV_HANDLER: + pxSocket->u.xUDP.pxHandleReceive = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnUDPReceive; + break; + + case FREERTOS_SO_UDP_SENT_HANDLER: + pxSocket->u.xUDP.pxHandleSent = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnUDPSent; + break; + + default: /* LCOV_EXCL_LINE The default case is required by MISRA but control flow will never ever reach + * here since the switch statement enclosing this switch prevents that. */ + /* Should it throw an error here? */ + break; /* LCOV_EXCL_LINE. Since the default case will never reach, this break statement will not execute as well. */ + } + } + + xReturn = 0; + break; + #endif /* ipconfigUSE_CALLBACKS */ + + #if ( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 ) + + /* Each socket has a semaphore on which the using task normally + * sleeps. */ + case FREERTOS_SO_SET_SEMAPHORE: + { + pxSocket->pxUserSemaphore = *( ( SemaphoreHandle_t * ) pvOptionValue ); + } + xReturn = 0; + break; + #endif /* ipconfigSOCKET_HAS_USER_SEMAPHORE */ + + #if ( ipconfigSOCKET_HAS_USER_WAKE_CALLBACK != 0 ) + case FREERTOS_SO_WAKEUP_CALLBACK: + + /* Each socket can have a callback function that is executed + * when there is an event the socket's owner might want to + * process. */ + + /* The type cast of the pointer expression "A" to + * type "B" removes const qualifier from the pointed to type. */ + + /* MISRA Ref 11.8.1 [Function pointer and use of const pointer] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-118 */ + + /* MISRA Ref 11.1.1 [ Conversion between pointer to + * a function and another type ] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-111 */ + /* coverity[misra_c_2012_rule_11_8_violation] */ + /* coverity[misra_c_2012_rule_11_1_violation] */ + pxSocket->pxUserWakeCallback = ( SocketWakeupCallback_t ) pvOptionValue; + xReturn = 0; + break; + #endif /* ipconfigSOCKET_HAS_USER_WAKE_CALLBACK */ + + #if ( ipconfigUSE_TCP != 0 ) + case FREERTOS_SO_SET_LOW_HIGH_WATER: + { + const LowHighWater_t * pxLowHighWater = ( const LowHighWater_t * ) pvOptionValue; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + /* It is not allowed to access 'pxSocket->u.xTCP'. */ + FreeRTOS_debug_printf( ( "FREERTOS_SO_SET_LOW_HIGH_WATER: wrong socket type\n" ) ); + break; /* will return -pdFREERTOS_ERRNO_EINVAL */ + } + + if( ( pxLowHighWater->uxLittleSpace >= pxLowHighWater->uxEnoughSpace ) || + ( pxLowHighWater->uxEnoughSpace > pxSocket->u.xTCP.uxRxStreamSize ) ) + { + /* Impossible values. */ + FreeRTOS_debug_printf( ( "FREERTOS_SO_SET_LOW_HIGH_WATER: bad values\n" ) ); + break; /* will return -pdFREERTOS_ERRNO_EINVAL */ + } + + /* Send a STOP when buffer space drops below 'uxLittleSpace' bytes. */ + pxSocket->u.xTCP.uxLittleSpace = pxLowHighWater->uxLittleSpace; + /* Send a GO when buffer space grows above 'uxEnoughSpace' bytes. */ + pxSocket->u.xTCP.uxEnoughSpace = pxLowHighWater->uxEnoughSpace; + xReturn = 0; + } + break; + + case FREERTOS_SO_SNDBUF: /* Set the size of the send buffer, in units of MSS (TCP only) */ + case FREERTOS_SO_RCVBUF: /* Set the size of the receive buffer, in units of MSS (TCP only) */ + xReturn = prvSockopt_so_buffer( pxSocket, lOptionName, pvOptionValue ); + break; + + case FREERTOS_SO_WIN_PROPERTIES: /* Set all buffer and window properties in one call, parameter is pointer to WinProperties_t */ + { + IPTCPSocket_t * pxTCP = &( pxSocket->u.xTCP ); + const WinProperties_t * pxProps; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + FreeRTOS_debug_printf( ( "Set SO_WIN_PROP: wrong socket type\n" ) ); + break; /* will return -pdFREERTOS_ERRNO_EINVAL */ + } + + pxProps = ( const WinProperties_t * ) pvOptionValue; + + /* Validity of txStream will be checked by the function below. */ + xReturn = prvSockopt_so_buffer( pxSocket, FREERTOS_SO_SNDBUF, &( pxProps->lTxBufSize ) ); + + if( xReturn != 0 ) + { + break; /* will return an error. */ + } + + /* Validity of rxStream will be checked by the function below. */ + xReturn = prvSockopt_so_buffer( pxSocket, FREERTOS_SO_RCVBUF, &( pxProps->lRxBufSize ) ); + + if( xReturn != 0 ) + { + break; /* will return an error. */ + } + + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + pxTCP->uxRxWinSize = ( uint32_t ) pxProps->lRxWinSize; /* Fixed value: size of the TCP reception window */ + pxTCP->uxTxWinSize = ( uint32_t ) pxProps->lTxWinSize; /* Fixed value: size of the TCP transmit window */ + } + #else + { + pxTCP->uxRxWinSize = 1U; + pxTCP->uxTxWinSize = 1U; + } + #endif + + /* In case the socket has already initialised its tcpWin, + * adapt the window size parameters */ + if( pxTCP->xTCPWindow.u.bits.bHasInit != pdFALSE_UNSIGNED ) + { + pxTCP->xTCPWindow.xSize.ulRxWindowLength = ( uint32_t ) ( pxTCP->uxRxWinSize * pxTCP->usMSS ); + pxTCP->xTCPWindow.xSize.ulTxWindowLength = ( uint32_t ) ( pxTCP->uxTxWinSize * pxTCP->usMSS ); + } + } + + xReturn = 0; + break; + + case FREERTOS_SO_REUSE_LISTEN_SOCKET: /* If true, the server-socket will turn into a connected socket */ + { + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + break; /* will return -pdFREERTOS_ERRNO_EINVAL */ + } + + if( *( ( const BaseType_t * ) pvOptionValue ) != 0 ) + { + pxSocket->u.xTCP.bits.bReuseSocket = pdTRUE; + } + else + { + pxSocket->u.xTCP.bits.bReuseSocket = pdFALSE; + } + } + xReturn = 0; + break; + + case FREERTOS_SO_CLOSE_AFTER_SEND: /* As soon as the last byte has been transmitted, finalise the connection */ + { + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + break; /* will return -pdFREERTOS_ERRNO_EINVAL */ + } + + if( *( ( const BaseType_t * ) pvOptionValue ) != 0 ) + { + pxSocket->u.xTCP.bits.bCloseAfterSend = pdTRUE; + } + else + { + pxSocket->u.xTCP.bits.bCloseAfterSend = pdFALSE; + } + } + xReturn = 0; + break; + + case FREERTOS_SO_SET_FULL_SIZE: /* Refuse to send packets smaller than MSS */ + { + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + break; /* will return -pdFREERTOS_ERRNO_EINVAL */ + } + + if( *( ( const BaseType_t * ) pvOptionValue ) != 0 ) + { + pxSocket->u.xTCP.xTCPWindow.u.bits.bSendFullSize = pdTRUE; + } + else + { + pxSocket->u.xTCP.xTCPWindow.u.bits.bSendFullSize = pdFALSE; + } + + if( ( pxSocket->u.xTCP.xTCPWindow.u.bits.bSendFullSize == pdFALSE_UNSIGNED ) && + ( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) && + ( FreeRTOS_outstanding( pxSocket ) > 0 ) ) + { + pxSocket->u.xTCP.usTimeout = 1U; /* to set/clear bSendFullSize */ + ( void ) xSendEventToIPTask( eTCPTimerEvent ); + } + } + xReturn = 0; + break; + + case FREERTOS_SO_STOP_RX: /* Refuse to receive more packets. */ + { + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + break; /* will return -pdFREERTOS_ERRNO_EINVAL */ + } + + if( *( ( const BaseType_t * ) pvOptionValue ) != 0 ) + { + pxSocket->u.xTCP.bits.bRxStopped = pdTRUE; + } + else + { + pxSocket->u.xTCP.bits.bRxStopped = pdFALSE; + } + + pxSocket->u.xTCP.bits.bWinChange = pdTRUE; + pxSocket->u.xTCP.usTimeout = 1U; /* to set/clear bRxStopped */ + ( void ) xSendEventToIPTask( eTCPTimerEvent ); + } + xReturn = 0; + break; + #endif /* ipconfigUSE_TCP == 1 */ + + default: + /* No other options are handled. */ + xReturn = -pdFREERTOS_ERRNO_ENOPROTOOPT; + break; + } + } + else + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + + return xReturn; +} /* Tested */ + +/*-----------------------------------------------------------*/ + +/** + * @brief Find an available port number per https://tools.ietf.org/html/rfc6056. + * + * @param[in] xProtocol: FREERTOS_IPPROTO_TCP/FREERTOS_IPPROTO_UDP. + * + * @return If an available protocol port is found then that port number is returned. + * Or else, 0 is returned. + */ +static uint16_t prvGetPrivatePortNumber( BaseType_t xProtocol ) +{ + const uint16_t usEphemeralPortCount = + socketAUTO_PORT_ALLOCATION_MAX_NUMBER - ( socketAUTO_PORT_ALLOCATION_START_NUMBER - 1U ); + uint16_t usIterations = usEphemeralPortCount; + uint32_t ulRandomSeed = 0; + uint16_t usResult = 0; + const List_t * pxList; + + #if ipconfigUSE_TCP == 1 + if( xProtocol == ( BaseType_t ) FREERTOS_IPPROTO_TCP ) + { + pxList = &xBoundTCPSocketsList; + } + else + #endif + { + pxList = &xBoundUDPSocketsList; + } + + /* Avoid compiler warnings if ipconfigUSE_TCP is not defined. */ + ( void ) xProtocol; + + /* Find the next available port using the random seed as a starting + * point. */ + do + { + /* Only proceed if the random number generator succeeded. */ + if( xApplicationGetRandomNumber( &( ulRandomSeed ) ) == pdFALSE ) + { + break; + } + + /* Map the random to a candidate port. */ + usResult = + socketAUTO_PORT_ALLOCATION_START_NUMBER + + ( ( ( uint16_t ) ulRandomSeed ) % usEphemeralPortCount ); + + /* Check if there's already an open socket with the same protocol + * and port. */ + if( NULL == pxListFindListItemWithValue( + pxList, + ( TickType_t ) FreeRTOS_htons( usResult ) ) ) + { + usResult = FreeRTOS_htons( usResult ); + break; + } + else + { + usResult = 0; + } + + usIterations--; + } + while( usIterations > 0U ); + + return usResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Find a list item associated with the wanted-item. + * + * @param[in] pxList: The list through which the search is to be conducted. + * @param[in] xWantedItemValue: The wanted item whose association is to be found. + * + * @return The list item holding the value being searched for. If nothing is found, + * then a NULL is returned. + */ +static const ListItem_t * pxListFindListItemWithValue( const List_t * pxList, + TickType_t xWantedItemValue ) +{ + const ListItem_t * pxResult = NULL; + + if( ( xIPIsNetworkTaskReady() != pdFALSE ) && ( pxList != NULL ) ) + { + const ListItem_t * pxIterator; + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( pxList->xListEnd ) ); + + for( pxIterator = listGET_NEXT( pxEnd ); + pxIterator != pxEnd; + pxIterator = listGET_NEXT( pxIterator ) ) + { + if( listGET_LIST_ITEM_VALUE( pxIterator ) == xWantedItemValue ) + { + pxResult = pxIterator; + break; + } + } + } + + return pxResult; +} /* Tested */ + +/*-----------------------------------------------------------*/ + +/** + * @brief Find the UDP socket corresponding to the port number. + * + * @param[in] uxLocalPort: The port whose corresponding bound UDP socket + * is to be found. + * + * @return The socket owning the port if found or else NULL. + */ +FreeRTOS_Socket_t * pxUDPSocketLookup( UBaseType_t uxLocalPort ) +{ + const ListItem_t * pxListItem; + FreeRTOS_Socket_t * pxSocket = NULL; + + /* Looking up a socket is quite simple, find a match with the local port. + * + * See if there is a list item associated with the port number on the + * list of bound sockets. */ + pxListItem = pxListFindListItemWithValue( &xBoundUDPSocketsList, ( TickType_t ) uxLocalPort ); + + if( pxListItem != NULL ) + { + /* The owner of the list item is the socket itself. */ + pxSocket = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxListItem ) ); + configASSERT( pxSocket != NULL ); + } + + return pxSocket; +} + +/*-----------------------------------------------------------*/ + +#define sockDIGIT_COUNT ( 3U ) /**< Each nibble is expressed in at most 3 digits such as "192". */ + +/** + * @brief Convert the 32-bit representation of the IP-address to the dotted decimal + * notation after some checks. + * A safe alternative is FreeRTOS_inet_ntop4(). + * + * @param[in] ulIPAddress: 32-bit representation of the IP-address. + * @param[out] pcBuffer: The buffer where the dotted decimal representation will be + * stored if all checks pass. The buffer must be at least 16 + * bytes long. + * + * @return The pointer returned will be same as pcBuffer and will have the address + * stored in the location. + */ +const char * FreeRTOS_inet_ntoa( uint32_t ulIPAddress, + char * pcBuffer ) +{ + socklen_t uxNibble; + socklen_t uxIndex = 0; + const uint8_t * pucAddress = ( const uint8_t * ) &( ulIPAddress ); + const char * pcResult = pcBuffer; + + for( uxNibble = 0; uxNibble < ipSIZE_OF_IPv4_ADDRESS; uxNibble++ ) + { + uint8_t pucDigits[ sockDIGIT_COUNT ]; + uint8_t ucValue = pucAddress[ uxNibble ]; + socklen_t uxSource = ( socklen_t ) sockDIGIT_COUNT - ( socklen_t ) 1U; + + for( ; ; ) + { + pucDigits[ uxSource ] = ucValue % ( uint8_t ) 10U; + ucValue /= ( uint8_t ) 10U; + + if( uxSource == 1U ) + { + break; + } + + uxSource--; + } + + pucDigits[ 0 ] = ucValue; + + /* Skip leading zeros. */ + for( uxSource = 0; uxSource < ( ( socklen_t ) sockDIGIT_COUNT - ( socklen_t ) 1U ); uxSource++ ) + { + if( pucDigits[ uxSource ] != 0U ) + { + break; + } + } + + for( ; uxSource < ( socklen_t ) sockDIGIT_COUNT; uxSource++ ) + { + pcBuffer[ uxIndex ] = ( char ) ( pucDigits[ uxSource ] + ( char ) '0' ); + uxIndex++; + } + + if( uxNibble < ( ipSIZE_OF_IPv4_ADDRESS - 1U ) ) + { + pcBuffer[ uxIndex ] = '.'; + } + else + { + pcBuffer[ uxIndex ] = '\0'; + } + + uxIndex++; + } + + return pcResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Convert the dotted decimal format of the IP-address to the 32-bit representation. + * + * @param[in] xAddressFamily: The Address family to which the IP-address belongs to. Only + * FREERTOS_AF_INET (IPv4) is supported. + * @param[in] pcSource: Pointer to the string holding the dotted decimal representation of + * the IP-address. + * @param[out] pvDestination: The pointer to the address struct/variable where the converted + * IP-address will be stored. The buffer must be 4 bytes long + * in case of a IPv4 address. + * + * @return If all checks pass, then pdPASS is returned or else pdFAIL is returned. + */ +BaseType_t FreeRTOS_inet_pton( BaseType_t xAddressFamily, + const char * pcSource, + void * pvDestination ) +{ + BaseType_t xResult; + + /* Printable string to struct sockaddr. */ + switch( xAddressFamily ) + { + case FREERTOS_AF_INET: + xResult = FreeRTOS_inet_pton4( pcSource, pvDestination ); + break; + + default: + xResult = -pdFREERTOS_ERRNO_EAFNOSUPPORT; + break; + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Convert the 32-bit representation of the IP-address to the dotted + * decimal format based on the Address Family. (Only FREERTOS_AF_INET + * is allowed). + * + * @param[in] xAddressFamily: The address family of the IP-address. + * @param[in] pvSource: Pointer to the 32-bit representation of IP-address. + * @param[out] pcDestination: The pointer to the character array where the dotted + * decimal address will be stored if every check does pass. + * @param[in] uxSize: Size of the character array. This value makes sure that the code + * doesn't write beyond it's bounds. + * + * @return If every check does pass, then the pointer to the pcDestination is returned + * holding the dotted decimal format of IP-address. Else, a NULL is returned. + */ +const char * FreeRTOS_inet_ntop( BaseType_t xAddressFamily, + const void * pvSource, + char * pcDestination, + socklen_t uxSize ) +{ + const char * pcResult; + + /* Printable struct sockaddr to string. */ + switch( xAddressFamily ) + { + case FREERTOS_AF_INET: + pcResult = FreeRTOS_inet_ntop4( pvSource, pcDestination, uxSize ); + break; + + default: + /* errno should be set to pdFREERTOS_ERRNO_EAFNOSUPPORT. */ + pcResult = NULL; + break; + } + + return pcResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Convert the 32-bit representation of the IP-address to the dotted decimal format. + * + * @param[in] pvSource: The pointer to the 32-bit representation of the IP-address. + * @param[out] pcDestination: The pointer to a character array where the string of the + * dotted decimal IP format. + * @param[in] uxSize: Size of the character array. This value makes sure that the code + * doesn't write beyond it's bounds. + * + * @return The pointer to the string holding the dotted decimal format of the IP-address. If + * everything passes correctly, then the pointer being returned is the same as + * pcDestination, else a NULL is returned. + */ +const char * FreeRTOS_inet_ntop4( const void * pvSource, + char * pcDestination, + socklen_t uxSize ) +{ + uint32_t ulIPAddress; + void * pvCopyDest; + const char * pcReturn; + + if( uxSize < 16U ) + { + /* There must be space for "255.255.255.255". */ + pcReturn = NULL; + } + else + { + pvCopyDest = ( void * ) &ulIPAddress; + ( void ) memcpy( pvCopyDest, pvSource, sizeof( ulIPAddress ) ); + ( void ) FreeRTOS_inet_ntoa( ulIPAddress, pcDestination ); + pcReturn = pcDestination; + } + + return pcReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Convert an ASCII character to its corresponding hexadecimal value. + * Accepted characters are 0-9, a-f, and A-F. + * + * @param[in] cChar: The character to be converted. + * + * @return The hexadecimal value, between 0 and 15. + * When the character is not valid, socketINVALID_HEX_CHAR will be returned. + */ +static uint8_t ucASCIIToHex( char cChar ) +{ + char cValue = cChar; + uint8_t ucNew; + + if( ( cValue >= '0' ) && ( cValue <= '9' ) ) + { + cValue -= ( char ) '0'; + /* The value will be between 0 and 9. */ + ucNew = ( uint8_t ) cValue; + } + else if( ( cValue >= 'a' ) && ( cValue <= 'f' ) ) + { + cValue -= ( char ) 'a'; + ucNew = ( uint8_t ) cValue; + /* The value will be between 10 and 15. */ + ucNew += ( uint8_t ) 10; + } + else if( ( cValue >= 'A' ) && ( cValue <= 'F' ) ) + { + cValue -= ( char ) 'A'; + ucNew = ( uint8_t ) cValue; + /* The value will be between 10 and 15. */ + ucNew += ( uint8_t ) 10; + } + else + { + /* The character does not represent a valid hex number, return 255. */ + ucNew = ( uint8_t ) socketINVALID_HEX_CHAR; + } + + return ucNew; +} +/*-----------------------------------------------------------*/ + +/** + * @brief This function converts a 48-bit MAC address to a human readable string. + * + * @param[in] pucSource: A pointer to an array of 6 bytes. + * @param[out] pcTarget: A buffer that is 18 bytes long, it will contain the resulting string. + * @param[in] cTen: Either an 'A' or an 'a'. It determines whether the hex numbers will use + * capital or small letters. + * @param[in] cSeparator: The separator that should appear between the bytes, either ':' or '-'. + */ +void FreeRTOS_EUI48_ntop( const uint8_t * pucSource, + char * pcTarget, + char cTen, + char cSeparator ) +{ + size_t uxIndex; + size_t uxNibble; + size_t uxTarget = 0U; + + for( uxIndex = 0U; uxIndex < ipMAC_ADDRESS_LENGTH_BYTES; uxIndex++ ) + { + uint8_t ucByte = pucSource[ uxIndex ]; + + for( uxNibble = 0; uxNibble < 2U; uxNibble++ ) + { + uint8_t ucNibble; + char cResult; + + if( uxNibble == 0U ) + { + ucNibble = ucByte >> 4; + } + else + { + ucNibble = ucByte & 0x0FU; + } + + if( ucNibble <= 0x09U ) + { + cResult = '0'; + cResult = cResult + ucNibble; + } + else + { + cResult = cTen; /* Either 'a' or 'A' */ + cResult = cResult + ( ucNibble - 10U ); + } + + pcTarget[ uxTarget ] = cResult; + uxTarget++; + } + + if( uxIndex == ( ipMAC_ADDRESS_LENGTH_BYTES - 1U ) ) + { + pcTarget[ uxTarget ] = ( char ) 0; + uxTarget++; + } + else + { + pcTarget[ uxTarget ] = cSeparator; + uxTarget++; + } + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief This function converts a human readable string, representing an 48-bit MAC address, + * into a 6-byte address. Valid inputs are e.g. "62:48:5:83:A0:b2" and "0-12-34-fe-dc-ba". + * + * @param[in] pcSource: The null terminated string to be parsed. + * @param[out] pucTarget: A buffer that is 6 bytes long, it will contain the MAC address. + * + * @return pdTRUE in case the string got parsed correctly, otherwise pdFALSE. + */ +BaseType_t FreeRTOS_EUI48_pton( const char * pcSource, + uint8_t * pucTarget ) +{ + BaseType_t xResult = pdFALSE; + size_t uxByteNr = 0U; + size_t uxSourceIndex; + size_t uxNibbleCount = 0U; + size_t uxLength = strlen( pcSource ); + uint32_t uxSum = 0U; + uint8_t ucHex; + char cChar; + + /* Ignore the following line from branch coverage since the exits from this loop are + * covered by break statements. The loop is kept as is to future proof the code against + * any changes. LCOV_EXCL_BR_START */ + for( uxSourceIndex = 0U; uxSourceIndex <= uxLength; uxSourceIndex++ ) + /* LCOV_EXCL_BR_STOP */ + { + cChar = pcSource[ uxSourceIndex ]; + ucHex = ucASCIIToHex( cChar ); + + if( ucHex != socketINVALID_HEX_CHAR ) + { + /* A valid nibble was found. Shift it into the accumulator. */ + uxSum = uxSum << 4; + + if( uxSum > 0xffU ) + { + /* A hex value was too big. */ + break; + } + + uxSum |= ucHex; + uxNibbleCount++; + } + else + { + if( uxNibbleCount != 2U ) + { + /* Each number should have 2 nibbles. */ + break; + } + + pucTarget[ uxByteNr ] = ( uint8_t ) uxSum; + uxSum = 0U; + uxNibbleCount = 0U; + uxByteNr++; + + if( uxByteNr == ipMAC_ADDRESS_LENGTH_BYTES ) + { + xResult = pdTRUE; + break; + } + + if( ( cChar != ':' ) && ( cChar != '-' ) ) + { + /* Invalid character. */ + break; + } + } + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief This function converts the character string pcSource into a network address + * structure, then copies the network address structure to pvDestination. + * pvDestination is written in network byte order. + * + * @param[in] pcSource: The character string in holding the IP address. + * @param[out] pvDestination: The returned network address in 32-bit network-endian format. + * + * @return pdPASS if the translation was successful or else pdFAIL. + */ +BaseType_t FreeRTOS_inet_pton4( const char * pcSource, + void * pvDestination ) +{ + const uint32_t ulDecimalBase = 10U; + uint8_t ucOctet[ socketMAX_IP_ADDRESS_OCTETS ]; + uint32_t ulReturn = 0U, ulValue; + UBaseType_t uxOctetNumber; + BaseType_t xResult = pdPASS; + const char * pcIPAddress = pcSource; + const void * pvCopySource; + + ( void ) memset( pvDestination, 0, sizeof( ulReturn ) ); + + /* Translate "192.168.2.100" to a 32-bit number, network-endian. */ + for( uxOctetNumber = 0U; uxOctetNumber < socketMAX_IP_ADDRESS_OCTETS; uxOctetNumber++ ) + { + ulValue = 0U; + + if( pcIPAddress[ 0 ] == '0' ) + { + /* Test for the sequence "0[0-9]", which would make it an octal representation. */ + if( ( pcIPAddress[ 1 ] >= '0' ) && ( pcIPAddress[ 1 ] <= '9' ) ) + { + FreeRTOS_printf( ( "Octal representation of IP-addresses is not supported." ) ); + /* Don't support octal numbers. */ + xResult = pdFAIL; + break; + } + } + + while( ( *pcIPAddress >= '0' ) && ( *pcIPAddress <= '9' ) ) + { + BaseType_t xChar; + + /* Move previous read characters into the next decimal + * position. */ + ulValue *= ulDecimalBase; + + /* Add the binary value of the ascii character. */ + xChar = ( BaseType_t ) pcIPAddress[ 0 ]; + xChar = xChar - ( BaseType_t ) '0'; + ulValue += ( uint32_t ) xChar; + + /* Move to next character in the string. */ + pcIPAddress++; + } + + /* Check characters were read. */ + if( pcIPAddress == pcSource ) + { + xResult = pdFAIL; + } + + /* Check the value fits in an 8-bit number. */ + if( ulValue > 0xffU ) + { + xResult = pdFAIL; + } + else + { + ucOctet[ uxOctetNumber ] = ( uint8_t ) ulValue; + + /* Check the next character is as expected. */ + if( uxOctetNumber < ( socketMAX_IP_ADDRESS_OCTETS - 1U ) ) + { + if( *pcIPAddress != '.' ) + { + xResult = pdFAIL; + } + else + { + /* Move past the dot. */ + pcIPAddress++; + } + } + } + + if( xResult == pdFAIL ) + { + /* No point going on. */ + break; + } + } + + if( *pcIPAddress != ( char ) 0 ) + { + /* Expected the end of the string. */ + xResult = pdFAIL; + } + + if( uxOctetNumber != socketMAX_IP_ADDRESS_OCTETS ) + { + /* Didn't read enough octets. */ + xResult = pdFAIL; + } + + if( xResult == pdPASS ) + { + /* lint: ucOctet has been set because xResult == pdPASS. */ + ulReturn = FreeRTOS_inet_addr_quick( ucOctet[ 0 ], ucOctet[ 1 ], ucOctet[ 2 ], ucOctet[ 3 ] ); + } + else + { + ulReturn = 0U; + } + + if( xResult == pdPASS ) + { + pvCopySource = ( const void * ) &ulReturn; + ( void ) memcpy( pvDestination, pvCopySource, sizeof( ulReturn ) ); + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Convert the IP address from "w.x.y.z" (dotted decimal) format to the 32-bit format. + * + * @param[in] pcIPAddress: The character string pointer holding the IP-address in the "W.X.Y.Z" + * (dotted decimal) format. + * + * @return The 32-bit representation of IP(v4) address. + */ +uint32_t FreeRTOS_inet_addr( const char * pcIPAddress ) +{ + uint32_t ulReturn = 0U; + + /* inet_pton AF_INET target is a 4-byte 'struct in_addr'. */ + if( pdFAIL == FreeRTOS_inet_pton4( pcIPAddress, &( ulReturn ) ) ) + { + /* Return 0 if translation failed. */ + ulReturn = 0U; + } + + return ulReturn; +} +/*-----------------------------------------------------------*/ + + +/** + * @brief Function to get the local address and IP port of the given socket. + * + * @param[in] xSocket: Socket whose port is to be added to the pxAddress. + * @param[out] pxAddress: Structure in which the IP address and the port number + * is returned. + * + * @return Size of the freertos_sockaddr structure. + */ +size_t FreeRTOS_GetLocalAddress( ConstSocket_t xSocket, + struct freertos_sockaddr * pxAddress ) +{ + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + + /* IP address of local machine. */ + pxAddress->sin_addr = *ipLOCAL_IP_ADDRESS_POINTER; + + /* Local port on this machine. */ + pxAddress->sin_port = FreeRTOS_htons( pxSocket->usLocalPort ); + + return sizeof( *pxAddress ); +} + +/*-----------------------------------------------------------*/ + +/** + * @brief Wake up the user of the given socket through event-groups. + * + * @param[in] pxSocket: The socket whose user is to be woken up. + */ +void vSocketWakeUpUser( FreeRTOS_Socket_t * pxSocket ) +{ +/* _HT_ must work this out, now vSocketWakeUpUser will be called for any important + * event or transition */ + #if ( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 ) + { + if( pxSocket->pxUserSemaphore != NULL ) + { + ( void ) xSemaphoreGive( pxSocket->pxUserSemaphore ); + } + } + #endif /* ipconfigSOCKET_HAS_USER_SEMAPHORE */ + + #if ( ipconfigSOCKET_HAS_USER_WAKE_CALLBACK == 1 ) + { + if( pxSocket->pxUserWakeCallback != NULL ) + { + pxSocket->pxUserWakeCallback( pxSocket ); + } + } + #endif /* ipconfigSOCKET_HAS_USER_WAKE_CALLBACK */ + + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + if( pxSocket->pxSocketSet != NULL ) + { + EventBits_t xSelectBits = ( pxSocket->xEventBits >> SOCKET_EVENT_BIT_COUNT ) & ( ( EventBits_t ) eSELECT_ALL ); + + if( xSelectBits != 0U ) + { + pxSocket->xSocketBits |= xSelectBits; + ( void ) xEventGroupSetBits( pxSocket->pxSocketSet->xSelectGroup, xSelectBits ); + } + } + + pxSocket->xEventBits &= ( EventBits_t ) eSOCKET_ALL; + } + #endif /* ipconfigSUPPORT_SELECT_FUNCTION */ + + if( ( pxSocket->xEventGroup != NULL ) && ( pxSocket->xEventBits != 0U ) ) + { + ( void ) xEventGroupSetBits( pxSocket->xEventGroup, pxSocket->xEventBits ); + } + + pxSocket->xEventBits = 0U; +} + +/*-----------------------------------------------------------*/ + +#if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) + +/** + * @brief This define makes it possible for network interfaces to inspect + * UDP messages and see if there is any UDP socket bound to a given port + * number. This is probably only useful in systems with a minimum of + * RAM and when lots of anonymous broadcast messages come in. + * + * @param[in] usPortNr: the port number to look for. + * + * @return xFound if a socket with the port number is found. + */ + BaseType_t xPortHasUDPSocket( uint16_t usPortNr ) + { + BaseType_t xFound = pdFALSE; + + vTaskSuspendAll(); + { + if( ( pxListFindListItemWithValue( &xBoundUDPSocketsList, ( TickType_t ) usPortNr ) != NULL ) ) + { + xFound = pdTRUE; + } + } + ( void ) xTaskResumeAll(); + + return xFound; + } + +#endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ + +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Check if it makes any sense to wait for a connect event. + * + * @param[in] pxSocket: The socket trying to connect. + * + * @return It may return: -EINPROGRESS, -EAGAIN, or 0 for OK. + */ + static BaseType_t bMayConnect( FreeRTOS_Socket_t const * pxSocket ) + { + BaseType_t xResult; + + eIPTCPState_t eState = pxSocket->u.xTCP.eTCPState; + + switch( eState ) + { + case eCLOSED: + case eCLOSE_WAIT: + xResult = 0; + break; + + case eCONNECT_SYN: + xResult = -pdFREERTOS_ERRNO_EINPROGRESS; + break; + + case eTCP_LISTEN: + case eSYN_FIRST: + case eSYN_RECEIVED: + case eESTABLISHED: + case eFIN_WAIT_1: + case eFIN_WAIT_2: + case eCLOSING: + case eLAST_ACK: + case eTIME_WAIT: + default: + xResult = -pdFREERTOS_ERRNO_EAGAIN; + break; + } + + return xResult; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Called from #FreeRTOS_connect(): make some checks and if allowed, + * send a message to the IP-task to start connecting to a remote socket. + * + * @param[in] pxSocket: The socket attempting to connect to a remote port. + * @param[in] pxAddress: The address the socket is trying to connect to. + * + * @return 0 on successful checks or a negative error code. + */ + static BaseType_t prvTCPConnectStart( FreeRTOS_Socket_t * pxSocket, + struct freertos_sockaddr const * pxAddress ) + { + BaseType_t xResult = 0; + + if( pxAddress == NULL ) + { + /* NULL address passed to the function. Invalid value. */ + xResult = -pdFREERTOS_ERRNO_EINVAL; + } + else if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdFALSE ) == pdFALSE ) + { + /* Not a valid socket or wrong type */ + xResult = -pdFREERTOS_ERRNO_EBADF; + } + else if( FreeRTOS_issocketconnected( pxSocket ) > 0 ) + { + /* The socket is already connected. */ + xResult = -pdFREERTOS_ERRNO_EISCONN; + } + else if( !socketSOCKET_IS_BOUND( pxSocket ) ) + { + /* Bind the socket to the port that the client task will send from. + * Non-standard, so the error returned is that returned by bind(). */ + xResult = FreeRTOS_bind( pxSocket, NULL, 0U ); + } + else + { + /* The socket is valid, not yet connected, and already bound to a port number. */ + } + + if( xResult == 0 ) + { + /* Check if it makes any sense to wait for a connect event, this condition + * might change while sleeping, so it must be checked within each loop */ + xResult = bMayConnect( pxSocket ); /* -EINPROGRESS, -EAGAIN, or 0 for OK */ + + /* Start the connect procedure, kernel will start working on it */ + if( xResult == 0 ) + { + pxSocket->u.xTCP.bits.bConnPrepared = pdFALSE; + pxSocket->u.xTCP.ucRepCount = 0U; + + FreeRTOS_debug_printf( ( "FreeRTOS_connect: %u to %xip:%u\n", + pxSocket->usLocalPort, ( unsigned ) FreeRTOS_ntohl( pxAddress->sin_addr ), FreeRTOS_ntohs( pxAddress->sin_port ) ) ); + + /* Port on remote machine. */ + pxSocket->u.xTCP.usRemotePort = FreeRTOS_ntohs( pxAddress->sin_port ); + + /* IP address of remote machine. */ + pxSocket->u.xTCP.ulRemoteIP = FreeRTOS_ntohl( pxAddress->sin_addr ); + + /* (client) internal state: socket wants to send a connect. */ + vTCPStateChange( pxSocket, eCONNECT_SYN ); + + /* To start an active connect. */ + pxSocket->u.xTCP.usTimeout = 1U; + + if( xSendEventToIPTask( eTCPTimerEvent ) != pdPASS ) + { + xResult = -pdFREERTOS_ERRNO_ECANCELED; + } + } + } + + return xResult; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Connect to a remote port. + * + * @param[in] xClientSocket: The socket initiating the connection. + * @param[in] pxAddress: The address of the remote socket. + * @param[in] xAddressLength: This parameter is not used. It is kept in + * the function signature to adhere to the Berkeley + * sockets standard. + * + * @return 0 is returned on a successful connection, else a negative + * error code is returned. + */ + BaseType_t FreeRTOS_connect( Socket_t xClientSocket, + const struct freertos_sockaddr * pxAddress, + socklen_t xAddressLength ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xClientSocket; + TickType_t xRemainingTime; + BaseType_t xTimed = pdFALSE; + BaseType_t xResult = -pdFREERTOS_ERRNO_EINVAL; + TimeOut_t xTimeOut; + + ( void ) xAddressLength; + + xResult = prvTCPConnectStart( pxSocket, pxAddress ); + + if( xResult == 0 ) + { + /* And wait for the result */ + for( ; ; ) + { + EventBits_t uxEvents; + + if( xTimed == pdFALSE ) + { + /* Only in the first round, check for non-blocking */ + xRemainingTime = pxSocket->xReceiveBlockTime; + + if( xRemainingTime == ( TickType_t ) 0 ) + { + /* Not yet connected, correct state, non-blocking. */ + xResult = -pdFREERTOS_ERRNO_EWOULDBLOCK; + break; + } + + /* Don't get here a second time. */ + xTimed = pdTRUE; + + /* Fetch the current time */ + vTaskSetTimeOutState( &xTimeOut ); + } + + /* Did it get connected while sleeping ? */ + xResult = FreeRTOS_issocketconnected( pxSocket ); + + if( xResult > 0 ) + { + /* Socket now connected, return a zero */ + xResult = 0; + break; + } + + /* Is it allowed to sleep more? */ + if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) + { + xResult = -pdFREERTOS_ERRNO_ETIMEDOUT; + break; + } + + /* Go sleeping until we get any down-stream event */ + uxEvents = xEventGroupWaitBits( pxSocket->xEventGroup, + ( EventBits_t ) eSOCKET_CONNECT | ( EventBits_t ) eSOCKET_CLOSED, + pdTRUE /*xClearOnExit*/, + pdFALSE /*xWaitAllBits*/, + xRemainingTime ); + + if( ( uxEvents & eSOCKET_CLOSED ) != 0U ) + { + xResult = -pdFREERTOS_ERRNO_ENOTCONN; + FreeRTOS_debug_printf( ( "FreeRTOS_connect() stopped due to an error\n" ) ); + break; + } + } + } + + return xResult; + } + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Accept a connection on an listening socket. + * + * @param[in] xServerSocket: The socket in listening mode. + * @param[out] pxAddress: The address of the machine trying to connect to this node + * is returned in this pointer. + * @param[out] pxAddressLength: The length of the address of the remote machine. + * + * @return FreeRTOS_accept: can return a new connected socket if the server socket + * is in listen mode and receives a connection request. The new socket will + * be bound already to the same port number as the listening socket. + */ + Socket_t FreeRTOS_accept( Socket_t xServerSocket, + struct freertos_sockaddr * pxAddress, + socklen_t * pxAddressLength ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xServerSocket; + FreeRTOS_Socket_t * pxClientSocket = NULL; + TickType_t xRemainingTime; + BaseType_t xTimed = pdFALSE, xAsk = pdFALSE; + TimeOut_t xTimeOut; + IPStackEvent_t xAskEvent; + + if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) + { + /* Not a valid socket or wrong type */ + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + pxClientSocket = FREERTOS_INVALID_SOCKET; + } + else if( ( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) && + ( pxSocket->u.xTCP.eTCPState != eTCP_LISTEN ) ) + { + /* Parent socket is not in listening mode */ + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + pxClientSocket = FREERTOS_INVALID_SOCKET; + } + else + { + /* Loop will stop with breaks. */ + for( ; ; ) + { + /* Is there a new client? */ + vTaskSuspendAll(); + { + if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) + { + pxClientSocket = pxSocket->u.xTCP.pxPeerSocket; + } + else + { + pxClientSocket = pxSocket; + } + + if( pxClientSocket != NULL ) + { + pxSocket->u.xTCP.pxPeerSocket = NULL; + + /* Is it still not taken ? */ + if( pxClientSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) + { + pxClientSocket->u.xTCP.bits.bPassAccept = pdFALSE; + } + else + { + pxClientSocket = NULL; + } + } + } + ( void ) xTaskResumeAll(); + + if( pxClientSocket != NULL ) + { + if( pxAddress != NULL ) + { + /* IP address of remote machine. */ + pxAddress->sin_addr = FreeRTOS_ntohl( pxClientSocket->u.xTCP.ulRemoteIP ); + + /* Port on remote machine. */ + pxAddress->sin_port = FreeRTOS_ntohs( pxClientSocket->u.xTCP.usRemotePort ); + } + + if( pxAddressLength != NULL ) + { + *pxAddressLength = ( socklen_t ) sizeof( *pxAddress ); + } + + if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) + { + xAsk = pdTRUE; + } + } + + if( xAsk != pdFALSE ) + { + /* Ask to set an event in 'xEventGroup' as soon as a new + * client gets connected for this listening socket. */ + xAskEvent.eEventType = eTCPAcceptEvent; + xAskEvent.pvData = pxSocket; + ( void ) xSendEventStructToIPTask( &xAskEvent, portMAX_DELAY ); + } + + if( pxClientSocket != NULL ) + { + break; + } + + if( xTimed == pdFALSE ) + { + /* Only in the first round, check for non-blocking */ + xRemainingTime = pxSocket->xReceiveBlockTime; + + if( xRemainingTime == ( TickType_t ) 0 ) + { + break; + } + + /* Don't get here a second time */ + xTimed = pdTRUE; + + /* Fetch the current time */ + vTaskSetTimeOutState( &xTimeOut ); + } + + /* Has the timeout been reached? */ + if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) + { + break; + } + + /* Put the calling task to 'sleep' until a down-stream event is received. */ + ( void ) xEventGroupWaitBits( pxSocket->xEventGroup, + ( EventBits_t ) eSOCKET_ACCEPT, + pdTRUE /*xClearOnExit*/, + pdFALSE /*xWaitAllBits*/, + xRemainingTime ); + } + } + + return pxClientSocket; + } + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Read incoming data from a TCP socket. Only after the last + * byte has been read, a close error might be returned. + * + * @param[in] xSocket: The socket owning the connection. + * @param[out] pvBuffer: The buffer to store the incoming data in. + * @param[in] uxBufferLength: The length of the buffer so that the function + * does not do out of bound access. + * @param[in] xFlags: The flags for conveying preference. The values + * FREERTOS_MSG_DONTWAIT, FREERTOS_ZERO_COPY and/or + * FREERTOS_MSG_PEEK can be used. + * + * @return The number of bytes actually received and stored in the pvBuffer. + */ + BaseType_t FreeRTOS_recv( Socket_t xSocket, + void * pvBuffer, + size_t uxBufferLength, + BaseType_t xFlags ) + { + BaseType_t xByteCount; + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + TickType_t xRemainingTime; + BaseType_t xTimed = pdFALSE; + TimeOut_t xTimeOut; + EventBits_t xEventBits = ( EventBits_t ) 0; + + /* Check if the socket is valid, has type TCP and if it is bound to a + * port. */ + if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) + { + xByteCount = -pdFREERTOS_ERRNO_EINVAL; + } + else if( ( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_ZERO_COPY ) != 0U ) && + ( pvBuffer == NULL ) ) + { + /* In zero-copy mode, pvBuffer is a pointer to a pointer ( not NULL ). */ + xByteCount = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + if( pxSocket->u.xTCP.rxStream != NULL ) + { + xByteCount = ( BaseType_t ) uxStreamBufferGetSize( pxSocket->u.xTCP.rxStream ); + } + else + { + xByteCount = 0; + } + + while( xByteCount == 0 ) + { + eIPTCPState_t eState = pxSocket->u.xTCP.eTCPState; + + switch( eState ) + { + case eCLOSED: + case eCLOSE_WAIT: /* (server + client) waiting for a connection termination request from the local user. */ + case eCLOSING: /* (server + client) waiting for a connection termination request acknowledgement from the remote TCP. */ + + if( pxSocket->u.xTCP.bits.bMallocError != pdFALSE_UNSIGNED ) + { + /* The no-memory error has priority above the non-connected error. + * Both are fatal and will lead to closing the socket. */ + xByteCount = -pdFREERTOS_ERRNO_ENOMEM; + } + else + { + xByteCount = -pdFREERTOS_ERRNO_ENOTCONN; + } + + break; + + case eTCP_LISTEN: + case eCONNECT_SYN: + case eSYN_FIRST: + case eSYN_RECEIVED: + case eESTABLISHED: + case eFIN_WAIT_1: + case eFIN_WAIT_2: + case eLAST_ACK: + case eTIME_WAIT: + default: + /* Nothing. */ + break; + } + + if( xByteCount < 0 ) + { + break; + } + + if( xTimed == pdFALSE ) + { + /* Only in the first round, check for non-blocking. */ + xRemainingTime = pxSocket->xReceiveBlockTime; + + if( xRemainingTime == ( TickType_t ) 0 ) + { + #if ( ipconfigSUPPORT_SIGNALS != 0 ) + { + /* Just check for the interrupt flag. */ + xEventBits = xEventGroupWaitBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_INTR, + pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, socketDONT_BLOCK ); + } + #endif /* ipconfigSUPPORT_SIGNALS */ + break; + } + + if( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_MSG_DONTWAIT ) != 0U ) + { + break; + } + + /* Don't get here a second time. */ + xTimed = pdTRUE; + + /* Fetch the current time. */ + vTaskSetTimeOutState( &xTimeOut ); + } + + /* Has the timeout been reached? */ + if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) + { + break; + } + + /* Block until there is a down-stream event. */ + xEventBits = xEventGroupWaitBits( pxSocket->xEventGroup, + ( EventBits_t ) eSOCKET_RECEIVE | ( EventBits_t ) eSOCKET_CLOSED | ( EventBits_t ) eSOCKET_INTR, + pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime ); + #if ( ipconfigSUPPORT_SIGNALS != 0 ) + { + if( ( xEventBits & ( EventBits_t ) eSOCKET_INTR ) != 0U ) + { + break; + } + } + #else + { + ( void ) xEventBits; + } + #endif /* ipconfigSUPPORT_SIGNALS */ + + if( pxSocket->u.xTCP.rxStream != NULL ) + { + xByteCount = ( BaseType_t ) uxStreamBufferGetSize( pxSocket->u.xTCP.rxStream ); + } + else + { + xByteCount = 0; + } + } + + #if ( ipconfigSUPPORT_SIGNALS != 0 ) + if( ( xEventBits & ( EventBits_t ) eSOCKET_INTR ) != 0U ) + { + if( ( xEventBits & ( ( EventBits_t ) eSOCKET_RECEIVE | ( EventBits_t ) eSOCKET_CLOSED ) ) != 0U ) + { + /* Shouldn't have cleared other flags. */ + xEventBits &= ~( ( EventBits_t ) eSOCKET_INTR ); + ( void ) xEventGroupSetBits( pxSocket->xEventGroup, xEventBits ); + } + + xByteCount = -pdFREERTOS_ERRNO_EINTR; + } + else + #endif /* ipconfigSUPPORT_SIGNALS */ + + if( xByteCount > 0 ) + { + if( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_ZERO_COPY ) == 0U ) + { + BaseType_t xIsPeek = ( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_MSG_PEEK ) != 0U ) ? 1L : 0L; + + xByteCount = ( BaseType_t ) + uxStreamBufferGet( pxSocket->u.xTCP.rxStream, + 0U, + ( uint8_t * ) pvBuffer, + ( size_t ) uxBufferLength, + xIsPeek ); + + if( pxSocket->u.xTCP.bits.bLowWater != pdFALSE_UNSIGNED ) + { + /* We had reached the low-water mark, now see if the flag + * can be cleared */ + size_t uxFrontSpace = uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ); + + if( uxFrontSpace >= pxSocket->u.xTCP.uxEnoughSpace ) + { + pxSocket->u.xTCP.bits.bLowWater = pdFALSE; + pxSocket->u.xTCP.bits.bWinChange = pdTRUE; + pxSocket->u.xTCP.usTimeout = 1U; /* because bLowWater is cleared. */ + ( void ) xSendEventToIPTask( eTCPTimerEvent ); + } + } + } + else + { + /* Zero-copy reception of data: pvBuffer is a pointer to a pointer. */ + xByteCount = ( BaseType_t ) uxStreamBufferGetPtr( pxSocket->u.xTCP.rxStream, ( uint8_t ** ) pvBuffer ); + } + } + else + { + /* Nothing. */ + } + } /* prvValidSocket() */ + + return xByteCount; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Called from FreeRTOS_send(): some checks which will be done before + * sending a TCP packed. + * + * @param[in] pxSocket: The socket owning the connection. + * @param[in] uxDataLength: The length of the data to be sent. + * + * @return 0: representing OK, else a negative error code will be returned. + */ + static int32_t prvTCPSendCheck( FreeRTOS_Socket_t * pxSocket, + size_t uxDataLength ) + { + int32_t xResult = 1; + + /* Is this a socket of type TCP and is it already bound to a port number ? */ + if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) + { + xResult = -pdFREERTOS_ERRNO_EINVAL; + } + else if( pxSocket->u.xTCP.bits.bMallocError != pdFALSE_UNSIGNED ) + { + xResult = -pdFREERTOS_ERRNO_ENOMEM; + } + else if( ( pxSocket->u.xTCP.eTCPState == eCLOSED ) || + ( pxSocket->u.xTCP.eTCPState == eCLOSE_WAIT ) || + ( pxSocket->u.xTCP.eTCPState == eCLOSING ) ) + { + xResult = -pdFREERTOS_ERRNO_ENOTCONN; + } + else if( pxSocket->u.xTCP.bits.bFinSent != pdFALSE_UNSIGNED ) + { + /* This TCP connection is closing already, the FIN flag has been sent. + * Maybe it is still delivering or receiving data. + * Return OK in order not to get closed/deleted too quickly */ + xResult = 0; + } + else if( uxDataLength == 0U ) + { + /* send() is being called to send zero bytes */ + xResult = 0; + } + else if( pxSocket->u.xTCP.txStream == NULL ) + { + /* Create the outgoing stream only when it is needed */ + ( void ) prvTCPCreateStream( pxSocket, pdFALSE ); + + if( pxSocket->u.xTCP.txStream == NULL ) + { + xResult = -pdFREERTOS_ERRNO_ENOMEM; + } + } + else + { + /* Nothing. */ + } + + return xResult; + } + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Get a direct pointer to the circular transmit buffer. + * + * @param[in] xSocket: The socket owning the buffer. + * @param[in] pxLength: This will contain the number of bytes that may be written. + * + * @return Head of the circular transmit buffer if all checks pass. Or else, NULL + * is returned. + */ + uint8_t * FreeRTOS_get_tx_head( ConstSocket_t xSocket, + BaseType_t * pxLength ) + { + uint8_t * pucReturn = NULL; + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + StreamBuffer_t * pxBuffer = NULL; + + *pxLength = 0; + + /* Confirm that this is a TCP socket before dereferencing structure + * member pointers. */ + if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdFALSE ) == pdTRUE ) + { + pxBuffer = pxSocket->u.xTCP.txStream; + + if( pxBuffer != NULL ) + { + size_t uxSpace = uxStreamBufferGetSpace( pxBuffer ); + size_t uxRemain = pxBuffer->LENGTH - pxBuffer->uxHead; + + if( uxRemain <= uxSpace ) + { + *pxLength = ( BaseType_t ) uxRemain; + } + else + { + *pxLength = ( BaseType_t ) uxSpace; + } + + pucReturn = &( pxBuffer->ucArray[ pxBuffer->uxHead ] ); + } + } + + return pucReturn; + } +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Send data using a TCP socket. It is not necessary to have the socket + * connected already. Outgoing data will be stored and delivered as soon as + * the socket gets connected. + * + * @param[in] xSocket: The socket owning the connection. + * @param[in] pvBuffer: The buffer containing the data. The value of this pointer + * may be NULL in case zero-copy transmissions are used. + * It is used in combination with 'FreeRTOS_get_tx_head()'. + * @param[in] uxDataLength: The length of the data to be added. + * @param[in] xFlags: This parameter is not used. (zero or FREERTOS_MSG_DONTWAIT). + * + * @return The number of bytes actually sent. Zero when nothing could be sent + * or a negative error code in case an error occurred. + */ + BaseType_t FreeRTOS_send( Socket_t xSocket, + const void * pvBuffer, + size_t uxDataLength, + BaseType_t xFlags ) + { + BaseType_t xByteCount; + BaseType_t xBytesLeft; + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + TickType_t xRemainingTime; + BaseType_t xTimed = pdFALSE; + TimeOut_t xTimeOut; + BaseType_t xCloseAfterSend; + const uint8_t * pucSource = ( const uint8_t * ) pvBuffer; + + /* Prevent compiler warnings about unused parameters. The parameter + * may be used in future versions. */ + ( void ) xFlags; + + xByteCount = ( BaseType_t ) prvTCPSendCheck( pxSocket, uxDataLength ); + + if( xByteCount > 0 ) + { + /* xBytesLeft is number of bytes to send, will count to zero. */ + xBytesLeft = ( BaseType_t ) uxDataLength; + + /* xByteCount is number of bytes that can be sent now. */ + xByteCount = ( BaseType_t ) uxStreamBufferGetSpace( pxSocket->u.xTCP.txStream ); + + /* Try sending till there is a timeout or all bytes have been sent. */ + while( ipTRUE_BOOL ) + { + /* If txStream has space. */ + if( xByteCount > 0 ) + { + /* Don't send more than necessary. */ + if( xByteCount > xBytesLeft ) + { + xByteCount = xBytesLeft; + } + + /* Is the close-after-send flag set and is this really the + * last transmission? */ + if( ( pxSocket->u.xTCP.bits.bCloseAfterSend != pdFALSE_UNSIGNED ) && ( xByteCount == xBytesLeft ) ) + { + xCloseAfterSend = pdTRUE; + } + else + { + xCloseAfterSend = pdFALSE; + } + + /* The flag 'bCloseAfterSend' can be set before sending data + * using setsockopt() + * + * When the last data packet is being sent out, a FIN flag will + * be included to let the peer know that no more data is to be + * expected. The use of 'bCloseAfterSend' is not mandatory, it + * is just a faster way of transferring files (e.g. when using + * FTP). */ + if( xCloseAfterSend != pdFALSE ) + { + /* Now suspend the scheduler: sending the last data and + * setting bCloseRequested must be done together */ + vTaskSuspendAll(); + pxSocket->u.xTCP.bits.bCloseRequested = pdTRUE; + } + + xByteCount = ( BaseType_t ) uxStreamBufferAdd( pxSocket->u.xTCP.txStream, 0U, pucSource, ( size_t ) xByteCount ); + + if( xCloseAfterSend != pdFALSE ) + { + /* Now when the IP-task transmits the data, it will also + * see that bCloseRequested is true and include the FIN + * flag to start closure of the connection. */ + ( void ) xTaskResumeAll(); + } + + /* Send a message to the IP-task so it can work on this + * socket. Data is sent, let the IP-task work on it. */ + pxSocket->u.xTCP.usTimeout = 1U; + + if( xIsCallingFromIPTask() == pdFALSE ) + { + /* Only send a TCP timer event when not called from the + * IP-task. */ + ( void ) xSendEventToIPTask( eTCPTimerEvent ); + } + + xBytesLeft -= xByteCount; + + if( ( xBytesLeft == 0 ) || ( pvBuffer == NULL ) ) + { + /* pvBuffer can be NULL in case TCP zero-copy transmissions are used. */ + break; + } + + /* As there are still bytes left to be sent, increase the + * data pointer. */ + pucSource = &( pucSource[ xByteCount ] ); + } + + /* Not all bytes have been sent. In case the socket is marked as + * blocking sleep for a while. */ + if( xTimed == pdFALSE ) + { + /* Only in the first round, check for non-blocking. */ + xRemainingTime = pxSocket->xSendBlockTime; + + #if ( ipconfigUSE_CALLBACKS != 0 ) + { + if( xIsCallingFromIPTask() != pdFALSE ) + { + /* If this send function is called from within a + * call-back handler it may not block, otherwise + * chances would be big to get a deadlock: the IP-task + * waiting for itself. */ + xRemainingTime = ( TickType_t ) 0; + } + } + #endif /* ipconfigUSE_CALLBACKS */ + + if( xRemainingTime == ( TickType_t ) 0 ) + { + break; + } + + if( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_MSG_DONTWAIT ) != 0U ) + { + break; + } + + /* Don't get here a second time. */ + xTimed = pdTRUE; + + /* Fetch the current time. */ + vTaskSetTimeOutState( &xTimeOut ); + } + else + { + /* Has the timeout been reached? */ + if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) + { + break; + } + } + + /* Go sleeping until down-stream events are received. */ + ( void ) xEventGroupWaitBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_SEND | ( EventBits_t ) eSOCKET_CLOSED, + pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime ); + + xByteCount = ( BaseType_t ) uxStreamBufferGetSpace( pxSocket->u.xTCP.txStream ); + } + + /* How much was actually sent? */ + xByteCount = ( ( BaseType_t ) uxDataLength ) - xBytesLeft; + + if( xByteCount == 0 ) + { + if( pxSocket->u.xTCP.eTCPState > eESTABLISHED ) + { + xByteCount = ( BaseType_t ) -pdFREERTOS_ERRNO_ENOTCONN; + } + else + { + if( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) ) + { + FreeRTOS_debug_printf( ( "FreeRTOS_send: %u -> %xip:%d: no space\n", + pxSocket->usLocalPort, + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort ) ); + } + + xByteCount = ( BaseType_t ) -pdFREERTOS_ERRNO_ENOSPC; + } + } + } + + return xByteCount; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Request to put a socket in listen mode. + * + * @param[in] xSocket: the socket to be put in listening mode. + * @param[in] xBacklog: Maximum number of child sockets. + * + * @return 0 in case of success, or else a negative error code is + * returned. + */ + BaseType_t FreeRTOS_listen( Socket_t xSocket, + BaseType_t xBacklog ) + { + FreeRTOS_Socket_t * pxSocket; + BaseType_t xResult = 0; + + pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + + /* listen() is allowed for a valid TCP socket in Closed state and already + * bound. */ + if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) + { + xResult = -pdFREERTOS_ERRNO_EOPNOTSUPP; + } + else if( ( pxSocket->u.xTCP.eTCPState != eCLOSED ) && ( pxSocket->u.xTCP.eTCPState != eCLOSE_WAIT ) ) + { + /* Socket is in a wrong state. */ + xResult = -pdFREERTOS_ERRNO_EOPNOTSUPP; + } + else + { + /* Backlog is interpreted here as "the maximum number of child + * sockets. */ + pxSocket->u.xTCP.usBacklog = ( uint16_t ) FreeRTOS_min_int32( ( int32_t ) 0xffff, ( int32_t ) xBacklog ); + + /* This cleaning is necessary only if a listening socket is being + * reused as it might have had a previous connection. */ + if( pxSocket->u.xTCP.bits.bReuseSocket != pdFALSE_UNSIGNED ) + { + if( pxSocket->u.xTCP.rxStream != NULL ) + { + vStreamBufferClear( pxSocket->u.xTCP.rxStream ); + } + + if( pxSocket->u.xTCP.txStream != NULL ) + { + vStreamBufferClear( pxSocket->u.xTCP.txStream ); + } + + ( void ) memset( pxSocket->u.xTCP.xPacket.u.ucLastPacket, 0, sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) ); + ( void ) memset( &pxSocket->u.xTCP.xTCPWindow, 0, sizeof( pxSocket->u.xTCP.xTCPWindow ) ); + ( void ) memset( &pxSocket->u.xTCP.bits, 0, sizeof( pxSocket->u.xTCP.bits ) ); + + /* Now set the bReuseSocket flag again, because the bits have + * just been cleared. */ + pxSocket->u.xTCP.bits.bReuseSocket = pdTRUE; + } + + vTCPStateChange( pxSocket, eTCP_LISTEN ); + } + + return xResult; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Shutdown - This function will shut down the connection in both + * directions. However, it will first deliver all data queued for + * transmission, and also it will first wait to receive any missing + * packets from the peer. + * + * @param[in] xSocket: The socket owning the connection. + * @param[in] xHow: Not used. Just present to stick to Berkeley standard. + * + * @return 0 on successful shutdown or else a negative error code. + */ + BaseType_t FreeRTOS_shutdown( Socket_t xSocket, + BaseType_t xHow ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + BaseType_t xResult; + + if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) + { + /*_RB_ Is this comment correct? The socket is not of a type that + * supports the listen() operation. */ + xResult = -pdFREERTOS_ERRNO_EOPNOTSUPP; + } + else if( pxSocket->u.xTCP.eTCPState != eESTABLISHED ) + { + /* The socket is not connected. */ + xResult = -pdFREERTOS_ERRNO_ENOTCONN; + } + else + { + pxSocket->u.xTCP.bits.bUserShutdown = pdTRUE_UNSIGNED; + + /* Let the IP-task perform the shutdown of the connection. */ + pxSocket->u.xTCP.usTimeout = 1U; + ( void ) xSendEventToIPTask( eTCPTimerEvent ); + xResult = 0; + } + + ( void ) xHow; + + return xResult; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief A TCP timer has expired, now check all TCP sockets for: + * - Active connect + * - Send a delayed ACK + * - Send new data + * - Send a keep-alive packet + * - Check for timeout (in non-connected states only) + * + * @param[in] xWillSleep: Whether the calling task is going to sleep. + * + * @return Minimum amount of time before the timer shall expire. + */ + TickType_t xTCPTimerCheck( BaseType_t xWillSleep ) + { + FreeRTOS_Socket_t * pxSocket; + TickType_t xShortest = pdMS_TO_TICKS( ( TickType_t ) ipTCP_TIMER_PERIOD_MS ); + TickType_t xNow = xTaskGetTickCount(); + static TickType_t xLastTime = 0U; + TickType_t xDelta = xNow - xLastTime; + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( xBoundTCPSocketsList.xListEnd ) ); + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ListItem_t * pxIterator = ( const ListItem_t * ) listGET_HEAD_ENTRY( &xBoundTCPSocketsList ); + + xLastTime = xNow; + + if( xDelta == 0U ) + { + xDelta = 1U; + } + + while( pxIterator != pxEnd ) + { + pxSocket = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + pxIterator = ( ListItem_t * ) listGET_NEXT( pxIterator ); + + /* Sockets with 'timeout == 0' do not need any regular attention. */ + if( pxSocket->u.xTCP.usTimeout == 0U ) + { + continue; + } + + if( xDelta < ( TickType_t ) pxSocket->u.xTCP.usTimeout ) + { + pxSocket->u.xTCP.usTimeout = ( uint16_t ) ( ( ( TickType_t ) pxSocket->u.xTCP.usTimeout ) - xDelta ); + } + else + { + BaseType_t xRc; + + pxSocket->u.xTCP.usTimeout = 0U; + xRc = xTCPSocketCheck( pxSocket ); + + /* Within this function, the socket might want to send a delayed + * ack or send out data or whatever it needs to do. */ + if( xRc < 0 ) + { + /* Continue because the socket was deleted. */ + continue; + } + } + + /* In xEventBits the driver may indicate that the socket has + * important events for the user. These are only done just before the + * IP-task goes to sleep. */ + if( pxSocket->xEventBits != 0U ) + { + if( xWillSleep != pdFALSE ) + { + /* The IP-task is about to go to sleep, so messages can be + * sent to the socket owners. */ + vSocketWakeUpUser( pxSocket ); + } + else + { + /* Or else make sure this will be called again to wake-up + * the sockets' owner. */ + xShortest = ( TickType_t ) 0; + } + } + + if( ( pxSocket->u.xTCP.usTimeout != 0U ) && ( xShortest > ( TickType_t ) pxSocket->u.xTCP.usTimeout ) ) + { + xShortest = ( TickType_t ) pxSocket->u.xTCP.usTimeout; + } + } + + return xShortest; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief As multiple sockets may be bound to the same local port number + * looking up a socket is a little more complex: Both a local port, + * and a remote port and IP address are being used to find a match. + * For a socket in listening mode, the remote port and IP address + * are both 0. + * + * @param[in] ulLocalIP: Local IP address. Ignored for now. + * @param[in] uxLocalPort: Local port number. + * @param[in] ulRemoteIP: Remote (peer) IP address. + * @param[in] uxRemotePort: Remote (peer) port. + * + * @return The socket which was found. + */ + FreeRTOS_Socket_t * pxTCPSocketLookup( uint32_t ulLocalIP, + UBaseType_t uxLocalPort, + uint32_t ulRemoteIP, + UBaseType_t uxRemotePort ) + { + const ListItem_t * pxIterator; + FreeRTOS_Socket_t * pxResult = NULL, * pxListenSocket = NULL; + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( xBoundTCPSocketsList.xListEnd ) ); + + /* Parameter not yet supported. */ + ( void ) ulLocalIP; + + for( pxIterator = listGET_NEXT( pxEnd ); + pxIterator != pxEnd; + pxIterator = listGET_NEXT( pxIterator ) ) + { + FreeRTOS_Socket_t * pxSocket = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + + if( pxSocket->usLocalPort == ( uint16_t ) uxLocalPort ) + { + if( pxSocket->u.xTCP.eTCPState == eTCP_LISTEN ) + { + /* If this is a socket listening to uxLocalPort, remember it + * in case there is no perfect match. */ + pxListenSocket = pxSocket; + } + else if( ( pxSocket->u.xTCP.usRemotePort == ( uint16_t ) uxRemotePort ) && ( pxSocket->u.xTCP.ulRemoteIP == ulRemoteIP ) ) + { + /* For sockets not in listening mode, find a match with + * xLocalPort, ulRemoteIP AND xRemotePort. */ + pxResult = pxSocket; + break; + } + else + { + /* This 'pxSocket' doesn't match. */ + } + } + } + + if( pxResult == NULL ) + { + /* An exact match was not found, maybe a listening socket was + * found. */ + pxResult = pxListenSocket; + } + + return pxResult; + } + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief For the web server: borrow the circular Rx buffer for inspection. + * HTML driver wants to see if a sequence of 13/10/13/10 is available. + * + * @param[in] xSocket: The socket whose Rx stream is to be returned. + * + * @return The Rx stream of the socket if all checks pass, else NULL. + */ + const struct xSTREAM_BUFFER * FreeRTOS_get_rx_buf( ConstSocket_t xSocket ) + { + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + const struct xSTREAM_BUFFER * pxReturn = NULL; + + + /* Confirm that this is a TCP socket before dereferencing structure + * member pointers. */ + if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdFALSE ) == pdTRUE ) + { + pxReturn = pxSocket->u.xTCP.rxStream; + } + + return pxReturn; + } + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Create the stream buffer for the given socket. + * + * @param[in] pxSocket: the socket to create the stream for. + * @param[in] xIsInputStream: Is this input stream? pdTRUE/pdFALSE? + * + * @return The stream buffer. + */ + static StreamBuffer_t * prvTCPCreateStream( FreeRTOS_Socket_t * pxSocket, + BaseType_t xIsInputStream ) + { + StreamBuffer_t * pxBuffer; + size_t uxLength; + size_t uxSize; + + /* Now that a stream is created, the maximum size is fixed before + * creation, it could still be changed with setsockopt(). */ + if( xIsInputStream != pdFALSE ) + { + size_t uxLittlePerc = sock20_PERCENT; + size_t uxEnoughPerc = sock80_PERCENT; + size_t uxSegmentCount = pxSocket->u.xTCP.uxRxStreamSize / pxSocket->u.xTCP.usMSS; + static const struct Percent + { + size_t uxPercLittle, uxPercEnough; + } + xPercTable[] = + { + { 0U, 100U }, /* 1 segment. */ + { 50U, 100U }, /* 2 segments. */ + { 34U, 100U }, /* 3 segments. */ + { 25U, 100U }, /* 4 segments. */ + }; + + if( ( uxSegmentCount > 0U ) && + ( uxSegmentCount <= ARRAY_USIZE( xPercTable ) ) ) + { + uxLittlePerc = xPercTable[ uxSegmentCount - 1U ].uxPercLittle; + uxEnoughPerc = xPercTable[ uxSegmentCount - 1U ].uxPercEnough; + } + + uxLength = pxSocket->u.xTCP.uxRxStreamSize; + + if( pxSocket->u.xTCP.uxLittleSpace == 0U ) + { + pxSocket->u.xTCP.uxLittleSpace = ( uxLittlePerc * pxSocket->u.xTCP.uxRxStreamSize ) / sock100_PERCENT; + } + + if( pxSocket->u.xTCP.uxEnoughSpace == 0U ) + { + pxSocket->u.xTCP.uxEnoughSpace = ( uxEnoughPerc * pxSocket->u.xTCP.uxRxStreamSize ) / sock100_PERCENT; + } + } + else + { + uxLength = pxSocket->u.xTCP.uxTxStreamSize; + } + + /* Add an extra 4 (or 8) bytes. */ + uxLength += sizeof( size_t ); + + /* And make the length a multiple of sizeof( size_t ). */ + uxLength &= ~( sizeof( size_t ) - 1U ); + + uxSize = ( sizeof( *pxBuffer ) + uxLength ) - sizeof( pxBuffer->ucArray ); + + pxBuffer = ( ( StreamBuffer_t * ) pvPortMallocLarge( uxSize ) ); + + if( pxBuffer == NULL ) + { + FreeRTOS_debug_printf( ( "prvTCPCreateStream: malloc failed\n" ) ); + pxSocket->u.xTCP.bits.bMallocError = pdTRUE; + vTCPStateChange( pxSocket, eCLOSE_WAIT ); + } + else + { + /* Clear the markers of the stream */ + ( void ) memset( pxBuffer, 0, sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray ) ); + pxBuffer->LENGTH = ( size_t ) uxLength; + + if( xTCPWindowLoggingLevel != 0 ) + { + FreeRTOS_debug_printf( ( "prvTCPCreateStream: %cxStream created %u bytes (total %u)\n", ( xIsInputStream != 0 ) ? 'R' : 'T', ( unsigned ) uxLength, ( unsigned ) uxSize ) ); + } + + if( xIsInputStream != 0 ) + { + iptraceMEM_STATS_CREATE( tcpRX_STREAM_BUFFER, pxBuffer, uxSize ); + pxSocket->u.xTCP.rxStream = pxBuffer; + } + else + { + iptraceMEM_STATS_CREATE( tcpTX_STREAM_BUFFER, pxBuffer, uxSize ); + pxSocket->u.xTCP.txStream = pxBuffer; + } + } + + return pxBuffer; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Add data to the RxStream. When uxOffset > 0, data has come in out-of-order + * and will be put in front of the head so it can not be popped by the user. + * + * @param[in] pxSocket: The socket to whose RxStream data is to be added. + * @param[in] uxOffset: Offset of the packet. + * @param[in] pcData: The data to be added to the RxStream. + * @param[in] ulByteCount: Number of bytes in the data. + * + * @return The number of bytes actually added to the RxStream. Or else, a negative + * error code is returned. + */ + int32_t lTCPAddRxdata( FreeRTOS_Socket_t * pxSocket, + size_t uxOffset, + const uint8_t * pcData, + uint32_t ulByteCount ) + { + StreamBuffer_t * pxStream = pxSocket->u.xTCP.rxStream; + int32_t xResult = 0; + + #if ( ipconfigUSE_CALLBACKS == 1 ) + BaseType_t bHasHandler = ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleReceive ) ? pdTRUE : pdFALSE; + const uint8_t * pucBuffer = NULL; + #endif /* ipconfigUSE_CALLBACKS */ + + /* int32_t uxStreamBufferAdd( pxBuffer, uxOffset, pucData, aCount ) + * if( pucData != NULL ) copy data the the buffer + * if( pucData == NULL ) no copying, just advance rxHead + * if( uxOffset != 0 ) Just store data which has come out-of-order + * if( uxOffset == 0 ) Also advance rxHead */ + if( pxStream == NULL ) + { + pxStream = prvTCPCreateStream( pxSocket, pdTRUE ); + + if( pxStream == NULL ) + { + xResult = -1; + } + } + + if( xResult >= 0 ) + { + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + if( ( bHasHandler != pdFALSE ) && ( uxStreamBufferGetSize( pxStream ) == 0U ) && ( uxOffset == 0U ) && ( pcData != NULL ) ) + { + /* Data can be passed directly to the user */ + pucBuffer = pcData; + + pcData = NULL; + } + } + #endif /* ipconfigUSE_CALLBACKS */ + + xResult = ( int32_t ) uxStreamBufferAdd( pxStream, uxOffset, pcData, ( size_t ) ulByteCount ); + + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + if( xResult != ( int32_t ) ulByteCount ) + { + FreeRTOS_debug_printf( ( "lTCPAddRxdata: at %u: %d/%u bytes (tail %u head %u space %u front %u)\n", + ( unsigned int ) uxOffset, + ( int ) xResult, + ( unsigned int ) ulByteCount, + ( unsigned int ) pxStream->uxTail, + ( unsigned int ) pxStream->uxHead, + ( unsigned int ) uxStreamBufferFrontSpace( pxStream ), + ( unsigned int ) pxStream->uxFront ) ); + } + } + #endif /* ipconfigHAS_DEBUG_PRINTF */ + + if( uxOffset == 0U ) + { + /* Data is being added to rxStream at the head (offs = 0) */ + #if ( ipconfigUSE_CALLBACKS == 1 ) + if( bHasHandler != pdFALSE ) + { + /* The socket owner has installed an OnReceive handler. Pass the + * Rx data, without copying from the rxStream, to the user. */ + for( ; ; ) + { + uint8_t * ucReadPtr = NULL; + uint32_t ulCount; + + if( pucBuffer != NULL ) + { + ucReadPtr = ( uint8_t * ) pucBuffer; + ulCount = ulByteCount; + pucBuffer = NULL; + } + else + { + ulCount = ( uint32_t ) uxStreamBufferGetPtr( pxStream, &( ucReadPtr ) ); + } + + if( ulCount == 0U ) + { + break; + } + + ( void ) pxSocket->u.xTCP.pxHandleReceive( pxSocket, ucReadPtr, ( size_t ) ulCount ); + ( void ) uxStreamBufferGet( pxStream, 0U, NULL, ( size_t ) ulCount, pdFALSE ); + } + } + else + #endif /* ipconfigUSE_CALLBACKS */ + { + /* See if running out of space. */ + if( pxSocket->u.xTCP.bits.bLowWater == pdFALSE_UNSIGNED ) + { + size_t uxFrontSpace = uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ); + + if( uxFrontSpace <= pxSocket->u.xTCP.uxLittleSpace ) + { + pxSocket->u.xTCP.bits.bLowWater = pdTRUE; + pxSocket->u.xTCP.bits.bWinChange = pdTRUE; + + /* bLowWater was reached, send the changed window size. */ + pxSocket->u.xTCP.usTimeout = 1U; + ( void ) xSendEventToIPTask( eTCPTimerEvent ); + } + } + + /* New incoming data is available, wake up the user. User's + * semaphores will be set just before the IP-task goes asleep. */ + pxSocket->xEventBits |= ( EventBits_t ) eSOCKET_RECEIVE; + + #if ipconfigSUPPORT_SELECT_FUNCTION == 1 + { + if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_READ ) != 0U ) + { + pxSocket->xEventBits |= ( ( ( EventBits_t ) eSELECT_READ ) << SOCKET_EVENT_BIT_COUNT ); + } + } + #endif + } + } + } + + return xResult; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Function to get the remote IP-address and port number. + * + * @param[in] xSocket: Socket owning the connection. + * @param[out] pxAddress: The address pointer to which the address + * is to be added. + * + * @return The size of the address being returned. Or else a negative + * error code will be returned. + */ + BaseType_t FreeRTOS_GetRemoteAddress( ConstSocket_t xSocket, + struct freertos_sockaddr * pxAddress ) + { + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + BaseType_t xResult; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + xResult = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + /* BSD style sockets communicate IP and port addresses in network + * byte order. + * IP address of remote machine. */ + pxAddress->sin_addr = FreeRTOS_htonl( pxSocket->u.xTCP.ulRemoteIP ); + + /* Port on remote machine. */ + pxAddress->sin_port = FreeRTOS_htons( pxSocket->u.xTCP.usRemotePort ); + + xResult = ( BaseType_t ) sizeof( *pxAddress ); + } + + return xResult; + } + + +#endif /* ipconfigUSE_TCP */ + +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Check the number of bytes that may be added to txStream. + * + * @param[in] xSocket: The socket to be checked. + * + * @return the number of bytes that may be added to txStream or + * else a negative error code. + */ + BaseType_t FreeRTOS_maywrite( ConstSocket_t xSocket ) + { + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + BaseType_t xResult; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + xResult = -pdFREERTOS_ERRNO_EINVAL; + } + else if( pxSocket->u.xTCP.eTCPState != eESTABLISHED ) + { + if( ( pxSocket->u.xTCP.eTCPState < eCONNECT_SYN ) || ( pxSocket->u.xTCP.eTCPState > eESTABLISHED ) ) + { + xResult = -1; + } + else + { + xResult = 0; + } + } + else if( pxSocket->u.xTCP.txStream == NULL ) + { + xResult = ( BaseType_t ) pxSocket->u.xTCP.uxTxStreamSize; + } + else + { + xResult = ( BaseType_t ) uxStreamBufferGetSpace( pxSocket->u.xTCP.txStream ); + } + + return xResult; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Get the number of bytes that can be written in the Tx buffer + * of the given socket. + * + * @param[in] xSocket: the socket to be checked. + * + * @return The bytes that can be written. Or else an error code. + */ + BaseType_t FreeRTOS_tx_space( ConstSocket_t xSocket ) + { + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + BaseType_t xReturn; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + if( pxSocket->u.xTCP.txStream != NULL ) + { + xReturn = ( BaseType_t ) uxStreamBufferGetSpace( pxSocket->u.xTCP.txStream ); + } + else + { + xReturn = ( BaseType_t ) pxSocket->u.xTCP.uxTxStreamSize; + } + } + + return xReturn; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Returns the number of bytes stored in the Tx buffer. + * + * @param[in] xSocket: The socket to be checked. + * + * @return The number of bytes stored in the Tx buffer of the socket. + * Or an error code. + */ + BaseType_t FreeRTOS_tx_size( ConstSocket_t xSocket ) + { + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + BaseType_t xReturn; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + if( pxSocket->u.xTCP.txStream != NULL ) + { + xReturn = ( BaseType_t ) uxStreamBufferGetSize( pxSocket->u.xTCP.txStream ); + } + else + { + xReturn = 0; + } + } + + return xReturn; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Is the socket connected. + * + * @param[in] xSocket: The socket being checked. + * + * @return pdTRUE if TCP socket is connected. + */ + BaseType_t FreeRTOS_issocketconnected( ConstSocket_t xSocket ) + { + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + BaseType_t xReturn = pdFALSE; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + if( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) + { + if( pxSocket->u.xTCP.eTCPState < eCLOSE_WAIT ) + { + xReturn = pdTRUE; + } + } + } + + return xReturn; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Get the actual value of Maximum Segment Size ( MSS ) being used. + * + * @param[in] xSocket: The socket whose MSS is to be returned. + * + * @return the actual size of MSS being used or an error code. + */ + BaseType_t FreeRTOS_mss( ConstSocket_t xSocket ) + { + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + BaseType_t xReturn; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + /* usMSS is declared as uint16_t to save space. FreeRTOS_mss() + * will often be used in signed native-size expressions cast it to + * BaseType_t. */ + xReturn = ( BaseType_t ) ( pxSocket->u.xTCP.usMSS ); + } + + return xReturn; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Get the connection status. The values correspond to the members + * of the enum 'eIPTCPState_t'. + * + * @param[in] xSocket: Socket to get the connection status from. + * + * @return The connection status or an error code. + * + * @note For internal use only. + */ + BaseType_t FreeRTOS_connstatus( ConstSocket_t xSocket ) + { + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + BaseType_t xReturn; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else + { + /* Cast it to BaseType_t. */ + xReturn = ( BaseType_t ) ( pxSocket->u.xTCP.eTCPState ); + } + + return xReturn; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Returns the number of bytes which can be read from the RX stream buffer. + * + * @param[in] xSocket: the socket to get the number of bytes from. + * + * @return Returns the number of bytes which can be read. Or an error + * code is returned. + */ + BaseType_t FreeRTOS_rx_size( ConstSocket_t xSocket ) + { + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + BaseType_t xReturn; + + if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else if( pxSocket->u.xTCP.rxStream != NULL ) + { + xReturn = ( BaseType_t ) uxStreamBufferGetSize( pxSocket->u.xTCP.rxStream ); + } + else + { + xReturn = 0; + } + + return xReturn; + } + + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +/** + * @brief Check whether a given socket is valid or not. Validity is defined + * as the socket not being NULL and not being Invalid. + * @param[in] xSocket: The socket to be checked. + * @return pdTRUE if the socket is valid, else pdFALSE. + * + */ +BaseType_t xSocketValid( const ConstSocket_t xSocket ) +{ + BaseType_t xReturnValue = pdFALSE; + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + if( ( xSocket != FREERTOS_INVALID_SOCKET ) && ( xSocket != NULL ) ) + { + xReturnValue = pdTRUE; + } + + return xReturnValue; +} +/*-----------------------------------------------------------*/ + +#if 0 + +/** + * @brief Returns the number of packets that are stored in a UDP socket. + * + * @param[in] xSocket: the socket to get the number of bytes from. + * + * @return Returns the number of packets that are stored. Use FreeRTOS_recvfrom() + * to retrieve those packets. + */ + BaseType_t FreeRTOS_udp_rx_size( Socket_t xSocket ) + { + BaseType_t xReturn = 0; + const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket; + + if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_UDP ) + { + xReturn = ( BaseType_t ) listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ); + } + else + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + + return xReturn; + } +#endif /* 0 */ + +#if ( ipconfigUSE_TCP == 1 ) + +/** + * @brief Get the net status. The IP-task will print a summary of all sockets and + * their connections. + */ + void FreeRTOS_netstat( void ) + { + IPStackEvent_t xAskEvent; + + /* Ask the IP-task to call vTCPNetStat() + * to avoid accessing xBoundTCPSocketsList + */ + xAskEvent.eEventType = eTCPNetStat; + xAskEvent.pvData = ( void * ) NULL; + ( void ) xSendEventStructToIPTask( &xAskEvent, pdMS_TO_TICKS( 1000U ) ); + } + +#endif /* ipconfigUSE_TCP */ +/*-----------------------------------------------------------*/ + +#if ( ( ipconfigHAS_PRINTF != 0 ) && ( ipconfigUSE_TCP == 1 ) ) + +/** + * @brief Print a summary of all sockets and their connections. + */ + void vTCPNetStat( void ) + { + /* Show a simple listing of all created sockets and their connections */ + const ListItem_t * pxIterator; + BaseType_t count = 0; + size_t uxMinimum = uxGetMinimumFreeNetworkBuffers(); + size_t uxCurrent = uxGetNumberOfFreeNetworkBuffers(); + + if( !listLIST_IS_INITIALISED( &xBoundTCPSocketsList ) ) + { + FreeRTOS_printf( ( "PLUS-TCP not initialized\n" ) ); + } + else + { + const ListItem_t * pxEndTCP = listGET_END_MARKER( &xBoundTCPSocketsList ); + const ListItem_t * pxEndUDP = listGET_END_MARKER( &xBoundUDPSocketsList ); + FreeRTOS_printf( ( "Prot Port IP-Remote : Port R/T Status Alive tmout Child\n" ) ); + + for( pxIterator = listGET_HEAD_ENTRY( &xBoundTCPSocketsList ); + pxIterator != pxEndTCP; + pxIterator = listGET_NEXT( pxIterator ) ) + { + const FreeRTOS_Socket_t * pxSocket = ( ( const FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + #if ( ipconfigTCP_KEEP_ALIVE == 1 ) + TickType_t age = xTaskGetTickCount() - pxSocket->u.xTCP.xLastAliveTime; + #else + TickType_t age = 0U; + #endif + + char ucChildText[ 16 ] = ""; + + if( pxSocket->u.xTCP.eTCPState == eTCP_LISTEN ) + { + /* Using function "snprintf". */ + const int32_t copied_len = snprintf( ucChildText, sizeof( ucChildText ), " %d/%d", + pxSocket->u.xTCP.usChildCount, + pxSocket->u.xTCP.usBacklog ); + ( void ) copied_len; + /* These should never evaluate to false since the buffers are both shorter than 5-6 characters (<=65535) */ + configASSERT( copied_len >= 0 ); /* LCOV_EXCL_BR_LINE the 'taken' branch will never execute. See the above comment. */ + configASSERT( copied_len < ( int32_t ) sizeof( ucChildText ) ); /* LCOV_EXCL_BR_LINE the 'taken' branch will never execute. See the above comment. */ + } + + FreeRTOS_printf( ( "TCP %5u %-16xip:%5u %d/%d %-13.13s %6u %6u%s\n", + pxSocket->usLocalPort, /* Local port on this machine */ + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, /* IP address of remote machine */ + pxSocket->u.xTCP.usRemotePort, /* Port on remote machine */ + ( pxSocket->u.xTCP.rxStream != NULL ) ? 1 : 0, + ( pxSocket->u.xTCP.txStream != NULL ) ? 1 : 0, + FreeRTOS_GetTCPStateName( pxSocket->u.xTCP.eTCPState ), + ( unsigned ) ( ( age > 999999U ) ? 999999U : age ), /* Format 'age' for printing */ + pxSocket->u.xTCP.usTimeout, + ucChildText ) ); + count++; + } + + for( pxIterator = listGET_HEAD_ENTRY( &xBoundUDPSocketsList ); + pxIterator != pxEndUDP; + pxIterator = listGET_NEXT( pxIterator ) ) + { + /* Local port on this machine */ + FreeRTOS_printf( ( "UDP Port %5u\n", + FreeRTOS_ntohs( listGET_LIST_ITEM_VALUE( pxIterator ) ) ) ); + count++; + } + + FreeRTOS_printf( ( "FreeRTOS_netstat: %d sockets %u < %u < %u buffers free\n", + ( int ) count, + ( unsigned ) uxMinimum, + ( unsigned ) uxCurrent, + ( unsigned ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ) ); + } + } + +#endif /* ( ( ipconfigHAS_PRINTF != 0 ) && ( ipconfigUSE_TCP == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/** + * @brief This internal non-blocking function will check all sockets that belong + * to a select set. The events bits of each socket will be updated, and it + * will check if an ongoing select() call must be interrupted because of an + * event has occurred. + * + * @param[in] pxSocketSet: The socket-set which is to be waited on for change. + */ + void vSocketSelect( const SocketSelect_t * pxSocketSet ) + { + BaseType_t xRound; + EventBits_t xSocketBits, xBitsToClear; + + #if ipconfigUSE_TCP == 1 + BaseType_t xLastRound = 1; + #else + BaseType_t xLastRound = 0; + #endif + + /* These flags will be switched on after checking the socket status. */ + EventBits_t xGroupBits = 0; + + for( xRound = 0; xRound <= xLastRound; xRound++ ) + { + const ListItem_t * pxIterator; + const ListItem_t * pxEnd; + + if( xRound == 0 ) + { + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxEnd = ( ( const ListItem_t * ) &( xBoundUDPSocketsList.xListEnd ) ); + } + + #if ipconfigUSE_TCP == 1 + else + { + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxEnd = ( ( const ListItem_t * ) &( xBoundTCPSocketsList.xListEnd ) ); + } + #endif /* ipconfigUSE_TCP == 1 */ + + for( pxIterator = listGET_NEXT( pxEnd ); + pxIterator != pxEnd; + pxIterator = listGET_NEXT( pxIterator ) ) + { + FreeRTOS_Socket_t * pxSocket = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + + if( pxSocket->pxSocketSet != pxSocketSet ) + { + /* Socket does not belong to this select group. */ + continue; + } + + xSocketBits = 0; + + #if ( ipconfigUSE_TCP == 1 ) + if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) + { + /* Check if the socket has already been accepted by the + * owner. If not, it is useless to return it from a + * select(). */ + BaseType_t bAccepted = pdFALSE; + + if( pxSocket->u.xTCP.bits.bPassQueued == pdFALSE_UNSIGNED ) + { + if( pxSocket->u.xTCP.bits.bPassAccept == pdFALSE_UNSIGNED ) + { + bAccepted = pdTRUE; + } + } + + /* Is the set owner interested in READ events? */ + if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_READ ) != ( EventBits_t ) 0U ) + { + if( pxSocket->u.xTCP.eTCPState == eTCP_LISTEN ) + { + if( ( pxSocket->u.xTCP.pxPeerSocket != NULL ) && ( pxSocket->u.xTCP.pxPeerSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) ) + { + xSocketBits |= ( EventBits_t ) eSELECT_READ; + } + } + else if( ( pxSocket->u.xTCP.bits.bReuseSocket != pdFALSE_UNSIGNED ) && ( pxSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) ) + { + /* This socket has the re-use flag. After connecting it turns into + * a connected socket. Set the READ event, so that accept() will be called. */ + xSocketBits |= ( EventBits_t ) eSELECT_READ; + } + else if( ( bAccepted != 0 ) && ( FreeRTOS_recvcount( pxSocket ) > 0 ) ) + { + xSocketBits |= ( EventBits_t ) eSELECT_READ; + } + else + { + /* Nothing. */ + } + } + + /* Is the set owner interested in EXCEPTION events? */ + if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_EXCEPT ) != 0U ) + { + if( ( pxSocket->u.xTCP.eTCPState == eCLOSE_WAIT ) || ( pxSocket->u.xTCP.eTCPState == eCLOSED ) ) + { + xSocketBits |= ( EventBits_t ) eSELECT_EXCEPT; + } + } + + /* Is the set owner interested in WRITE events? */ + if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_WRITE ) != 0U ) + { + BaseType_t bMatch = pdFALSE; + + if( bAccepted != 0 ) + { + if( FreeRTOS_tx_space( pxSocket ) > 0 ) + { + bMatch = pdTRUE; + } + } + + if( bMatch == pdFALSE ) + { + if( ( pxSocket->u.xTCP.bits.bConnPrepared != pdFALSE_UNSIGNED ) && + ( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) && + ( pxSocket->u.xTCP.bits.bConnPassed == pdFALSE_UNSIGNED ) ) + { + pxSocket->u.xTCP.bits.bConnPassed = pdTRUE; + bMatch = pdTRUE; + } + } + + if( bMatch != pdFALSE ) + { + xSocketBits |= ( EventBits_t ) eSELECT_WRITE; + } + } + } + else + #endif /* ipconfigUSE_TCP == 1 */ + { + /* Select events for UDP are simpler. */ + if( ( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_READ ) != 0U ) && + ( listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) > 0U ) ) + { + xSocketBits |= ( EventBits_t ) eSELECT_READ; + } + + /* The WRITE and EXCEPT bits are not used for UDP */ + } /* if( pxSocket->ucProtocol == FREERTOS_IPPROTO_TCP ) */ + + /* Each socket keeps its own event flags, which are looked-up + * by FreeRTOS_FD_ISSSET() */ + pxSocket->xSocketBits = xSocketBits; + + /* The ORed value will be used to set the bits in the event + * group. */ + xGroupBits |= xSocketBits; + } /* for( pxIterator ... ) */ + } /* for( xRound = 0; xRound <= xLastRound; xRound++ ) */ + + xBitsToClear = xEventGroupGetBits( pxSocketSet->xSelectGroup ); + + /* Now set the necessary bits. */ + xBitsToClear = ( xBitsToClear & ~xGroupBits ) & ( ( EventBits_t ) eSELECT_ALL ); + + #if ( ipconfigSUPPORT_SIGNALS != 0 ) + { + /* Maybe the socketset was signalled, but don't + * clear the 'eSELECT_INTR' bit here, as it will be used + * and cleared in FreeRTOS_select(). */ + xBitsToClear &= ~( ( EventBits_t ) eSELECT_INTR ); + } + #endif /* ipconfigSUPPORT_SIGNALS */ + + if( xBitsToClear != 0U ) + { + ( void ) xEventGroupClearBits( pxSocketSet->xSelectGroup, xBitsToClear ); + } + + /* Now include eSELECT_CALL_IP to wakeup the caller. */ + ( void ) xEventGroupSetBits( pxSocketSet->xSelectGroup, xGroupBits | ( EventBits_t ) eSELECT_CALL_IP ); + } + + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_SIGNALS != 0 ) + +/** + * @brief Send a signal to the task which reads from this socket. + * The socket will receive an event of the type 'eSOCKET_INTR'. + * Any ongoing blocking API ( e.g. FreeRTOS_recv() ) will be terminated + * and return the value -pdFREERTOS_ERRNO_EINTR ( -4 ). + * + * @param[in] xSocket: The socket that will be signalled. + */ + BaseType_t FreeRTOS_SignalSocket( Socket_t xSocket ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + BaseType_t xReturn; + + if( pxSocket == NULL ) + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + else + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + if( ( pxSocket->pxSocketSet != NULL ) && ( pxSocket->pxSocketSet->xSelectGroup != NULL ) ) + { + ( void ) xEventGroupSetBits( pxSocket->pxSocketSet->xSelectGroup, ( EventBits_t ) eSELECT_INTR ); + xReturn = 0; + } + else + #endif /* ipconfigSUPPORT_SELECT_FUNCTION */ + if( pxSocket->xEventGroup != NULL ) + { + ( void ) xEventGroupSetBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_INTR ); + xReturn = 0; + } + else + { + xReturn = -pdFREERTOS_ERRNO_EINVAL; + } + + return xReturn; + } + +#endif /* ipconfigSUPPORT_SIGNALS */ +/*-----------------------------------------------------------*/ + +#if ( ipconfigSUPPORT_SIGNALS != 0 ) + +/** + * @brief The same as 'FreeRTOS_SignalSocket()', except that this function should + * be called from an ISR context. + * + * @param[in] xSocket: The socket that will be signalled. + * @param[in,out] pxHigherPriorityTaskWoken: will be set to non-zero in case a higher- + * priority task has become runnable. + */ + BaseType_t FreeRTOS_SignalSocketFromISR( Socket_t xSocket, + BaseType_t * pxHigherPriorityTaskWoken ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket; + BaseType_t xReturn; + IPStackEvent_t xEvent; + + configASSERT( pxSocket != NULL ); + configASSERT( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ); + configASSERT( pxSocket->xEventGroup != NULL ); + + xEvent.eEventType = eSocketSignalEvent; + xEvent.pvData = pxSocket; + + /* The IP-task will call FreeRTOS_SignalSocket for this socket. */ + xReturn = xQueueSendToBackFromISR( xNetworkEventQueue, &xEvent, pxHigherPriorityTaskWoken ); + + return xReturn; + } + +#endif /* ipconfigSUPPORT_SIGNALS */ +/*-----------------------------------------------------------*/ + +#if 0 + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + struct pollfd + { + Socket_t fd; /* file descriptor */ + EventBits_t events; /* requested events */ + EventBits_t revents; /* returned events */ + }; + + typedef BaseType_t nfds_t; + + BaseType_t poll( struct pollfd * fds, + nfds_t nfds, + BaseType_t timeout ); + BaseType_t poll( struct pollfd * fds, + nfds_t nfds, + BaseType_t timeout ) + { + BaseType_t index; + SocketSelect_t * pxSocketSet = NULL; + BaseType_t xReturn = 0; + + /* See which socket-sets have been created and bound to the sockets involved. */ + for( index = 0; index < nfds; index++ ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) fds[ index ].fd; + + if( pxSocket->pxSocketSet != NULL ) + { + if( pxSocketSet == NULL ) + { + /* Use this socket-set. */ + pxSocketSet = pxSocket->pxSocketSet; + xReturn = 1; + } + else if( pxSocketSet == pxSocket->pxSocketSet ) + { + /* Good: associated with the same socket-set. */ + } + else + { + /* More than one socket-set is found: can not do a select on 2 sets. */ + xReturn = -1; + break; + } + } + } + + if( xReturn == 0 ) + { + /* Create a new socket-set, and attach all sockets to it. */ + pxSocketSet = FreeRTOS_CreateSocketSet(); + + if( pxSocketSet != NULL ) + { + xReturn = 1; + } + else + { + xReturn = -2; + } + + /* Memory leak: when the last socket closes, there is no more reference to + * this socket-set. It should be marked as an automatic or anonymous socket-set, + * so when closing the last member, its memory will be freed. */ + } + + if( xReturn > 0 ) + { + /* Only one socket-set is found. Connect all sockets to this socket-set. */ + for( index = 0; index < nfds; index++ ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) fds[ index ].fd; + EventBits_t xEventBits = fds[ index ].events; + + FreeRTOS_FD_SET( pxSocket, pxSocketSet, xEventBits ); + FreeRTOS_FD_CLR( pxSocket, pxSocketSet, ( EventBits_t ) ~xEventBits ); + } + + /* And sleep until an event happens or a time-out. */ + xReturn = FreeRTOS_select( pxSocketSet, timeout ); + + /* Now set the return events, copying from the socked field 'xSocketBits'. */ + for( index = 0; index < nfds; index++ ) + { + FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) fds[ index ].fd; + + fds[ index ].revents = pxSocket->xSocketBits & ( ( EventBits_t ) eSELECT_ALL ); + } + } + else + { + /* -1: Sockets are connected to different socket sets. */ + /* -2: FreeRTOS_CreateSocketSet() failed. */ + } + + return xReturn; + } + + #endif /* ipconfigSUPPORT_SELECT_FUNCTION */ +#endif /* 0 */ diff --git a/FreeRTOS/source/FreeRTOS_Stream_Buffer.c b/FreeRTOS/source/FreeRTOS_Stream_Buffer.c new file mode 100644 index 0000000..6fadaeb --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_Stream_Buffer.c @@ -0,0 +1,436 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_Stream_Buffer.c + * @brief Provides the API for managing/creating the stream buffers in the FreeRTOS+TCP network stack. + */ + +/* Standard includes. */ +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" + + +/** + * @brief Get the space between lower and upper value provided to the function. + * @param[in] pxBuffer: The circular stream buffer. + * @param[in] uxLower: The lower value. + * @param[in] uxUpper: The upper value. + * @return The space between uxLower and uxUpper, which equals to the distance + * minus 1. + */ +size_t uxStreamBufferSpace( const StreamBuffer_t * pxBuffer, + const size_t uxLower, + const size_t uxUpper ) +{ + size_t uxCount; + + uxCount = pxBuffer->LENGTH + uxUpper - uxLower - 1U; + + if( uxCount >= pxBuffer->LENGTH ) + { + uxCount -= pxBuffer->LENGTH; + } + + return uxCount; +} + +/** + * @brief Get the distance between lower and upper value provided to the function. + * @param[in] pxBuffer: The circular stream buffer. + * @param[in] uxLower: The lower value. + * @param[in] uxUpper: The upper value. + * @return The distance between uxLower and uxUpper. + */ +size_t uxStreamBufferDistance( const StreamBuffer_t * pxBuffer, + const size_t uxLower, + const size_t uxUpper ) +{ + size_t uxCount; + + uxCount = pxBuffer->LENGTH + uxUpper - uxLower; + + if( uxCount >= pxBuffer->LENGTH ) + { + uxCount -= pxBuffer->LENGTH; + } + + return uxCount; +} + +/** + * @brief Get the number of items which can be added to the buffer at + * the head before reaching the tail. + * @param[in] pxBuffer: The circular stream buffer. + * @return The number of items which can still be added to uxHead + * before hitting on uxTail + */ +size_t uxStreamBufferGetSpace( const StreamBuffer_t * pxBuffer ) +{ + size_t uxHead = pxBuffer->uxHead; + size_t uxTail = pxBuffer->uxTail; + + return uxStreamBufferSpace( pxBuffer, uxHead, uxTail ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the distance between the pointer in free space and the tail. + * @param[in] pxBuffer: The circular stream buffer. + * @return Distance between uxFront and uxTail or the number of items + * which can still be added to uxFront, before hitting on uxTail. + */ +size_t uxStreamBufferFrontSpace( const StreamBuffer_t * pxBuffer ) +{ + size_t uxFront = pxBuffer->uxFront; + size_t uxTail = pxBuffer->uxTail; + + return uxStreamBufferSpace( pxBuffer, uxFront, uxTail ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the number of items which can be read from the tail before + * reaching the head. + * @param[in] pxBuffer: The circular stream buffer. + * @return The number of items which can be read from the tail before + * reaching the head. + */ +size_t uxStreamBufferGetSize( const StreamBuffer_t * pxBuffer ) +{ + size_t uxHead = pxBuffer->uxHead; + size_t uxTail = pxBuffer->uxTail; + + return uxStreamBufferDistance( pxBuffer, uxTail, uxHead ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the space between the mid pointer and the head in the stream + * buffer. + * @param[in] pxBuffer: The circular stream buffer. + * @return The space between the mid pointer and the head. + */ +size_t uxStreamBufferMidSpace( const StreamBuffer_t * pxBuffer ) +{ + size_t uxHead = pxBuffer->uxHead; + size_t uxMid = pxBuffer->uxMid; + + return uxStreamBufferDistance( pxBuffer, uxMid, uxHead ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Move Clear the stream buffer. + * @param[in] pxBuffer: The circular stream buffer. + */ +void vStreamBufferClear( StreamBuffer_t * pxBuffer ) +{ + /* Make the circular buffer empty */ + pxBuffer->uxHead = 0U; + pxBuffer->uxTail = 0U; + pxBuffer->uxFront = 0U; + pxBuffer->uxMid = 0U; +} + +/*-----------------------------------------------------------*/ + +/** + * @brief Move the mid pointer forward by given byte count + * @param[in] pxBuffer: The circular stream buffer. + * @param[in] uxCount: The byte count by which the mid pointer is to be moved. + */ +void vStreamBufferMoveMid( StreamBuffer_t * pxBuffer, + size_t uxCount ) +{ + /* Increment uxMid, but no further than uxHead */ + size_t uxSize = uxStreamBufferMidSpace( pxBuffer ); + size_t uxMid = pxBuffer->uxMid; + size_t uxMoveCount = uxCount; + + if( uxMoveCount > uxSize ) + { + uxMoveCount = uxSize; + } + + uxMid += uxMoveCount; + + if( uxMid >= pxBuffer->LENGTH ) + { + uxMid -= pxBuffer->LENGTH; + } + + pxBuffer->uxMid = uxMid; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Check whether the value in left is less than or equal to the + * value in right from the perspective of the circular stream + * buffer. + * @param[in] pxBuffer: The circular stream buffer. + * @param[in] uxLeft: The left pointer in the stream buffer. + * @param[in] uxRight: The right value pointer in the stream buffer. + * @return pdTRUE if uxLeft <= uxRight, else pdFALSE. + */ +BaseType_t xStreamBufferLessThenEqual( const StreamBuffer_t * pxBuffer, + const size_t uxLeft, + const size_t uxRight ) +{ + BaseType_t xReturn = pdFALSE; + size_t uxTail = pxBuffer->uxTail; + + /* Returns true if ( uxLeft <= uxRight ) */ + if( ( uxLeft - uxTail ) <= ( uxRight - uxTail ) ) + { + xReturn = pdTRUE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Get the pointer to data and the amount of data which can be read in one go. + * + * @param[in] pxBuffer: The circular stream buffer. + * @param[out] ppucData: Pointer to the data pointer which will point to the + * data which can be read. + * + * @return The number of bytes which can be read in one go (which might be less than + * actual number of available bytes since this is a circular buffer and tail + * can loop back to the start of the buffer). + */ +size_t uxStreamBufferGetPtr( StreamBuffer_t * pxBuffer, + uint8_t ** ppucData ) +{ + size_t uxNextTail = pxBuffer->uxTail; + size_t uxSize = uxStreamBufferGetSize( pxBuffer ); + + *ppucData = pxBuffer->ucArray + uxNextTail; + + return FreeRTOS_min_size_t( uxSize, pxBuffer->LENGTH - uxNextTail ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Adds data to a stream buffer. + * + * @param[in,out] pxBuffer: The buffer to which the bytes will be added. + * @param[in] uxOffset: If uxOffset > 0, data will be written at an offset from uxHead + * while uxHead will not be moved yet. + * @param[in,out] pucData: A pointer to the data to be added. If 'pucData' equals NULL, + * the function is called to advance the 'Head' only. + * @param[in] uxByteCount: The number of bytes to add. + * + * @return The number of bytes added to the buffer. + */ +size_t uxStreamBufferAdd( StreamBuffer_t * pxBuffer, + size_t uxOffset, + const uint8_t * pucData, + size_t uxByteCount ) +{ + size_t uxSpace, uxNextHead, uxFirst; + size_t uxCount = uxByteCount; + + uxSpace = uxStreamBufferGetSpace( pxBuffer ); + + /* If uxOffset > 0, items can be placed in front of uxHead */ + if( uxSpace > uxOffset ) + { + uxSpace -= uxOffset; + } + else + { + uxSpace = 0U; + } + + /* The number of bytes that can be written is the minimum of the number of + * bytes requested and the number available. */ + uxCount = FreeRTOS_min_size_t( uxSpace, uxCount ); + + if( uxCount != 0U ) + { + uxNextHead = pxBuffer->uxHead; + + if( uxOffset != 0U ) + { + /* ( uxOffset > 0 ) means: write in front if the uxHead marker */ + uxNextHead += uxOffset; + + if( uxNextHead >= pxBuffer->LENGTH ) + { + uxNextHead -= pxBuffer->LENGTH; + } + } + + if( pucData != NULL ) + { + /* Calculate the number of bytes that can be added in the first + * write - which may be less than the total number of bytes that need + * to be added if the buffer will wrap back to the beginning. */ + uxFirst = FreeRTOS_min_size_t( pxBuffer->LENGTH - uxNextHead, uxCount ); + + /* Write as many bytes as can be written in the first write. */ + ( void ) memcpy( &( pxBuffer->ucArray[ uxNextHead ] ), pucData, uxFirst ); + + /* If the number of bytes written was less than the number that + * could be written in the first write... */ + if( uxCount > uxFirst ) + { + /* ...then write the remaining bytes to the start of the + * buffer. */ + ( void ) memcpy( pxBuffer->ucArray, &( pucData[ uxFirst ] ), uxCount - uxFirst ); + } + } + + /* The below update to the stream buffer members must happen + * atomically. */ + vTaskSuspendAll(); + { + if( uxOffset == 0U ) + { + /* ( uxOffset == 0 ) means: write at uxHead position */ + uxNextHead += uxCount; + + if( uxNextHead >= pxBuffer->LENGTH ) + { + uxNextHead -= pxBuffer->LENGTH; + } + + pxBuffer->uxHead = uxNextHead; + } + + if( xStreamBufferLessThenEqual( pxBuffer, pxBuffer->uxFront, uxNextHead ) != pdFALSE ) + { + /* Advance the front pointer */ + pxBuffer->uxFront = uxNextHead; + } + } + ( void ) xTaskResumeAll(); + } + + return uxCount; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Read bytes from stream buffer. + * + * @param[in] pxBuffer: The buffer from which the bytes will be read. + * @param[in] uxOffset: can be used to read data located at a certain offset from 'lTail'. + * @param[in,out] pucData: If 'pucData' equals NULL, the function is called to advance 'lTail' only. + * @param[in] uxMaxCount: The number of bytes to read. + * @param[in] xPeek: if 'xPeek' is pdTRUE, or if 'uxOffset' is non-zero, the 'lTail' pointer will + * not be advanced. + * + * @return The count of the bytes read. + */ +size_t uxStreamBufferGet( StreamBuffer_t * pxBuffer, + size_t uxOffset, + uint8_t * pucData, + size_t uxMaxCount, + BaseType_t xPeek ) +{ + size_t uxSize, uxCount, uxFirst, uxNextTail; + + /* How much data is available? */ + uxSize = uxStreamBufferGetSize( pxBuffer ); + + if( uxSize > uxOffset ) + { + uxSize -= uxOffset; + } + else + { + uxSize = 0U; + } + + /* Use the minimum of the wanted bytes and the available bytes. */ + uxCount = FreeRTOS_min_size_t( uxSize, uxMaxCount ); + + if( uxCount > 0U ) + { + uxNextTail = pxBuffer->uxTail; + + if( uxOffset != 0U ) + { + uxNextTail += uxOffset; + + if( uxNextTail >= pxBuffer->LENGTH ) + { + uxNextTail -= pxBuffer->LENGTH; + } + } + + if( pucData != NULL ) + { + /* Calculate the number of bytes that can be read - which may be + * less than the number wanted if the data wraps around to the start of + * the buffer. */ + uxFirst = FreeRTOS_min_size_t( pxBuffer->LENGTH - uxNextTail, uxCount ); + + /* Obtain the number of bytes it is possible to obtain in the first + * read. */ + ( void ) memcpy( pucData, &( pxBuffer->ucArray[ uxNextTail ] ), uxFirst ); + + /* If the total number of wanted bytes is greater than the number + * that could be read in the first read... */ + if( uxCount > uxFirst ) + { + /*...then read the remaining bytes from the start of the buffer. */ + ( void ) memcpy( &( pucData[ uxFirst ] ), pxBuffer->ucArray, uxCount - uxFirst ); + } + } + + if( ( xPeek == pdFALSE ) && ( uxOffset == 0U ) ) + { + /* Move the tail pointer to effectively remove the data read from + * the buffer. */ + uxNextTail += uxCount; + + if( uxNextTail >= pxBuffer->LENGTH ) + { + uxNextTail -= pxBuffer->LENGTH; + } + + pxBuffer->uxTail = uxNextTail; + } + } + + return uxCount; +} diff --git a/FreeRTOS/source/FreeRTOS_TCP_IP.c b/FreeRTOS/source/FreeRTOS_TCP_IP.c new file mode 100644 index 0000000..1b9451d --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_TCP_IP.c @@ -0,0 +1,925 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_TCP_IP.c + * @brief Module which handles the TCP connections for FreeRTOS+TCP. + * It depends on FreeRTOS_TCP_WIN.c, which handles the TCP windowing + * schemes. + * + * Endianness: in this module all ports and IP addresses are stored in + * host byte-order, except fields in the IP-packets + */ + +/* Standard includes. */ +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_ARP.h" + +#include "FreeRTOS_TCP_Reception.h" +#include "FreeRTOS_TCP_Transmission.h" +#include "FreeRTOS_TCP_State_Handling.h" +#include "FreeRTOS_TCP_Utils.h" + + +/* Just make sure the contents doesn't get compiled if TCP is not enabled. */ +#if ipconfigUSE_TCP == 1 + + + +/** @brief When closing a socket an event is posted to the Network Event Queue. + * If the queue is full, then the event is not posted and the socket + * can be orphaned. To prevent this, the below variable is used to keep + * track of any socket which needs to be closed. This variable can be + * accessed by the IP task only. Thus, preventing any race condition. + */ + /* MISRA Ref 8.9.1 [File scoped variables] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */ + /* coverity[misra_c_2012_rule_8_9_violation] */ + static FreeRTOS_Socket_t * xSocketToClose = NULL; + +/** @brief When a connection is coming in on a reusable socket, and the + * SYN phase times out, the socket must be put back into eTCP_LISTEN + * mode, so it can accept a new connection again. + * This variable can be accessed by the IP task only. Thus, preventing any + * race condition. + */ + /* MISRA Ref 8.9.1 [File scoped variables] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */ + /* coverity[misra_c_2012_rule_8_9_violation] */ + static FreeRTOS_Socket_t * xSocketToListen = NULL; + +/* + * For anti-hang protection and TCP keep-alive messages. Called in two places: + * after receiving a packet and after a state change. The socket's alive timer + * may be reset. + */ + static void prvTCPTouchSocket( FreeRTOS_Socket_t * pxSocket ); + + +/* + * Calculate when this socket needs to be checked to do (re-)transmissions. + */ + static TickType_t prvTCPNextTimeout( FreeRTOS_Socket_t * pxSocket ); + + + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + +/* + * For logging and debugging: make a string showing the TCP flags. + */ + const char * prvTCPFlagMeaning( UBaseType_t xFlags ); + #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ + + +/*-----------------------------------------------------------*/ + + +/** @brief Close the socket another time. + * + * @param[in] pxSocket: The socket to be checked. + */ + /* coverity[single_use] */ + void vSocketCloseNextTime( FreeRTOS_Socket_t * pxSocket ) + { + if( ( xSocketToClose != NULL ) && ( xSocketToClose != pxSocket ) ) + { + ( void ) vSocketClose( xSocketToClose ); + } + + xSocketToClose = pxSocket; + } + /*-----------------------------------------------------------*/ + +/** @brief Postpone a call to FreeRTOS_listen() to avoid recursive calls. + * + * @param[in] pxSocket: The socket to be checked. + */ + /* coverity[single_use] */ + void vSocketListenNextTime( FreeRTOS_Socket_t * pxSocket ) + { + if( ( xSocketToListen != NULL ) && ( xSocketToListen != pxSocket ) ) + { + ( void ) FreeRTOS_listen( ( Socket_t ) xSocketToListen, xSocketToListen->u.xTCP.usBacklog ); + } + + xSocketToListen = pxSocket; + } + /*-----------------------------------------------------------*/ + +/** + * @brief As soon as a TCP socket timer expires, this function will be called + * (from xTCPTimerCheck). It can send a delayed ACK or new data. + * + * @param[in] pxSocket: socket to be checked. + * + * @return 0 on success, a negative error code on failure. A negative value will be + * returned in case the hang-protection has put the socket in a wait-close state. + * + * @note Sequence of calling (normally) : + * IP-Task: + * xTCPTimerCheck() // Check all sockets ( declared in FreeRTOS_Sockets.c ) + * xTCPSocketCheck() // Either send a delayed ACK or call prvTCPSendPacket() + * prvTCPSendPacket() // Either send a SYN or call prvTCPSendRepeated ( regular messages ) + * prvTCPSendRepeated() // Send at most 8 messages on a row + * prvTCPReturnPacket() // Prepare for returning + * xNetworkInterfaceOutput() // Sends data to the NIC ( declared in portable/NetworkInterface/xxx ) + */ + BaseType_t xTCPSocketCheck( FreeRTOS_Socket_t * pxSocket ) + { + BaseType_t xResult = 0; + BaseType_t xReady = pdFALSE; + + if( ( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) && ( pxSocket->u.xTCP.txStream != NULL ) ) + { + /* The API FreeRTOS_send() might have added data to the TX stream. Add + * this data to the windowing system so it can be transmitted. */ + prvTCPAddTxData( pxSocket ); + } + + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + if( pxSocket->u.xTCP.pxAckMessage != NULL ) + { + /* The first task of this regular socket check is to send-out delayed + * ACK's. */ + if( pxSocket->u.xTCP.bits.bUserShutdown == pdFALSE_UNSIGNED ) + { + /* Earlier data was received but not yet acknowledged. This + * function is called when the TCP timer for the socket expires, the + * ACK may be sent now. */ + if( pxSocket->u.xTCP.eTCPState != eCLOSED ) + { + if( ( xTCPWindowLoggingLevel > 1 ) && ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) ) + { + FreeRTOS_debug_printf( ( "Send[%u->%u] del ACK %u SEQ %u (len %u)\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + ( unsigned ) ( pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber ), + ( unsigned ) ( pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - pxSocket->u.xTCP.xTCPWindow.tx.ulFirstSequenceNumber ), + ( unsigned ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) ) ); + } + + prvTCPReturnPacket( pxSocket, pxSocket->u.xTCP.pxAckMessage, ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER, ipconfigZERO_COPY_TX_DRIVER ); + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* The ownership has been passed to the SEND routine, + * clear the pointer to it. */ + pxSocket->u.xTCP.pxAckMessage = NULL; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + } + + if( prvTCPNextTimeout( pxSocket ) > 1U ) + { + /* Tell the code below that this function is ready. */ + xReady = pdTRUE; + } + } + else + { + /* The user wants to perform an active shutdown(), skip sending + * the delayed ACK. The function prvTCPSendPacket() will send the + * FIN along with the ACK's. */ + } + + if( pxSocket->u.xTCP.pxAckMessage != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); + pxSocket->u.xTCP.pxAckMessage = NULL; + } + } + } + #endif /* ipconfigUSE_TCP_WIN */ + + if( xReady == pdFALSE ) + { + /* The second task of this regular socket check is sending out data. */ + if( ( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) || + ( pxSocket->u.xTCP.eTCPState == eCONNECT_SYN ) ) + { + ( void ) prvTCPSendPacket( pxSocket ); + } + + /* Set the time-out for the next wakeup for this socket. */ + ( void ) prvTCPNextTimeout( pxSocket ); + + #if ( ipconfigTCP_HANG_PROTECTION == 1 ) + { + /* In all (non-connected) states in which keep-alive messages can not be sent + * the anti-hang protocol will close sockets that are 'hanging'. */ + xResult = prvTCPStatusAgeCheck( pxSocket ); + } + #endif + } + + return xResult; + } + /*-----------------------------------------------------------*/ + +/** + * @brief 'Touch' the socket to keep it alive/updated. + * + * @param[in] pxSocket: The socket to be updated. + * + * @note This is used for anti-hanging protection and TCP keep-alive messages. + * Called in two places: after receiving a packet and after a state change. + * The socket's alive timer may be reset. + */ + static void prvTCPTouchSocket( FreeRTOS_Socket_t * pxSocket ) + { + #if ( ipconfigTCP_HANG_PROTECTION == 1 ) + { + pxSocket->u.xTCP.xLastActTime = xTaskGetTickCount(); + } + #endif + + #if ( ipconfigTCP_KEEP_ALIVE == 1 ) + { + pxSocket->u.xTCP.bits.bWaitKeepAlive = pdFALSE_UNSIGNED; + pxSocket->u.xTCP.bits.bSendKeepAlive = pdFALSE_UNSIGNED; + pxSocket->u.xTCP.ucKeepRepCount = 0U; + pxSocket->u.xTCP.xLastAliveTime = xTaskGetTickCount(); + } + #endif + + ( void ) pxSocket; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Changing to a new state. Centralised here to do specific actions such as + * resetting the alive timer, calling the user's OnConnect handler to notify + * that a socket has got (dis)connected, and setting bit to unblock a call to + * FreeRTOS_select(). + * + * @param[in] pxSocket: The socket whose state we are trying to change. + * @param[in] eTCPState: The state to which we want to change to. + */ + void vTCPStateChange( FreeRTOS_Socket_t * pxSocket, + enum eTCP_STATE eTCPState ) + { + FreeRTOS_Socket_t * xParent = pxSocket; + BaseType_t bBefore = tcpNOW_CONNECTED( ( BaseType_t ) pxSocket->u.xTCP.eTCPState ); /* Was it connected ? */ + BaseType_t bAfter = tcpNOW_CONNECTED( ( BaseType_t ) eTCPState ); /* Is it connected now ? */ + + BaseType_t xPreviousState = ( BaseType_t ) pxSocket->u.xTCP.eTCPState; + + #if ( ipconfigUSE_CALLBACKS == 1 ) + FreeRTOS_Socket_t * xConnected = NULL; + #endif + + if( ( ( xPreviousState == eCONNECT_SYN ) || + ( xPreviousState == eSYN_FIRST ) || + ( xPreviousState == eSYN_RECEIVED ) ) && + ( eTCPState == eCLOSE_WAIT ) ) + { + /* A socket was in the connecting phase but something + * went wrong and it should be closed. */ + FreeRTOS_debug_printf( ( "Move from %s to %s\n", + FreeRTOS_GetTCPStateName( xPreviousState ), + FreeRTOS_GetTCPStateName( eTCPState ) ) ); + + /* Set the flag to show that it was connected before and that the + * status has changed now. This will cause the control flow to go + * in the below if condition.*/ + bBefore = pdTRUE; + } + + /* Has the connected status changed? */ + if( bBefore != bAfter ) + { + /* if bPassQueued is true, this socket is an orphan until it gets connected. */ + if( pxSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED ) + { + /* Find it's parent if the reuse bit is not set. */ + if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) + { + xParent = pxSocket->u.xTCP.pxPeerSocket; + configASSERT( xParent != NULL ); + } + } + + /* Is the socket connected now ? */ + if( bAfter != pdFALSE ) + { + /* if bPassQueued is true, this socket is an orphan until it gets connected. */ + if( pxSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED ) + { + if( xParent != NULL ) + { + /* The child socket has got connected. See if the parent + * ( the listening socket ) should be signalled, or if a + * call-back must be made, in which case 'xConnected' will + * be set to the parent socket. */ + + if( xParent->u.xTCP.pxPeerSocket == NULL ) + { + xParent->u.xTCP.pxPeerSocket = pxSocket; + } + + xParent->xEventBits |= ( EventBits_t ) eSOCKET_ACCEPT; + + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + /* Library support FreeRTOS_select(). Receiving a new + * connection is being translated as a READ event. */ + if( ( xParent->xSelectBits & ( ( EventBits_t ) eSELECT_READ ) ) != 0U ) + { + xParent->xEventBits |= ( ( EventBits_t ) eSELECT_READ ) << SOCKET_EVENT_BIT_COUNT; + } + } + #endif + + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + if( ( ipconfigIS_VALID_PROG_ADDRESS( xParent->u.xTCP.pxHandleConnected ) ) && + ( xParent->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) ) + { + /* The listening socket does not become connected itself, in stead + * a child socket is created. + * Postpone a call the OnConnect event until the end of this function. */ + xConnected = xParent; + } + } + #endif + } + + /* Don't need to access the parent socket anymore, so the + * reference 'pxPeerSocket' may be cleared. */ + pxSocket->u.xTCP.pxPeerSocket = NULL; + pxSocket->u.xTCP.bits.bPassQueued = pdFALSE_UNSIGNED; + + /* When true, this socket may be returned in a call to accept(). */ + pxSocket->u.xTCP.bits.bPassAccept = pdTRUE_UNSIGNED; + } + else + { + /* An active connect() has succeeded. In this case there is no + * ( listening ) parent socket. Signal the now connected socket. */ + + pxSocket->xEventBits |= ( EventBits_t ) eSOCKET_CONNECT; + + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + if( ( pxSocket->xSelectBits & ( ( EventBits_t ) eSELECT_WRITE ) ) != 0U ) + { + pxSocket->xEventBits |= ( ( EventBits_t ) eSELECT_WRITE ) << SOCKET_EVENT_BIT_COUNT; + } + } + #endif + } + } + else /* bAfter == pdFALSE, connection is closed. */ + { + /* Notify/wake-up the socket-owner by setting the event bits. */ + xParent->xEventBits |= ( EventBits_t ) eSOCKET_CLOSED; + + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + if( ( xParent->xSelectBits & ( EventBits_t ) eSELECT_EXCEPT ) != 0U ) + { + xParent->xEventBits |= ( ( EventBits_t ) eSELECT_EXCEPT ) << SOCKET_EVENT_BIT_COUNT; + } + } + #endif + } + + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + if( ( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleConnected ) ) && ( xConnected == NULL ) ) + { + /* The 'connected' state has changed, call the user handler. */ + xConnected = pxSocket; + } + } + #endif /* ipconfigUSE_CALLBACKS */ + + if( prvTCPSocketIsActive( pxSocket->u.xTCP.eTCPState ) == 0 ) + { + /* Now the socket isn't in an active state anymore so it + * won't need further attention of the IP-task. + * Setting time-out to zero means that the socket won't get checked during + * timer events. */ + pxSocket->u.xTCP.usTimeout = 0U; + } + } + + if( ( eTCPState == eCLOSED ) || + ( eTCPState == eCLOSE_WAIT ) ) + { + /* Socket goes to status eCLOSED because of a RST. + * When nobody owns the socket yet, delete it. */ + if( ( pxSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED ) || + ( pxSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) ) + { + FreeRTOS_debug_printf( ( "vTCPStateChange: Closing socket\n" ) ); + + if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) + { + configASSERT( xIsCallingFromIPTask() != pdFALSE ); + vSocketCloseNextTime( pxSocket ); + } + } + } + + /* Fill in the new state. */ + pxSocket->u.xTCP.eTCPState = eTCPState; + + if( ( eTCPState == eCLOSE_WAIT ) && ( pxSocket->u.xTCP.bits.bReuseSocket == pdTRUE_UNSIGNED ) ) + { + switch( xPreviousState ) + { + case eSYN_FIRST: /* 3 (server) Just created, must ACK the SYN request */ + case eSYN_RECEIVED: /* 4 (server) waiting for a confirming connection request */ + FreeRTOS_debug_printf( ( "Restoring a reuse socket port %u\n", pxSocket->usLocalPort ) ); + + /* Go back into listening mode. Set the TCP status to 'eCLOSED', + * otherwise FreeRTOS_listen() will refuse the action. */ + pxSocket->u.xTCP.eTCPState = eCLOSED; + + /* vSocketListenNextTime() makes sure that FreeRTOS_listen() will be called + * before the IP-task handles any new message. */ + vSocketListenNextTime( pxSocket ); + break; + + default: + /* Nothing to do. */ + break; + } + } + + /* Touch the alive timers because moving to another state. */ + prvTCPTouchSocket( pxSocket ); + + #if ( ipconfigHAS_DEBUG_PRINTF == 1 ) + { + if( ( xTCPWindowLoggingLevel >= 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) ) ) + { + FreeRTOS_debug_printf( ( "Socket %u -> %xip:%u State %s->%s\n", + pxSocket->usLocalPort, + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort, + FreeRTOS_GetTCPStateName( ( UBaseType_t ) xPreviousState ), + FreeRTOS_GetTCPStateName( ( UBaseType_t ) eTCPState ) ) ); + } + } + #endif /* ipconfigHAS_DEBUG_PRINTF */ + + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + if( xConnected != NULL ) + { + /* The 'connected' state has changed, call the OnConnect handler of the parent. */ + xConnected->u.xTCP.pxHandleConnected( ( Socket_t ) xConnected, bAfter ); + } + } + #endif + + if( xParent != NULL ) + { + vSocketWakeUpUser( xParent ); + } + } + /*-----------------------------------------------------------*/ + + +/** + * @brief Calculate after how much time this socket needs to be checked again. + * + * @param[in] pxSocket: The socket to be checked. + * + * @return The number of clock ticks before the timer expires. + */ + static TickType_t prvTCPNextTimeout( FreeRTOS_Socket_t * pxSocket ) + { + TickType_t ulDelayMs = ( TickType_t ) tcpMAXIMUM_TCP_WAKEUP_TIME_MS; + + if( pxSocket->u.xTCP.eTCPState == eCONNECT_SYN ) + { + /* The socket is actively connecting to a peer. */ + if( pxSocket->u.xTCP.bits.bConnPrepared != pdFALSE_UNSIGNED ) + { + /* Ethernet address has been found, use progressive timeout for + * active connect(). */ + if( pxSocket->u.xTCP.ucRepCount < 3U ) + { + ulDelayMs = ( ( ( uint32_t ) 3000U ) << ( pxSocket->u.xTCP.ucRepCount - 1U ) ); + } + else + { + ulDelayMs = 11000U; + } + } + else + { + /* Still in the ARP phase: check every half second. */ + ulDelayMs = 500U; + } + + FreeRTOS_debug_printf( ( "Connect[%xip:%u]: next timeout %u: %u ms\n", + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, pxSocket->u.xTCP.usRemotePort, + pxSocket->u.xTCP.ucRepCount, ( unsigned ) ulDelayMs ) ); + pxSocket->u.xTCP.usTimeout = ( uint16_t ) ipMS_TO_MIN_TICKS( ulDelayMs ); + } + else if( pxSocket->u.xTCP.usTimeout == 0U ) + { + /* Let the sliding window mechanism decide what time-out is appropriate. */ + BaseType_t xResult = xTCPWindowTxHasData( &pxSocket->u.xTCP.xTCPWindow, pxSocket->u.xTCP.ulWindowSize, &ulDelayMs ); + + if( ulDelayMs == 0U ) + { + if( xResult != ( BaseType_t ) 0 ) + { + ulDelayMs = 1U; + } + else + { + ulDelayMs = tcpMAXIMUM_TCP_WAKEUP_TIME_MS; + } + } + else + { + /* ulDelayMs contains the time to wait before a re-transmission. */ + } + + pxSocket->u.xTCP.usTimeout = ( uint16_t ) ipMS_TO_MIN_TICKS( ulDelayMs ); /* LCOV_EXCL_BR_LINE ulDelayMs will not be smaller than 1 */ + } + else + { + /* field '.usTimeout' has already been set (by the + * keep-alive/delayed-ACK mechanism). */ + } + + /* Return the number of clock ticks before the timer expires. */ + return ( TickType_t ) pxSocket->u.xTCP.usTimeout; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Process the received TCP packet. + * + * @param[in] pxDescriptor: The descriptor in which the TCP packet is held. + * + * @return If the processing of the packet was successful, then pdPASS is returned + * or else pdFAIL. + * + * @note FreeRTOS_TCP_IP has only 2 public functions, this is the second one: + * xProcessReceivedTCPPacket() + * prvTCPHandleState() + * prvTCPPrepareSend() + * prvTCPReturnPacket() + * xNetworkInterfaceOutput() // Sends data to the NIC + * prvTCPSendRepeated() + * prvTCPReturnPacket() // Prepare for returning + * xNetworkInterfaceOutput() // Sends data to the NIC + */ + BaseType_t xProcessReceivedTCPPacket( NetworkBufferDescriptor_t * pxDescriptor ) + { + /* Function might modify the parameter. */ + NetworkBufferDescriptor_t * pxNetworkBuffer = pxDescriptor; + + configASSERT( pxNetworkBuffer != NULL ); + configASSERT( pxNetworkBuffer->pucEthernetBuffer != NULL ); + + /* Map the buffer onto a ProtocolHeaders_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ProtocolHeaders_t * pxProtocolHeaders = ( ( const ProtocolHeaders_t * ) + &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( pxNetworkBuffer ) ] ) ); + FreeRTOS_Socket_t * pxSocket; + uint16_t ucTCPFlags = pxProtocolHeaders->xTCPHeader.ucTCPFlags; + uint32_t ulLocalIP; + uint16_t usLocalPort = FreeRTOS_htons( pxProtocolHeaders->xTCPHeader.usDestinationPort ); + uint16_t usRemotePort = FreeRTOS_htons( pxProtocolHeaders->xTCPHeader.usSourcePort ); + uint32_t ulRemoteIP; + uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxProtocolHeaders->xTCPHeader.ulSequenceNumber ); + uint32_t ulAckNumber = FreeRTOS_ntohl( pxProtocolHeaders->xTCPHeader.ulAckNr ); + BaseType_t xResult = pdPASS; + + const IPHeader_t * pxIPHeader; + + /* Check for a minimum packet size. */ + if( pxNetworkBuffer->xDataLength < ( ipSIZE_OF_ETH_HEADER + xIPHeaderSize( pxNetworkBuffer ) + ipSIZE_OF_TCP_HEADER ) ) + { + xResult = pdFAIL; + } + else + { + /* Map the ethernet buffer onto the IPHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxIPHeader = ( ( const IPHeader_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER ] ) ); + ulLocalIP = FreeRTOS_htonl( pxIPHeader->ulDestinationIPAddress ); + ulRemoteIP = FreeRTOS_htonl( pxIPHeader->ulSourceIPAddress ); + + /* Find the destination socket, and if not found: return a socket listening to + * the destination PORT. */ + pxSocket = ( FreeRTOS_Socket_t * ) pxTCPSocketLookup( ulLocalIP, usLocalPort, ulRemoteIP, usRemotePort ); + + if( ( pxSocket == NULL ) || ( prvTCPSocketIsActive( pxSocket->u.xTCP.eTCPState ) == pdFALSE ) ) + { + /* A TCP messages is received but either there is no socket with the + * given port number or the there is a socket, but it is in one of these + * non-active states: eCLOSED, eCLOSE_WAIT, eFIN_WAIT_2, eCLOSING, or + * eTIME_WAIT. */ + + FreeRTOS_debug_printf( ( "TCP: No active socket on port %d (%xip:%d)\n", usLocalPort, ( unsigned ) ulRemoteIP, usRemotePort ) ); + + /* Send a RST to all packets that can not be handled. As a result + * the other party will get a ECONN error. There are two exceptions: + * 1) A packet that already has the RST flag set. + * 2) A packet that only has the ACK flag set. + * A packet with only the ACK flag set might be the last ACK in + * a three-way hand-shake that closes a connection. */ + if( ( ( ucTCPFlags & tcpTCP_FLAG_CTRL ) != tcpTCP_FLAG_ACK ) && + ( ( ucTCPFlags & tcpTCP_FLAG_RST ) == 0U ) ) + { + ( void ) prvTCPSendReset( pxNetworkBuffer ); + } + + /* The packet can't be handled. */ + xResult = pdFAIL; + } + else + { + pxSocket->u.xTCP.ucRepCount = 0U; + + if( pxSocket->u.xTCP.eTCPState == eTCP_LISTEN ) + { + /* The matching socket is in a listening state. Test if the peer + * has set the SYN flag. */ + if( ( ucTCPFlags & tcpTCP_FLAG_CTRL ) != tcpTCP_FLAG_SYN ) + { + /* What happens: maybe after a reboot, a client doesn't know the + * connection had gone. Send a RST in order to get a new connect + * request. */ + #if ( ipconfigHAS_DEBUG_PRINTF == 1 ) + { + FreeRTOS_debug_printf( ( "TCP: Server can't handle flags: %s from %xip:%u to port %u\n", + prvTCPFlagMeaning( ( UBaseType_t ) ucTCPFlags ), ( unsigned ) ulRemoteIP, usRemotePort, usLocalPort ) ); + } + #endif /* ipconfigHAS_DEBUG_PRINTF */ + + if( ( ucTCPFlags & tcpTCP_FLAG_RST ) == 0U ) + { + ( void ) prvTCPSendReset( pxNetworkBuffer ); + } + + xResult = pdFAIL; + } + else + { + /* prvHandleListen() will either return a newly created socket + * (if bReuseSocket is false), otherwise it returns the current + * socket which will later get connected. */ + pxSocket = prvHandleListen( pxSocket, pxNetworkBuffer ); + + if( pxSocket == NULL ) + { + xResult = pdFAIL; + } + } + } /* if( pxSocket->u.xTCP.eTCPState == eTCP_LISTEN ). */ + else + { + /* This is not a socket in listening mode. Check for the RST + * flag. */ + if( ( ucTCPFlags & tcpTCP_FLAG_RST ) != 0U ) + { + FreeRTOS_debug_printf( ( "TCP: RST received from %xip:%u for %u\n", ( unsigned ) ulRemoteIP, usRemotePort, usLocalPort ) ); + + /* Implement https://tools.ietf.org/html/rfc5961#section-3.2. */ + if( pxSocket->u.xTCP.eTCPState == eCONNECT_SYN ) + { + /* Per the above RFC, "In the SYN-SENT state ... the RST is + * acceptable if the ACK field acknowledges the SYN." */ + if( ulAckNumber == ( pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber + 1U ) ) + { + vTCPStateChange( pxSocket, eCLOSED ); + } + } + else + { + /* Check whether the packet matches the next expected sequence number. */ + if( ulSequenceNumber == pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber ) + { + vTCPStateChange( pxSocket, eCLOSED ); + } + /* Otherwise, check whether the packet is within the receive window. */ + else if( ( xSequenceGreaterThan( ulSequenceNumber, pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber ) != pdFALSE ) && + ( xSequenceLessThan( ulSequenceNumber, pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber + + pxSocket->u.xTCP.xTCPWindow.xSize.ulRxWindowLength ) != pdFALSE ) ) + { + /* Send a challenge ACK. */ + ( void ) prvTCPSendChallengeAck( pxNetworkBuffer ); + } + else + { + /* Nothing. */ + } + } + + /* Otherwise, do nothing. In any case, the packet cannot be handled. */ + xResult = pdFAIL; + } + /* Check whether there is a pure SYN amongst the TCP flags while the connection is established. */ + else if( ( ( ucTCPFlags & tcpTCP_FLAG_CTRL ) == tcpTCP_FLAG_SYN ) && ( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) ) + { + /* SYN flag while this socket is already connected. */ + FreeRTOS_debug_printf( ( "TCP: SYN unexpected from %xip:%u\n", ( unsigned ) ulRemoteIP, usRemotePort ) ); + + /* The packet cannot be handled. */ + xResult = pdFAIL; + } + else + { + /* Update the copy of the TCP header only (skipping eth and IP + * headers). It might be used later on, whenever data must be sent + * to the peer. */ + const size_t uxOffset = ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ); + ( void ) memcpy( ( void * ) ( &( pxSocket->u.xTCP.xPacket.u.ucLastPacket[ uxOffset ] ) ), + ( const void * ) ( &( pxNetworkBuffer->pucEthernetBuffer[ uxOffset ] ) ), + ipSIZE_OF_TCP_HEADER ); + /* Clear flags that are set by the peer, and set the ACK flag. */ + pxSocket->u.xTCP.xPacket.u.ucLastPacket[ uxOffset + ipTCP_FLAGS_OFFSET ] = tcpTCP_FLAG_ACK; + } + } + } + + if( xResult != pdFAIL ) + { + uint16_t usWindow; + + /* pxSocket is not NULL when xResult != pdFAIL. */ + configASSERT( pxSocket != NULL ); /* LCOV_EXCL_LINE ,this branch will not be hit*/ + + /* Touch the alive timers because we received a message for this + * socket. */ + prvTCPTouchSocket( pxSocket ); + + /* Parse the TCP option(s), if present. */ + + /* _HT_ : if we're in the SYN phase, and peer does not send a MSS option, + * then we MUST assume an MSS size of 536 bytes for backward compatibility. */ + + /* When there are no TCP options, the TCP offset equals 20 bytes, which is stored as + * the number 5 (words) in the higher nibble of the TCP-offset byte. */ + if( ( pxProtocolHeaders->xTCPHeader.ucTCPOffset & tcpTCP_OFFSET_LENGTH_BITS ) > tcpTCP_OFFSET_STANDARD_LENGTH ) + { + xResult = prvCheckOptions( pxSocket, pxNetworkBuffer ); + } + + if( xResult != pdFAIL ) + { + usWindow = FreeRTOS_ntohs( pxProtocolHeaders->xTCPHeader.usWindow ); + pxSocket->u.xTCP.ulWindowSize = ( uint32_t ) usWindow; + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + /* rfc1323 : The Window field in a SYN (i.e., a or ) + * segment itself is never scaled. */ + if( ( ucTCPFlags & ( uint8_t ) tcpTCP_FLAG_SYN ) == 0U ) + { + pxSocket->u.xTCP.ulWindowSize = + ( pxSocket->u.xTCP.ulWindowSize << pxSocket->u.xTCP.ucPeerWinScaleFactor ); + } + } + #endif /* ipconfigUSE_TCP_WIN */ + + /* In prvTCPHandleState() the incoming messages will be handled + * depending on the current state of the connection. */ + if( prvTCPHandleState( pxSocket, &pxNetworkBuffer ) > 0 ) + { + /* prvTCPHandleState() has sent a message, see if there are more to + * be transmitted. */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + ( void ) prvTCPSendRepeated( pxSocket, &pxNetworkBuffer ); + } + #endif /* ipconfigUSE_TCP_WIN */ + } + + if( pxNetworkBuffer != NULL ) + { + /* We must check if the buffer is unequal to NULL, because the + * socket might keep a reference to it in case a delayed ACK must be + * sent. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + #ifndef _lint + /* Clear pointers that are freed. */ + pxNetworkBuffer = NULL; + #endif + } + + /* And finally, calculate when this socket wants to be woken up. */ + ( void ) prvTCPNextTimeout( pxSocket ); + } + } + } + + /* pdPASS being returned means the buffer has been consumed. */ + return xResult; + } + /*-----------------------------------------------------------*/ + + +/** + * @brief In the API accept(), the user asks is there is a new client? As API's can + * not walk through the xBoundTCPSocketsList the IP-task will do this. + * + * @param[in] pxSocket: The socket for which the bound socket list will be iterated. + * + * @return if there is a new client, then pdTRUE is returned or else, pdFALSE. + */ + BaseType_t xTCPCheckNewClient( FreeRTOS_Socket_t * pxSocket ) + { + TickType_t uxLocalPort = ( TickType_t ) FreeRTOS_htons( pxSocket->usLocalPort ); + const ListItem_t * pxIterator; + FreeRTOS_Socket_t * pxFound; + BaseType_t xResult = pdFALSE; + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ListItem_t * pxEndTCP = ( ( const ListItem_t * ) &( xBoundTCPSocketsList.xListEnd ) ); + + /* Here xBoundTCPSocketsList can be accessed safely IP-task is the only one + * who has access. */ + for( pxIterator = ( const ListItem_t * ) listGET_HEAD_ENTRY( &xBoundTCPSocketsList ); + pxIterator != pxEndTCP; + pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) ) + { + if( listGET_LIST_ITEM_VALUE( pxIterator ) == ( configLIST_VOLATILE TickType_t ) uxLocalPort ) + { + pxFound = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + + if( ( pxFound->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) && ( pxFound->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) ) + { + pxSocket->u.xTCP.pxPeerSocket = pxFound; + FreeRTOS_debug_printf( ( "xTCPCheckNewClient[0]: client on port %u\n", pxSocket->usLocalPort ) ); + xResult = pdTRUE; + break; + } + } + } + + return xResult; + } + /*-----------------------------------------------------------*/ + + +#endif /* ipconfigUSE_TCP == 1 */ + +/* Provide access to private members for testing. */ +#ifdef FREERTOS_ENABLE_UNIT_TESTS + #include "freertos_tcp_test_access_tcp_define.h" +#endif + +/* Provide access to private members for verification. */ +#ifdef FREERTOS_TCP_ENABLE_VERIFICATION + #include "aws_freertos_tcp_verification_access_tcp_define.h" +#endif diff --git a/FreeRTOS/source/FreeRTOS_TCP_Reception.c b/FreeRTOS/source/FreeRTOS_TCP_Reception.c new file mode 100644 index 0000000..cc83d43 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_TCP_Reception.c @@ -0,0 +1,616 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_TCP_Reception.c + * @brief Module which processes the packet received from a socket for FreeRTOS+TCP. + * + * Endianness: in this module all ports and IP addresses are stored in + * host byte-order, except fields in the IP-packets + */ + +/* Standard includes. */ +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_TCP_Transmission.h" +#include "FreeRTOS_TCP_Reception.h" + +/* Just make sure the contents doesn't get compiled if TCP is not enabled. */ +#if ipconfigUSE_TCP == 1 + +/* + * Identify and deal with a single TCP header option, advancing the pointer to + * the header. This function returns pdTRUE or pdFALSE depending on whether the + * caller should continue to parse more header options or break the loop. + */ + static int32_t prvSingleStepTCPHeaderOptions( const uint8_t * const pucPtr, + size_t uxTotalLength, + FreeRTOS_Socket_t * const pxSocket, + BaseType_t xHasSYNFlag ); + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/* + * Skip past TCP header options when doing Selective ACK, until there are no + * more options left. + */ + static void prvReadSackOption( const uint8_t * const pucPtr, + size_t uxIndex, + FreeRTOS_Socket_t * const pxSocket ); + #endif /* ( ipconfigUSE_TCP_WIN == 1 ) */ + +/** + * @brief Parse the TCP option(s) received, if present. + * + * @param[in] pxSocket: The socket handling the connection. + * @param[in] pxNetworkBuffer: The network buffer containing the TCP + * packet. + * + * @return: If the options are well formed and processed successfully + * then pdPASS is returned; else a pdFAIL is returned. + * + * @note It has already been verified that: + * ((pxTCPHeader->ucTCPOffset & 0xf0) > 0x50), meaning that + * the TP header is longer than the usual 20 (5 x 4) bytes. + */ + BaseType_t prvCheckOptions( FreeRTOS_Socket_t * pxSocket, + const NetworkBufferDescriptor_t * pxNetworkBuffer ) + { + size_t uxTCPHeaderOffset = ipSIZE_OF_ETH_HEADER + xIPHeaderSize( pxNetworkBuffer ); + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * ) + &( pxNetworkBuffer->pucEthernetBuffer[ uxTCPHeaderOffset ] ) ); + const TCPHeader_t * pxTCPHeader; + const uint8_t * pucPtr; + BaseType_t xHasSYNFlag; + BaseType_t xReturn = pdPASS; + /* Offset in the network packet where the first option byte is stored. */ + size_t uxOptionOffset = uxTCPHeaderOffset + ( sizeof( TCPHeader_t ) - sizeof( pxTCPHeader->ucOptdata ) ); + size_t uxOptionsLength; + int32_t lResult; + uint8_t ucLength; + + pxTCPHeader = &( pxProtocolHeaders->xTCPHeader ); + + + /* A character pointer to iterate through the option data */ + pucPtr = pxTCPHeader->ucOptdata; + + if( pxTCPHeader->ucTCPOffset <= ( 5U << 4U ) ) + { + /* Avoid integer underflow in computation of ucLength. */ + } + else + { + ucLength = ( ( ( pxTCPHeader->ucTCPOffset >> 4U ) - 5U ) << 2U ); + uxOptionsLength = ( size_t ) ucLength; + + if( pxNetworkBuffer->xDataLength > uxOptionOffset ) + { + /* Validate options size calculation. */ + if( uxOptionsLength <= ( pxNetworkBuffer->xDataLength - uxOptionOffset ) ) + { + if( ( pxTCPHeader->ucTCPFlags & tcpTCP_FLAG_SYN ) != ( uint8_t ) 0U ) + { + xHasSYNFlag = pdTRUE; + } + else + { + xHasSYNFlag = pdFALSE; + } + + /* The length check is only necessary in case the option data are + * corrupted, we don't like to run into invalid memory and crash. */ + for( ; ; ) + { + if( uxOptionsLength == 0U ) + { + /* coverity[break_stmt] : Break statement terminating the loop */ + break; + } + + lResult = prvSingleStepTCPHeaderOptions( pucPtr, uxOptionsLength, pxSocket, xHasSYNFlag ); + + if( lResult < 0 ) + { + xReturn = pdFAIL; + break; + } + + if( lResult == 0 ) + { + break; + } + + uxOptionsLength -= ( size_t ) lResult; + pucPtr = &( pucPtr[ lResult ] ); + } + } + } + } + + return xReturn; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Identify and deal with a single TCP header option, advancing the pointer to + * the header. + * + * @param[in] pucPtr: Pointer to the TCP packet options. + * @param[in] uxTotalLength: Length of the TCP packet options. + * @param[in] pxSocket: Socket handling the connection. + * @param[in] xHasSYNFlag: Whether the header has SYN flag or not. + * + * @return This function returns index of the next option if the current option is + * successfully processed and it is not the end of options whereafter the caller + * should continue to process more options. + * If the options have ended, this function will return a zero whereafter the + * caller should stop parsing options and continue further processing. + * If the current option has erroneous value, then the function returns a + * negative value wherein the calling function should not process this packet any + * further and drop it. + */ + static int32_t prvSingleStepTCPHeaderOptions( const uint8_t * const pucPtr, + size_t uxTotalLength, + FreeRTOS_Socket_t * const pxSocket, + BaseType_t xHasSYNFlag ) + { + UBaseType_t uxNewMSS; + size_t uxRemainingOptionsBytes = uxTotalLength; + uint8_t ucLen; + int32_t lIndex = 0; + TCPWindow_t * pxTCPWindow = &( pxSocket->u.xTCP.xTCPWindow ); + BaseType_t xReturn = pdFALSE; + + if( pucPtr[ 0U ] == tcpTCP_OPT_END ) + { + /* End of options. */ + lIndex = 0; + } + else if( pucPtr[ 0U ] == tcpTCP_OPT_NOOP ) + { + /* NOP option, inserted to make the length a multiple of 4. */ + lIndex = 1; + } + else if( uxRemainingOptionsBytes < 2U ) + { + /* Any other well-formed option must be at least two bytes: the option + * type byte followed by a length byte. */ + lIndex = -1; + } + + #if ( ipconfigUSE_TCP_WIN != 0 ) + else if( pucPtr[ 0 ] == tcpTCP_OPT_WSOPT ) + { + /* The TCP Window Scale Option. */ + /* Confirm that the option fits in the remaining buffer space. */ + if( ( uxRemainingOptionsBytes < tcpTCP_OPT_WSOPT_LEN ) || ( pucPtr[ 1 ] != tcpTCP_OPT_WSOPT_LEN ) ) + { + lIndex = -1; + } + else + { + /* Option is only valid in SYN phase. */ + if( xHasSYNFlag != 0 ) + { + pxSocket->u.xTCP.ucPeerWinScaleFactor = pucPtr[ 2 ]; + pxSocket->u.xTCP.bits.bWinScaling = pdTRUE_UNSIGNED; + } + + lIndex = ( int32_t ) tcpTCP_OPT_WSOPT_LEN; + } + } + #endif /* ipconfigUSE_TCP_WIN */ + else if( pucPtr[ 0 ] == tcpTCP_OPT_MSS ) + { + /* Confirm that the option fits in the remaining buffer space. */ + if( ( uxRemainingOptionsBytes < tcpTCP_OPT_MSS_LEN ) || ( pucPtr[ 1 ] != tcpTCP_OPT_MSS_LEN ) ) + { + lIndex = -1; + } + else + { + /* An MSS option with the correct option length. FreeRTOS_htons() + * is not needed here because usChar2u16() already returns a host + * endian number. */ + uxNewMSS = usChar2u16( &( pucPtr[ 2 ] ) ); + + if( pxSocket->u.xTCP.usMSS != uxNewMSS ) + { + /* Perform a basic check on the the new MSS. */ + if( uxNewMSS == 0U ) + { + lIndex = -1; + + /* Return Condition found. */ + xReturn = pdTRUE; + } + else + { + FreeRTOS_debug_printf( ( "MSS change %u -> %u\n", pxSocket->u.xTCP.usMSS, ( unsigned ) uxNewMSS ) ); + } + } + + /* If a 'return' condition has not been found. */ + if( xReturn == pdFALSE ) + { + /* Restrict the minimum value of segment length to the ( Minimum IP MTU (576) - IP header(20) - TCP Header(20) ). + * See "RFC 791 section 3.1 Total Length" for more details. */ + if( uxNewMSS < tcpMINIMUM_SEGMENT_LENGTH ) + { + uxNewMSS = tcpMINIMUM_SEGMENT_LENGTH; + } + + if( pxSocket->u.xTCP.usMSS > uxNewMSS ) + { + /* our MSS was bigger than the MSS of the other party: adapt it. */ + pxSocket->u.xTCP.bits.bMssChange = pdTRUE_UNSIGNED; + + if( pxSocket->u.xTCP.usMSS > uxNewMSS ) + { + /* The peer advertises a smaller MSS than this socket was + * using. Use that as well. */ + FreeRTOS_debug_printf( ( "Change mss %d => %u\n", pxSocket->u.xTCP.usMSS, ( unsigned ) uxNewMSS ) ); + } + + pxTCPWindow->xSize.ulRxWindowLength = ( ( uint32_t ) uxNewMSS ) * ( pxTCPWindow->xSize.ulRxWindowLength / ( ( uint32_t ) uxNewMSS ) ); + pxTCPWindow->usMSSInit = ( uint16_t ) uxNewMSS; + pxTCPWindow->usMSS = ( uint16_t ) uxNewMSS; + pxSocket->u.xTCP.usMSS = ( uint16_t ) uxNewMSS; + } + + lIndex = ( int32_t ) tcpTCP_OPT_MSS_LEN; + } + } + } + else + { + /* All other options have a length field, so that we easily + * can skip past them. */ + ucLen = pucPtr[ 1 ]; + lIndex = 0; + + if( ( ucLen < ( uint8_t ) 2U ) || ( uxRemainingOptionsBytes < ( size_t ) ucLen ) ) + { + /* If the length field is too small or too big, the options are + * malformed, don't process them further. + */ + lIndex = -1; + } + else + { + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + /* Selective ACK: the peer has received a packet but it is missing + * earlier packets. At least this packet does not need retransmission + * anymore. ulTCPWindowTxSack( ) takes care of this administration. + */ + if( pucPtr[ 0U ] == tcpTCP_OPT_SACK_A ) + { + ucLen -= 2U; + lIndex += 2; + + while( ucLen >= ( uint8_t ) 8U ) + { + prvReadSackOption( pucPtr, ( size_t ) lIndex, pxSocket ); + lIndex += 8; + ucLen -= 8U; + } + + /* ucLen should be 0 by now. */ + } + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ + + lIndex += ( int32_t ) ucLen; + } + } + + #if ( ipconfigUSE_TCP_WIN == 0 ) + /* Avoid compiler warnings when TCP window is not used. */ + ( void ) xHasSYNFlag; + #endif + + return lIndex; + } + /*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Skip past TCP header options when doing Selective ACK, until there are no + * more options left. + * + * @param[in] pucPtr: Pointer to the TCP packet options. + * @param[in] uxIndex: Index of options in the TCP packet options. + * @param[in] pxSocket: Socket handling the TCP connection. + */ + static void prvReadSackOption( const uint8_t * const pucPtr, + size_t uxIndex, + FreeRTOS_Socket_t * const pxSocket ) + { + uint32_t ulFirst = ulChar2u32( &( pucPtr[ uxIndex ] ) ); + uint32_t ulLast = ulChar2u32( &( pucPtr[ uxIndex + 4U ] ) ); + uint32_t ulCount = ulTCPWindowTxSack( &( pxSocket->u.xTCP.xTCPWindow ), ulFirst, ulLast ); + + /* ulTCPWindowTxSack( ) returns the number of bytes which have been acked + * starting from the head position. Advance the tail pointer in txStream. + */ + if( ( pxSocket->u.xTCP.txStream != NULL ) && ( ulCount > 0U ) ) + { + /* Just advancing the tail index, 'ulCount' bytes have been confirmed. */ + ( void ) uxStreamBufferGet( pxSocket->u.xTCP.txStream, 0, NULL, ( size_t ) ulCount, pdFALSE ); + pxSocket->xEventBits |= ( EventBits_t ) eSOCKET_SEND; + + #if ipconfigSUPPORT_SELECT_FUNCTION == 1 + { + if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_WRITE ) != 0U ) + { + /* The field 'xEventBits' is used to store regular socket events + * (at most 8), as well as 'select events', which will be left-shifted. + */ + pxSocket->xEventBits |= ( ( EventBits_t ) eSELECT_WRITE ) << SOCKET_EVENT_BIT_COUNT; + } + } + #endif + + /* In case the socket owner has installed an OnSent handler, + * call it now. */ + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleSent ) ) + { + pxSocket->u.xTCP.pxHandleSent( pxSocket, ulCount ); + } + } + #endif /* ipconfigUSE_CALLBACKS == 1 */ + } + } + + #endif /* ( ipconfigUSE_TCP_WIN != 0 ) */ + /*-----------------------------------------------------------*/ + +/** + * @brief prvCheckRxData(): called from prvTCPHandleState(). The + * first thing that will be done is find the TCP payload data + * and check the length of this data. + * + * @param[in] pxNetworkBuffer: The network buffer holding the received data. + * @param[out] ppucRecvData: It will point to first byte of the TCP payload. + * + * @return Length of the received buffer. + */ + BaseType_t prvCheckRxData( const NetworkBufferDescriptor_t * pxNetworkBuffer, + uint8_t ** ppucRecvData ) + { + /* Map the ethernet buffer onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * ) + &( pxNetworkBuffer->pucEthernetBuffer[ ( size_t ) ipSIZE_OF_ETH_HEADER + xIPHeaderSize( pxNetworkBuffer ) ] ) ); + const TCPHeader_t * pxTCPHeader = &( pxProtocolHeaders->xTCPHeader ); + int32_t lLength, lTCPHeaderLength, lReceiveLength, lUrgentLength; + + /* Map the buffer onto an IPHeader_t struct for easy access to fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const IPHeader_t * pxIPHeader = ( ( const IPHeader_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER ] ) ); + const size_t xIPHeaderLength = ipSIZE_OF_IPv4_HEADER; + uint16_t usLength; + uint8_t ucIntermediateResult = 0; + + /* Determine the length and the offset of the user-data sent to this + * node. + * + * The size of the TCP header is given in a multiple of 4-byte words (single + * byte, needs no ntoh() translation). A shift-right 2: is the same as + * (offset >> 4) * 4. */ + ucIntermediateResult = ( pxTCPHeader->ucTCPOffset & tcpVALID_BITS_IN_TCP_OFFSET_BYTE ) >> 2; + lTCPHeaderLength = ( int32_t ) ucIntermediateResult; + + /* Let pucRecvData point to the first byte received. */ + *ppucRecvData = &( pxNetworkBuffer->pucEthernetBuffer[ ( size_t ) ipSIZE_OF_ETH_HEADER + xIPHeaderLength + ( size_t ) lTCPHeaderLength ] ); + + /* Calculate lReceiveLength - the length of the TCP data received. This is + * equal to the total packet length minus: + * ( LinkLayer length (14) + IP header length (20) + size of TCP header(20 +) ).*/ + lReceiveLength = ( int32_t ) pxNetworkBuffer->xDataLength; + lReceiveLength -= ( int32_t ) ipSIZE_OF_ETH_HEADER; + + usLength = FreeRTOS_htons( pxIPHeader->usLength ); + lLength = ( int32_t ) usLength; + + if( lReceiveLength > lLength ) + { + /* More bytes were received than the reported length, often because of + * padding bytes at the end. */ + lReceiveLength = lLength; + } + + /* Subtract the size of the TCP and IP headers and the actual data size is + * known. */ + if( lReceiveLength > ( lTCPHeaderLength + ( int32_t ) xIPHeaderLength ) ) + { + lReceiveLength -= ( lTCPHeaderLength + ( int32_t ) xIPHeaderLength ); + } + else + { + lReceiveLength = 0; + } + + /* Urgent Pointer: + * This field communicates the current value of the urgent pointer as a + * positive offset from the sequence number in this segment. The urgent + * pointer points to the sequence number of the octet following the urgent + * data. This field is only be interpreted in segments with the URG control + * bit set. */ + if( ( pxTCPHeader->ucTCPFlags & tcpTCP_FLAG_URG ) != 0U ) + { + /* Although we ignore the urgent data, we have to skip it. */ + lUrgentLength = ( int32_t ) FreeRTOS_htons( pxTCPHeader->usUrgent ); + *ppucRecvData += lUrgentLength; + lReceiveLength -= FreeRTOS_min_int32( lReceiveLength, lUrgentLength ); + } + + return ( BaseType_t ) lReceiveLength; + } + /*-----------------------------------------------------------*/ + +/** + * @brief prvStoreRxData(): called from prvTCPHandleState(). + * The second thing is to do is check if the payload data may + * be accepted. If so, they will be added to the reception queue. + * + * @param[in] pxSocket: The socket owning the connection. + * @param[in] pucRecvData: Pointer to received data. + * @param[in] pxNetworkBuffer: The network buffer descriptor. + * @param[in] ulReceiveLength: The length of the received data. + * + * @return 0 on success, -1 on failure of storing data. + */ + BaseType_t prvStoreRxData( FreeRTOS_Socket_t * pxSocket, + const uint8_t * pucRecvData, + NetworkBufferDescriptor_t * pxNetworkBuffer, + uint32_t ulReceiveLength ) + { + /* Map the ethernet buffer onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ProtocolHeaders_t * pxProtocolHeaders = ( ( const ProtocolHeaders_t * ) + &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( pxNetworkBuffer ) ] ) ); + const TCPHeader_t * pxTCPHeader = &pxProtocolHeaders->xTCPHeader; + TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; + uint32_t ulSequenceNumber, ulSpace; + int32_t lOffset, lStored; + BaseType_t xResult = 0; + uint32_t ulRxLength = ulReceiveLength; + const uint8_t * pucRxBuffer = &( pucRecvData[ 0 ] ); + + ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ); + + if( ( ulRxLength > 0U ) && ( pxSocket->u.xTCP.eTCPState >= eSYN_RECEIVED ) ) + { + uint32_t ulSkipCount = 0; + + /* See if way may accept the data contents and forward it to the socket + * owner. + * + * If it can't be "accept"ed it may have to be stored and send a selective + * ack (SACK) option to confirm it. In that case, lTCPAddRxdata() will be + * called later to store an out-of-order packet (in case lOffset is + * negative). */ + if( pxSocket->u.xTCP.rxStream != NULL ) + { + ulSpace = ( uint32_t ) uxStreamBufferGetSpace( pxSocket->u.xTCP.rxStream ); + } + else + { + ulSpace = ( uint32_t ) pxSocket->u.xTCP.uxRxStreamSize; + } + + lOffset = lTCPWindowRxCheck( pxTCPWindow, ulSequenceNumber, ulRxLength, ulSpace, &( ulSkipCount ) ); + + if( lOffset >= 0 ) + { + /* New data has arrived and may be made available to the user. See + * if the head marker in rxStream may be advanced, only if lOffset == 0. + * In case the low-water mark is reached, bLowWater will be set + * "low-water" here stands for "little space". */ + if( ulSkipCount != 0U ) + { + /* A packet was received that starts before 'ulCurrentSequenceNumber', + * and that ends after it. The first 'ulSkipCount' bytes shall be + * skipped. */ + ulRxLength -= ulSkipCount; + pucRxBuffer = &( pucRecvData[ ulSkipCount ] ); + } + + lStored = lTCPAddRxdata( pxSocket, ( uint32_t ) lOffset, pucRxBuffer, ulRxLength ); + + if( lStored != ( int32_t ) ulRxLength ) + { + FreeRTOS_debug_printf( ( "lTCPAddRxdata: stored %d / %u bytes? ?\n", ( int ) lStored, ( unsigned ) ulRxLength ) ); + + /* Received data could not be stored. The socket's flag + * bMallocError has been set. The socket now has the status + * eCLOSE_WAIT and a RST packet will be sent back. */ + ( void ) prvTCPSendReset( pxNetworkBuffer ); + xResult = -1; + } + } + + /* After a missing packet has come in, higher packets may be passed to + * the user. */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + /* Now lTCPAddRxdata() will move the rxHead pointer forward + * so data becomes available to the user immediately + * In case the low-water mark is reached, bLowWater will be set. */ + if( ( xResult == 0 ) && ( pxTCPWindow->ulUserDataLength > 0U ) ) + { + ( void ) lTCPAddRxdata( pxSocket, 0U, NULL, pxTCPWindow->ulUserDataLength ); + pxTCPWindow->ulUserDataLength = 0; + } + } + #endif /* ipconfigUSE_TCP_WIN */ + } + else + { + pxTCPWindow->ucOptionLength = 0U; + } + + return xResult; + } + /*-----------------------------------------------------------*/ + +#endif /* ipconfigUSE_TCP == 1 */ diff --git a/FreeRTOS/source/FreeRTOS_TCP_State_Handling.c b/FreeRTOS/source/FreeRTOS_TCP_State_Handling.c new file mode 100644 index 0000000..c1d2464 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_TCP_State_Handling.c @@ -0,0 +1,1171 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_TCP_State_Handling.c + * @brief Module which handles the TCP protocol state transition for FreeRTOS+TCP. + * + * Endianness: in this module all ports and IP addresses are stored in + * host byte-order, except fields in the IP-packets + */ + +/* Standard includes. */ +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_ARP.h" + +#include "FreeRTOS_TCP_Reception.h" +#include "FreeRTOS_TCP_Transmission.h" +#include "FreeRTOS_TCP_State_Handling.h" +#include "FreeRTOS_TCP_Utils.h" + +/* Just make sure the contents doesn't get compiled if TCP is not enabled. */ +#if ipconfigUSE_TCP == 1 + +/* + * Called to handle the closure of a TCP connection. + */ + static BaseType_t prvTCPHandleFin( FreeRTOS_Socket_t * pxSocket, + const NetworkBufferDescriptor_t * pxNetworkBuffer ); + +/* + * Called from prvTCPHandleState() as long as the TCP status is eSYN_RECEIVED to + * eCONNECT_SYN. + */ + static BaseType_t prvHandleSynReceived( FreeRTOS_Socket_t * pxSocket, + const NetworkBufferDescriptor_t * pxNetworkBuffer, + uint32_t ulReceiveLength, + UBaseType_t uxOptionsLength ); + +/* + * Called from prvTCPHandleState() as long as the TCP status is eESTABLISHED. + */ + static BaseType_t prvHandleEstablished( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer, + uint32_t ulReceiveLength, + UBaseType_t uxOptionsLength ); + +/* + * After a listening socket receives a new connection, it may duplicate itself. + * The copying takes place in prvTCPSocketCopy. + */ + static BaseType_t prvTCPSocketCopy( FreeRTOS_Socket_t * pxNewSocket, + FreeRTOS_Socket_t * pxSocket ); + +/** + * @brief Check whether the socket is active or not. + * + * @param[in] ucStatus: The status of the socket. + * + * @return pdTRUE if the socket must be checked. Non-active sockets + * are waiting for user action, either connect() or close(). + */ + BaseType_t prvTCPSocketIsActive( eIPTCPState_t eStatus ) + { + BaseType_t xResult; + + switch( eStatus ) + { + case eCLOSED: + case eCLOSE_WAIT: + case eFIN_WAIT_2: + case eCLOSING: + case eTIME_WAIT: + xResult = pdFALSE; + break; + + case eTCP_LISTEN: + case eCONNECT_SYN: + case eSYN_FIRST: + case eSYN_RECEIVED: + case eESTABLISHED: + case eFIN_WAIT_1: + case eLAST_ACK: + default: + xResult = pdTRUE; + break; + } + + return xResult; + } +/*-----------------------------------------------------------*/ + + + + #if ( ipconfigTCP_HANG_PROTECTION == 1 ) + +/** + * @brief Some of the TCP states may only last a certain amount of time. + * This function checks if the socket is 'hanging', i.e. staying + * too long in the same state. + * + * @param[in] The socket to be checked. + * + * @return pdFALSE if no checks are needed, pdTRUE if checks were done, or negative + * in case the socket has reached a critical time-out. The socket will go to + * the eCLOSE_WAIT state. + */ + BaseType_t prvTCPStatusAgeCheck( FreeRTOS_Socket_t * pxSocket ) + { + BaseType_t xResult; + + eIPTCPState_t eState = pxSocket->u.xTCP.eTCPState; + + switch( eState ) + { + case eESTABLISHED: + + /* If the 'ipconfigTCP_KEEP_ALIVE' option is enabled, sockets in + * state ESTABLISHED can be protected using keep-alive messages. */ + xResult = pdFALSE; + break; + + case eCLOSED: + case eTCP_LISTEN: + case eCLOSE_WAIT: + /* These 3 states may last for ever, up to the owner. */ + xResult = pdFALSE; + break; + + case eCONNECT_SYN: + case eSYN_FIRST: + case eSYN_RECEIVED: + case eFIN_WAIT_1: + case eFIN_WAIT_2: + case eCLOSING: + case eLAST_ACK: + case eTIME_WAIT: + default: + + /* All other (non-connected) states will get anti-hanging + * protection. */ + xResult = pdTRUE; + break; + } + + if( xResult != pdFALSE ) + { + /* How much time has past since the last active moment which is + * defined as A) a state change or B) a packet has arrived. */ + TickType_t xAge = xTaskGetTickCount() - pxSocket->u.xTCP.xLastActTime; + + /* ipconfigTCP_HANG_PROTECTION_TIME is in units of seconds. */ + if( xAge > ( ( TickType_t ) ipconfigTCP_HANG_PROTECTION_TIME * ( TickType_t ) configTICK_RATE_HZ ) ) + { + #if ( ipconfigHAS_DEBUG_PRINTF == 1 ) + { + FreeRTOS_debug_printf( ( "Inactive socket closed: port %u rem %xip:%u status %s\n", + pxSocket->usLocalPort, + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort, + FreeRTOS_GetTCPStateName( ( UBaseType_t ) pxSocket->u.xTCP.eTCPState ) ) ); + } + #endif /* ipconfigHAS_DEBUG_PRINTF */ + + /* Move to eCLOSE_WAIT, user may close the socket. */ + vTCPStateChange( pxSocket, eCLOSE_WAIT ); + + /* When 'bPassQueued' true, this socket is an orphan until it + * gets connected. */ + if( pxSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED ) + { + /* vTCPStateChange() has called vSocketCloseNextTime() + * in case the socket is not yet owned by the application. + * Return a negative value to inform the caller that + * the socket will be closed in the next cycle. */ + xResult = -1; + } + } + } + + return xResult; + } + /*-----------------------------------------------------------*/ + + #endif /* if ( ipconfigTCP_HANG_PROTECTION == 1 ) */ + +/** + * @brief prvTCPHandleFin() will be called to handle connection closure. The + * closure starts when either a FIN has been received and accepted, + * or when the socket has sent a FIN flag to the peer. Before being + * called, it has been checked that both reception and transmission + * are complete. + * + * @param[in] pxSocket: Socket owning the the connection. + * @param[in] pxNetworkBuffer: The network buffer carrying the TCP packet. + * + * @return Length of the packet to be sent. + */ + static BaseType_t prvTCPHandleFin( FreeRTOS_Socket_t * pxSocket, + const NetworkBufferDescriptor_t * pxNetworkBuffer ) + { + /* Map the ethernet buffer onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * ) + &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( pxNetworkBuffer ) ] ) ); + TCPHeader_t * pxTCPHeader = &( pxProtocolHeaders->xTCPHeader ); + uint8_t ucIntermediateResult = 0, ucTCPFlags = pxTCPHeader->ucTCPFlags; + TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; + BaseType_t xSendLength = 0; + uint32_t ulAckNr = FreeRTOS_ntohl( pxTCPHeader->ulAckNr ); + + if( ( ucTCPFlags & tcpTCP_FLAG_FIN ) != 0U ) + { + pxTCPWindow->rx.ulCurrentSequenceNumber = pxTCPWindow->rx.ulFINSequenceNumber + 1U; + } + + if( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED ) + { + /* We haven't yet replied with a FIN, do so now. */ + pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber; + pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED; + } + else + { + /* We did send a FIN already, see if it's ACK'd. */ + if( ulAckNr == ( pxTCPWindow->tx.ulFINSequenceNumber + 1U ) ) + { + pxSocket->u.xTCP.bits.bFinAcked = pdTRUE_UNSIGNED; + } + } + + if( pxSocket->u.xTCP.bits.bFinAcked == pdFALSE_UNSIGNED ) + { + pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->tx.ulFINSequenceNumber; + pxTCPHeader->ucTCPFlags = ( uint8_t ) tcpTCP_FLAG_ACK | ( uint8_t ) tcpTCP_FLAG_FIN; + + /* And wait for the final ACK. */ + vTCPStateChange( pxSocket, eLAST_ACK ); + } + else + { + /* Our FIN has been ACK'd, the outgoing sequence number is now fixed. */ + pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->tx.ulFINSequenceNumber + 1U; + + if( pxSocket->u.xTCP.bits.bFinRecv == pdFALSE_UNSIGNED ) + { + /* We have sent out a FIN but the peer hasn't replied with a FIN + * yet. Do nothing for the moment. */ + pxTCPHeader->ucTCPFlags = 0U; + } + else + { + if( pxSocket->u.xTCP.bits.bFinLast == pdFALSE_UNSIGNED ) + { + /* This is the third of the three-way hand shake: the last + * ACK. */ + pxTCPHeader->ucTCPFlags = tcpTCP_FLAG_ACK; + } + else + { + /* The other party started the closure, so we just wait for the + * last ACK. */ + pxTCPHeader->ucTCPFlags = 0U; + } + + /* And wait for the user to close this socket. */ + vTCPStateChange( pxSocket, eCLOSE_WAIT ); + } + } + + pxTCPWindow->ulOurSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber; + + if( pxTCPHeader->ucTCPFlags != 0U ) + { + ucIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + pxTCPWindow->ucOptionLength; + xSendLength = ( BaseType_t ) ucIntermediateResult; + } + + pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + pxTCPWindow->ucOptionLength ) << 2 ); + + if( xTCPWindowLoggingLevel != 0 ) + { + FreeRTOS_debug_printf( ( "TCP: send FIN+ACK (ack %u, cur/nxt %u/%u) ourSeqNr %u | Rx %u\n", + ( unsigned ) ( ulAckNr - pxTCPWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( pxTCPWindow->tx.ulCurrentSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( pxTCPWindow->ulNextTxSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ) ) ); + } + + return xSendLength; + } + /*-----------------------------------------------------------*/ + + +/** + * @brief prvHandleSynReceived(): called from prvTCPHandleState(). Called + * from the states: eSYN_RECEIVED and eCONNECT_SYN. If the flags + * received are correct, the socket will move to eESTABLISHED. + * + * @param[in] pxSocket: The socket handling the connection. + * @param[in] pxNetworkBuffer: The pointer to the network buffer carrying + * the packet. + * @param[in] ulReceiveLength: Length in bytes of the data received. + * @param[in] uxOptionsLength: Length of the TCP options in bytes. + * + * @return Length of the data to be sent. + */ + static BaseType_t prvHandleSynReceived( FreeRTOS_Socket_t * pxSocket, + const NetworkBufferDescriptor_t * pxNetworkBuffer, + uint32_t ulReceiveLength, + UBaseType_t uxOptionsLength ) + { + /* Map the ethernet buffer onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * ) + &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) ] ) ); + TCPHeader_t * pxTCPHeader = &pxProtocolHeaders->xTCPHeader; + TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; + uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags; + uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ); + BaseType_t xSendLength = 0; + UBaseType_t uxIntermediateResult = 0U; + + /* Either expect a ACK or a SYN+ACK. */ + uint8_t ucExpect = tcpTCP_FLAG_ACK; + const uint8_t ucFlagsMask = tcpTCP_FLAG_ACK | tcpTCP_FLAG_RST | tcpTCP_FLAG_SYN | tcpTCP_FLAG_FIN; + + if( pxSocket->u.xTCP.eTCPState == eCONNECT_SYN ) + { + ucExpect |= tcpTCP_FLAG_SYN; + } + + if( ( ucTCPFlags & ucFlagsMask ) != ucExpect ) + { + /* eSYN_RECEIVED: flags 0010 expected, not 0002. */ + /* eSYN_RECEIVED: flags ACK expected, not SYN. */ + FreeRTOS_debug_printf( ( "%s: flags %04X expected, not %04X\n", + ( pxSocket->u.xTCP.eTCPState == ( uint8_t ) eSYN_RECEIVED ) ? "eSYN_RECEIVED" : "eCONNECT_SYN", + ucExpect, ucTCPFlags ) ); + + /* In case pxSocket is not yet owned by the application, a closure + * of the socket will be scheduled for the next cycle. */ + vTCPStateChange( pxSocket, eCLOSE_WAIT ); + + /* Send RST with the expected sequence and ACK numbers, + * otherwise the packet will be ignored. */ + pxTCPWindow->ulOurSequenceNumber = FreeRTOS_htonl( pxTCPHeader->ulAckNr ); + pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber; + + pxTCPHeader->ucTCPFlags |= tcpTCP_FLAG_RST; + + uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength; + xSendLength = ( BaseType_t ) uxIntermediateResult; + + pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + } + else + { + pxTCPWindow->usPeerPortNumber = pxSocket->u.xTCP.usRemotePort; + pxTCPWindow->usOurPortNumber = pxSocket->usLocalPort; + + if( pxSocket->u.xTCP.eTCPState == eCONNECT_SYN ) + { + /* Map the Last packet onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + ProtocolHeaders_t * pxLastHeaders = ( ( ProtocolHeaders_t * ) + &( pxSocket->u.xTCP.xPacket.u.ucLastPacket[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) ] ) ); + + /* Clear the SYN flag in lastPacket. */ + pxLastHeaders->xTCPHeader.ucTCPFlags = tcpTCP_FLAG_ACK; + pxProtocolHeaders->xTCPHeader.ucTCPFlags = tcpTCP_FLAG_ACK; + + /* This socket was the one connecting actively so now perform the + * synchronisation. */ + vTCPWindowInit( &pxSocket->u.xTCP.xTCPWindow, + ulSequenceNumber, pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber, ( uint32_t ) pxSocket->u.xTCP.usMSS ); + pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + 1U; + pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber + 1U; + pxTCPWindow->tx.ulCurrentSequenceNumber++; /* because we send a TCP_SYN [ | TCP_ACK ]; */ + pxTCPWindow->ulNextTxSequenceNumber++; + } + else if( ulReceiveLength == 0U ) + { + pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber; + } + else + { + /* Nothing. */ + } + + /* The SYN+ACK has been confirmed, increase the next sequence number by + * 1. */ + pxTCPWindow->ulOurSequenceNumber = pxTCPWindow->tx.ulFirstSequenceNumber + 1U; + + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + FreeRTOS_debug_printf( ( "TCP: %s %u => %xip:%u set ESTAB (scaling %u)\n", + ( pxSocket->u.xTCP.eTCPState == ( uint8_t ) eCONNECT_SYN ) ? "active" : "passive", + pxSocket->usLocalPort, + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort, + ( unsigned ) pxSocket->u.xTCP.bits.bWinScaling ) ); + } + #endif /* ipconfigUSE_TCP_WIN */ + + if( ( pxSocket->u.xTCP.eTCPState == eCONNECT_SYN ) || ( ulReceiveLength != 0U ) ) + { + pxTCPHeader->ucTCPFlags = tcpTCP_FLAG_ACK; + + uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ( size_t ) ipSIZE_OF_TCP_HEADER + uxOptionsLength; + xSendLength = ( BaseType_t ) uxIntermediateResult; + pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + } + + #if ( ipconfigUSE_TCP_WIN != 0 ) + { + if( pxSocket->u.xTCP.bits.bWinScaling == pdFALSE_UNSIGNED ) + { + /* The other party did not send a scaling factor. + * A shifting factor in this side must be canceled. */ + pxSocket->u.xTCP.ucMyWinScaleFactor = 0; + pxSocket->u.xTCP.ucPeerWinScaleFactor = 0; + } + } + #endif /* ipconfigUSE_TCP_WIN */ + + /* This was the third step of connecting: SYN, SYN+ACK, ACK so now the + * connection is established. */ + vTCPStateChange( pxSocket, eESTABLISHED ); + } + + return xSendLength; + } + /*-----------------------------------------------------------*/ + +/** + * @brief prvHandleEstablished(): called from prvTCPHandleState() + * Called if the status is eESTABLISHED. Data reception has been handled + * earlier. Here the ACK's from peer will be checked, and if a FIN is received, + * the code will check if it may be accepted, i.e. if all expected data has been + * completely received. + * + * @param[in] pxSocket: The socket owning the connection. + * @param[in,out] ppxNetworkBuffer: Pointer to pointer to the network buffer. + * @param[in] ulReceiveLength: The length of the received packet. + * @param[in] uxOptionsLength: Length of TCP options. + * + * @return The send length of the packet to be sent. + */ + static BaseType_t prvHandleEstablished( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer, + uint32_t ulReceiveLength, + UBaseType_t uxOptionsLength ) + { + /* Map the buffer onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * ) + &( ( *ppxNetworkBuffer )->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) ] ) ); + TCPHeader_t * pxTCPHeader = &pxProtocolHeaders->xTCPHeader; + TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; + uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags; + uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ), ulCount, ulIntermediateResult = 0; + BaseType_t xSendLength = 0, xMayClose = pdFALSE, bRxComplete, bTxDone; + int32_t lDistance, lSendResult; + uint16_t usWindow; + UBaseType_t uxIntermediateResult = 0; + + /* Remember the window size the peer is advertising. */ + usWindow = FreeRTOS_ntohs( pxTCPHeader->usWindow ); + pxSocket->u.xTCP.ulWindowSize = ( uint32_t ) usWindow; + #if ( ipconfigUSE_TCP_WIN != 0 ) + { + pxSocket->u.xTCP.ulWindowSize = + ( pxSocket->u.xTCP.ulWindowSize << pxSocket->u.xTCP.ucPeerWinScaleFactor ); + } + #endif /* ipconfigUSE_TCP_WIN */ + + if( ( ucTCPFlags & ( uint8_t ) tcpTCP_FLAG_ACK ) == 0U ) + { + /* RFC793: If ACK bit is not set at this state, the segment should + * be dropped + */ + } + else + { + ulCount = ulTCPWindowTxAck( pxTCPWindow, FreeRTOS_ntohl( pxTCPHeader->ulAckNr ) ); + + /* ulTCPWindowTxAck() returns the number of bytes which have been acked, + * starting at 'tx.ulCurrentSequenceNumber'. Advance the tail pointer in + * txStream. */ + if( ( pxSocket->u.xTCP.txStream != NULL ) && ( ulCount > 0U ) ) + { + /* Just advancing the tail index, 'ulCount' bytes have been + * confirmed, and because there is new space in the txStream, the + * user/owner should be woken up. */ + /* _HT_ : only in case the socket's waiting? */ + if( uxStreamBufferGet( pxSocket->u.xTCP.txStream, 0U, NULL, ( size_t ) ulCount, pdFALSE ) != 0U ) + { + pxSocket->xEventBits |= ( EventBits_t ) eSOCKET_SEND; + + #if ipconfigSUPPORT_SELECT_FUNCTION == 1 + { + if( ( pxSocket->xSelectBits & ( ( EventBits_t ) eSELECT_WRITE ) ) != 0U ) + { + pxSocket->xEventBits |= ( ( EventBits_t ) eSELECT_WRITE ) << SOCKET_EVENT_BIT_COUNT; + } + } + #endif + + /* In case the socket owner has installed an OnSent handler, + * call it now. */ + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleSent ) ) + { + pxSocket->u.xTCP.pxHandleSent( ( Socket_t ) pxSocket, ulCount ); + } + } + #endif /* ipconfigUSE_CALLBACKS == 1 */ + } + } + + /* If this socket has a stream for transmission, add the data to the + * outgoing segment(s). */ + if( pxSocket->u.xTCP.txStream != NULL ) + { + prvTCPAddTxData( pxSocket ); + } + + pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber; + + if( ( pxSocket->u.xTCP.bits.bFinAccepted != pdFALSE_UNSIGNED ) || ( ( ucTCPFlags & ( uint8_t ) tcpTCP_FLAG_FIN ) != 0U ) ) + { + /* Peer is requesting to stop, see if we're really finished. */ + xMayClose = pdTRUE; + + /* Checks are only necessary if we haven't sent a FIN yet. */ + if( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED ) + { + /* xTCPWindowTxDone returns true when all Tx queues are empty. */ + bRxComplete = xTCPWindowRxEmpty( pxTCPWindow ); + bTxDone = xTCPWindowTxDone( pxTCPWindow ); + + if( ( bRxComplete == 0 ) || ( bTxDone == 0 ) ) + { + /* Refusing FIN: Rx incomplete 1 optlen 4 tx done 1. */ + FreeRTOS_debug_printf( ( "Refusing FIN[%u,%u]: RxCompl %d tx done %d\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + ( int ) bRxComplete, + ( int ) bTxDone ) ); + xMayClose = pdFALSE; + } + else + { + ulIntermediateResult = ulSequenceNumber + ulReceiveLength - pxTCPWindow->rx.ulCurrentSequenceNumber; + lDistance = ( int32_t ) ulIntermediateResult; + + if( lDistance > 1 ) + { + FreeRTOS_debug_printf( ( "Refusing FIN: Rx not complete %d (cur %u high %u)\n", + ( int ) lDistance, + ( unsigned ) ( pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ), + ( unsigned ) ( pxTCPWindow->rx.ulHighestSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ) ) ); + + xMayClose = pdFALSE; + } + } + } + + if( xTCPWindowLoggingLevel > 0 ) + { + FreeRTOS_debug_printf( ( "TCP: FIN received, mayClose = %d (Rx %u Len %d, Tx %u)\n", + ( int ) xMayClose, + ( unsigned ) ( ulSequenceNumber - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber ), + ( unsigned ) ulReceiveLength, + ( unsigned ) ( pxTCPWindow->tx.ulCurrentSequenceNumber - pxSocket->u.xTCP.xTCPWindow.tx.ulFirstSequenceNumber ) ) ); + } + + if( xMayClose != pdFALSE ) + { + pxSocket->u.xTCP.bits.bFinAccepted = pdTRUE_UNSIGNED; + xSendLength = prvTCPHandleFin( pxSocket, *ppxNetworkBuffer ); + } + } + + if( xMayClose == pdFALSE ) + { + pxTCPHeader->ucTCPFlags = tcpTCP_FLAG_ACK; + + if( ulReceiveLength != 0U ) + { + uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength; + xSendLength = ( BaseType_t ) uxIntermediateResult; + /* TCP-offset equals '( ( length / 4 ) << 4 )', resulting in a shift-left 2 */ + pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + + if( pxSocket->u.xTCP.bits.bFinSent != pdFALSE_UNSIGNED ) + { + pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->tx.ulFINSequenceNumber; + } + } + + /* Now get data to be transmitted. */ + + /* _HT_ patch: since the MTU has be fixed at 1500 in stead of 1526, TCP + * can not send-out both TCP options and also a full packet. Sending + * options (SACK) is always more urgent than sending data, which can be + * sent later. */ + if( uxOptionsLength == 0U ) + { + /* prvTCPPrepareSend might allocate a bigger network buffer, if + * necessary. */ + lSendResult = prvTCPPrepareSend( pxSocket, ppxNetworkBuffer, uxOptionsLength ); + + if( lSendResult > 0 ) + { + xSendLength = ( BaseType_t ) lSendResult; + } + } + } + } + + return xSendLength; + } + /*-----------------------------------------------------------*/ + + +/** + * @brief Check incoming packets for valid data and handle the state of the + * TCP connection and respond according to the situation. + * + * @param[in] pxSocket: The socket whose connection state is being handled. + * @param[in] ppxNetworkBuffer: The network buffer descriptor holding the + * packet received from the peer. + * + * @return If the data is correct and some packet was sent to the peer, then + * the number of bytes sent is returned, or else a negative value is + * returned indicating an error. + * + * @note prvTCPHandleState() is the most important function of this TCP stack + * We've tried to keep it (relatively short) by putting a lot of code in + * the static functions above: + * + * prvCheckRxData() + * prvStoreRxData() + * prvSetOptions() + * prvHandleSynReceived() + * prvHandleEstablished() + * prvSendData() + * + * As these functions are declared static, and they're called from one location + * only, most compilers will inline them, thus avoiding a call and return. + */ + BaseType_t prvTCPHandleState( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer ) + { + /* Map the buffer onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * ) + &( ( *ppxNetworkBuffer )->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( *ppxNetworkBuffer ) ] ) ); + TCPHeader_t * pxTCPHeader = &( pxProtocolHeaders->xTCPHeader ); + BaseType_t xSendLength = 0; + uint32_t ulReceiveLength; /* Number of bytes contained in the TCP message. */ + uint8_t * pucRecvData; + uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ); + + /* uxOptionsLength: the size of the options to be sent (always a multiple of + * 4 bytes) + * 1. in the SYN phase, we shall communicate the MSS + * 2. in case of a SACK, Selective ACK, ack a segment which comes in + * out-of-order. */ + UBaseType_t uxOptionsLength = 0U; + uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags; + TCPWindow_t * pxTCPWindow = &( pxSocket->u.xTCP.xTCPWindow ); + UBaseType_t uxIntermediateResult = 0; + uint32_t ulSum; + + /* First get the length and the position of the received data, if any. + * pucRecvData will point to the first byte of the TCP payload. */ + ulReceiveLength = ( uint32_t ) prvCheckRxData( *ppxNetworkBuffer, &pucRecvData ); + + if( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) + { + if( pxTCPWindow->rx.ulCurrentSequenceNumber == ( ulSequenceNumber + 1U ) ) + { + /* This is most probably a keep-alive message from peer. Setting + * 'bWinChange' doesn't cause a window-size-change, the flag is used + * here to force sending an immediate ACK. */ + pxSocket->u.xTCP.bits.bWinChange = pdTRUE_UNSIGNED; + } + } + + /* Keep track of the highest sequence number that might be expected within + * this connection. */ + ulSum = ulSequenceNumber + ulReceiveLength - pxTCPWindow->rx.ulHighestSequenceNumber; + + if( ( ( int32_t ) ulSum ) > 0 ) + { + pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + ulReceiveLength; + } + + /* Storing data may result in a fatal error if malloc() fails. */ + if( prvStoreRxData( pxSocket, pucRecvData, *ppxNetworkBuffer, ulReceiveLength ) < 0 ) + { + xSendLength = -1; + } + else + { + eIPTCPState_t eState; + + uxOptionsLength = prvSetOptions( pxSocket, *ppxNetworkBuffer ); + + if( ( pxSocket->u.xTCP.eTCPState == eSYN_RECEIVED ) && ( ( ucTCPFlags & ( uint8_t ) tcpTCP_FLAG_CTRL ) == ( uint8_t ) tcpTCP_FLAG_SYN ) ) + { + FreeRTOS_debug_printf( ( "eSYN_RECEIVED: ACK expected, not SYN: peer missed our SYN+ACK\n" ) ); + + /* In eSYN_RECEIVED a simple ACK is expected, but apparently the + * 'SYN+ACK' didn't arrive. Step back to the previous state in which + * a first incoming SYN is handled. The SYN was counted already so + * decrease it first. */ + vTCPStateChange( pxSocket, eSYN_FIRST ); + } + + if( ( ( ucTCPFlags & tcpTCP_FLAG_FIN ) != 0U ) && ( pxSocket->u.xTCP.bits.bFinRecv == pdFALSE_UNSIGNED ) ) + { + /* It's the first time a FIN has been received, remember its + * sequence number. */ + pxTCPWindow->rx.ulFINSequenceNumber = ulSequenceNumber + ulReceiveLength; + pxSocket->u.xTCP.bits.bFinRecv = pdTRUE_UNSIGNED; + + /* Was peer the first one to send a FIN? */ + if( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED ) + { + /* If so, don't send the-last-ACK. */ + pxSocket->u.xTCP.bits.bFinLast = pdTRUE_UNSIGNED; + } + } + + eState = ( eIPTCPState_t ) pxSocket->u.xTCP.eTCPState; + + switch( eState ) + { + case eCLOSED: /* (server + client) no connection state at all. */ + + /* Nothing to do for a closed socket, except waiting for the + * owner. */ + break; + + case eTCP_LISTEN: /* (server) waiting for a connection request from + * any remote TCP and port. */ + + /* The listen state was handled in xProcessReceivedTCPPacket(). + * Should not come here. */ + break; + + case eSYN_FIRST: /* (server) Just received a SYN request for a server + * socket. */ + + /* A new socket has been created, reply with a SYN+ACK. + * Acknowledge with seq+1 because the SYN is seen as pseudo data + * with len = 1. */ + uxOptionsLength = prvSetSynAckOptions( pxSocket, pxTCPHeader ); + pxTCPHeader->ucTCPFlags = ( uint8_t ) tcpTCP_FLAG_SYN | ( uint8_t ) tcpTCP_FLAG_ACK; + + uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength; + xSendLength = ( BaseType_t ) uxIntermediateResult; + + /* Set the TCP offset field: ipSIZE_OF_TCP_HEADER equals 20 and + * uxOptionsLength is a multiple of 4. The complete expression is: + * ucTCPOffset = ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) / 4 ) << 4 */ + pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + vTCPStateChange( pxSocket, eSYN_RECEIVED ); + + pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + 1U; + pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber + 1U; + pxTCPWindow->ulNextTxSequenceNumber = pxTCPWindow->tx.ulFirstSequenceNumber + 1U; + pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->tx.ulFirstSequenceNumber + 1U; /* because we send a TCP_SYN. */ + break; + + case eCONNECT_SYN: /* (client) also called SYN_SENT: we've just send a + * SYN, expect a SYN+ACK and send a ACK now. */ + /* Fall through */ + case eSYN_RECEIVED: /* (server) we've had a SYN, replied with SYN+SCK + * expect a ACK and do nothing. */ + xSendLength = prvHandleSynReceived( pxSocket, *( ppxNetworkBuffer ), ulReceiveLength, uxOptionsLength ); + break; + + case eESTABLISHED: /* (server + client) an open connection, data + * received can be delivered to the user. The normal + * state for the data transfer phase of the connection + * The closing states are also handled here with the + * use of some flags. */ + xSendLength = prvHandleEstablished( pxSocket, ppxNetworkBuffer, ulReceiveLength, uxOptionsLength ); + break; + + case eLAST_ACK: /* (server + client) waiting for an acknowledgement + * of the connection termination request previously + * sent to the remote TCP (which includes an + * acknowledgement of its connection termination + * request). */ + /* Fall through */ + case eFIN_WAIT_1: /* (server + client) waiting for a connection termination request from the remote TCP, + * or an acknowledgement of the connection termination request previously sent. */ + /* Fall through */ + case eFIN_WAIT_2: /* (server + client) waiting for a connection termination request from the remote TCP. */ + xSendLength = prvTCPHandleFin( pxSocket, *ppxNetworkBuffer ); + break; + + case eCLOSE_WAIT: /* (server + client) waiting for a connection + * termination request from the local user. Nothing to + * do, connection is closed, wait for owner to close + * this socket. */ + break; + + case eCLOSING: /* (server + client) waiting for a connection + * termination request acknowledgement from the remote + * TCP. */ + break; + + case eTIME_WAIT: /* (either server or client) waiting for enough time + * to pass to be sure the remote TCP received the + * acknowledgement of its connection termination + * request. [According to RFC 793 a connection can stay + * in TIME-WAIT for a maximum of four minutes known as + * a MSL (maximum segment lifetime).] These states are + * implemented implicitly by settings flags like + * 'bFinSent', 'bFinRecv', and 'bFinAcked'. */ + break; + + default: + /* No more known states. */ + break; + } + } + + if( xSendLength > 0 ) + { + xSendLength = prvSendData( pxSocket, ppxNetworkBuffer, ulReceiveLength, xSendLength ); + } + + return xSendLength; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Handle 'listen' event on the given socket. + * + * @param[in] pxSocket: The socket on which the listen occurred. + * @param[in] pxNetworkBuffer: The network buffer carrying the packet. + * + * @return If a new socket/duplicate socket is created, then the pointer to + * that socket is returned or else, a NULL pointer is returned. + */ + FreeRTOS_Socket_t * prvHandleListen( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t * pxNetworkBuffer ) + { + /* Map the ethernet buffer onto a TCPPacket_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const TCPPacket_t * pxTCPPacket = ( ( const TCPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); + FreeRTOS_Socket_t * pxReturn = NULL; + uint32_t ulInitialSequenceNumber; + + /* Silently discard a SYN packet which was not specifically sent for this node. */ + if( pxTCPPacket->xIPHeader.ulDestinationIPAddress == *ipLOCAL_IP_ADDRESS_POINTER ) + { + /* Assume that a new Initial Sequence Number will be required. Request + * it now in order to fail out if necessary. */ + ulInitialSequenceNumber = ulApplicationGetNextSequenceNumber( *ipLOCAL_IP_ADDRESS_POINTER, + pxSocket->usLocalPort, + pxTCPPacket->xIPHeader.ulSourceIPAddress, + pxTCPPacket->xTCPHeader.usSourcePort ); + } + else + { + /* Set the sequence number to 0 to avoid further processing. */ + ulInitialSequenceNumber = 0U; + } + + /* A pure SYN (without ACK) has come in, create a new socket to answer + * it. */ + if( ulInitialSequenceNumber != 0U ) + { + if( pxSocket->u.xTCP.bits.bReuseSocket != pdFALSE_UNSIGNED ) + { + /* The flag bReuseSocket indicates that the same instance of the + * listening socket should be used for the connection. */ + pxReturn = pxSocket; + pxSocket->u.xTCP.bits.bPassQueued = pdTRUE_UNSIGNED; + pxSocket->u.xTCP.pxPeerSocket = pxSocket; + } + else + { + /* The socket does not have the bReuseSocket flag set meaning create a + * new socket when a connection comes in. */ + pxReturn = NULL; + + if( pxSocket->u.xTCP.usChildCount >= pxSocket->u.xTCP.usBacklog ) + { + FreeRTOS_printf( ( "Check: Socket %u already has %u / %u child%s\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usChildCount, + pxSocket->u.xTCP.usBacklog, + ( pxSocket->u.xTCP.usChildCount == 1U ) ? "" : "ren" ) ); + ( void ) prvTCPSendReset( pxNetworkBuffer ); + } + else + { + FreeRTOS_Socket_t * pxNewSocket = ( FreeRTOS_Socket_t * ) + FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP ); + + /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */ + /* coverity[misra_c_2012_rule_11_4_violation] */ + if( ( pxNewSocket == NULL ) || ( pxNewSocket == FREERTOS_INVALID_SOCKET ) ) + { + FreeRTOS_debug_printf( ( "TCP: Listen: new socket failed\n" ) ); + ( void ) prvTCPSendReset( pxNetworkBuffer ); + } + else if( prvTCPSocketCopy( pxNewSocket, pxSocket ) != pdFALSE ) + { + /* The socket will be connected immediately, no time for the + * owner to setsockopt's, therefore copy properties of the server + * socket to the new socket. Only the binding might fail (due to + * lack of resources). */ + pxReturn = pxNewSocket; + } + else + { + /* Copying failed somehow. */ + } + } + } + } + + if( ( ulInitialSequenceNumber != 0U ) && ( pxReturn != NULL ) ) + { + /* Map the byte stream onto the ProtocolHeaders_t for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ProtocolHeaders_t * pxProtocolHeaders = ( ( const ProtocolHeaders_t * ) + &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( pxNetworkBuffer ) ] ) ); + + pxReturn->u.xTCP.usRemotePort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usSourcePort ); + pxReturn->u.xTCP.ulRemoteIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulSourceIPAddress ); + pxReturn->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulInitialSequenceNumber; + + /* Here is the SYN action. */ + pxReturn->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber = FreeRTOS_ntohl( pxProtocolHeaders->xTCPHeader.ulSequenceNumber ); + prvSocketSetMSS( pxReturn ); + + prvTCPCreateWindow( pxReturn ); + + vTCPStateChange( pxReturn, eSYN_FIRST ); + + /* Make a copy of the header up to the TCP header. It is needed later + * on, whenever data must be sent to the peer. */ + ( void ) memcpy( ( void * ) pxReturn->u.xTCP.xPacket.u.ucLastPacket, + ( const void * ) pxNetworkBuffer->pucEthernetBuffer, + sizeof( pxReturn->u.xTCP.xPacket.u.ucLastPacket ) ); + } + + return pxReturn; + } + /*-----------------------------------------------------------*/ + + +/** + * @brief Duplicates a socket after a listening socket receives a connection and bind + * the new socket to the same port as the listening socket. + * Also, let the new socket inherit all properties from the listening socket. + * + * @param[in] pxNewSocket: Pointer to the new socket. + * @param[in] pxSocket: Pointer to the socket being duplicated. + * + * @return If all steps all successful, then pdTRUE is returned. Else, pdFALSE. + */ + static BaseType_t prvTCPSocketCopy( FreeRTOS_Socket_t * pxNewSocket, + FreeRTOS_Socket_t * pxSocket ) + { + struct freertos_sockaddr xAddress; + BaseType_t xResult; + + pxNewSocket->xReceiveBlockTime = pxSocket->xReceiveBlockTime; + pxNewSocket->xSendBlockTime = pxSocket->xSendBlockTime; + pxNewSocket->ucSocketOptions = pxSocket->ucSocketOptions; + pxNewSocket->u.xTCP.uxRxStreamSize = pxSocket->u.xTCP.uxRxStreamSize; + pxNewSocket->u.xTCP.uxTxStreamSize = pxSocket->u.xTCP.uxTxStreamSize; + pxNewSocket->u.xTCP.uxLittleSpace = pxSocket->u.xTCP.uxLittleSpace; + pxNewSocket->u.xTCP.uxEnoughSpace = pxSocket->u.xTCP.uxEnoughSpace; + pxNewSocket->u.xTCP.uxRxWinSize = pxSocket->u.xTCP.uxRxWinSize; + pxNewSocket->u.xTCP.uxTxWinSize = pxSocket->u.xTCP.uxTxWinSize; + + #if ( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 ) + { + pxNewSocket->pxUserSemaphore = pxSocket->pxUserSemaphore; + } + #endif /* ipconfigSOCKET_HAS_USER_SEMAPHORE */ + + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + /* In case call-backs are used, copy them from parent to child. */ + pxNewSocket->u.xTCP.pxHandleConnected = pxSocket->u.xTCP.pxHandleConnected; + pxNewSocket->u.xTCP.pxHandleReceive = pxSocket->u.xTCP.pxHandleReceive; + pxNewSocket->u.xTCP.pxHandleSent = pxSocket->u.xTCP.pxHandleSent; + } + #endif /* ipconfigUSE_CALLBACKS */ + + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + /* Child socket of listening sockets will inherit the Socket Set + * Otherwise the owner has no chance of including it into the set. */ + if( pxSocket->pxSocketSet != NULL ) + { + pxNewSocket->pxSocketSet = pxSocket->pxSocketSet; + pxNewSocket->xSelectBits = pxSocket->xSelectBits | ( ( EventBits_t ) eSELECT_READ ) | ( ( EventBits_t ) eSELECT_EXCEPT ); + } + } + #endif /* ipconfigSUPPORT_SELECT_FUNCTION */ + + /* And bind it to the same local port as its parent. */ + xAddress.sin_addr = *ipLOCAL_IP_ADDRESS_POINTER; + xAddress.sin_port = FreeRTOS_htons( pxSocket->usLocalPort ); + + #if ( ipconfigTCP_HANG_PROTECTION == 1 ) + { + /* Only when there is anti-hanging protection, a socket may become an + * orphan temporarily. Once this socket is really connected, the owner of + * the server socket will be notified. */ + + /* When bPassQueued is true, the socket is an orphan until it gets + * connected. */ + pxNewSocket->u.xTCP.bits.bPassQueued = pdTRUE_UNSIGNED; + pxNewSocket->u.xTCP.pxPeerSocket = pxSocket; + } + #else + { + /* A reference to the new socket may be stored and the socket is marked + * as 'passable'. */ + + /* When bPassAccept is true, this socket may be returned in a call to + * accept(). */ + pxNewSocket->u.xTCP.bits.bPassAccept = pdTRUE_UNSIGNED; + + if( pxSocket->u.xTCP.pxPeerSocket == NULL ) + { + pxSocket->u.xTCP.pxPeerSocket = pxNewSocket; + } + } + #endif /* if ( ipconfigTCP_HANG_PROTECTION == 1 ) */ + + pxSocket->u.xTCP.usChildCount++; + + FreeRTOS_debug_printf( ( "Gain: Socket %u now has %u / %u child%s\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usChildCount, + pxSocket->u.xTCP.usBacklog, + ( pxSocket->u.xTCP.usChildCount == 1U ) ? "" : "ren" ) ); + + /* Now bind the child socket to the same port as the listening socket. */ + if( vSocketBind( pxNewSocket, &xAddress, sizeof( xAddress ), pdTRUE ) != 0 ) + { + FreeRTOS_debug_printf( ( "TCP: Listen: new socket bind error\n" ) ); + ( void ) vSocketClose( pxNewSocket ); + xResult = pdFALSE; + } + else + { + xResult = pdTRUE; + } + + return xResult; + } + /*-----------------------------------------------------------*/ + + #if ( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) + + const char * FreeRTOS_GetTCPStateName( UBaseType_t ulState ) + { + static const char * const pcStateNames[] = + { + "eCLOSED", + "eTCP_LISTEN", + "eCONNECT_SYN", + "eSYN_FIRST", + "eSYN_RECEIVED", + "eESTABLISHED", + "eFIN_WAIT_1", + "eFIN_WAIT_2", + "eCLOSE_WAIT", + "eCLOSING", + "eLAST_ACK", + "eTIME_WAIT", + "eUNKNOWN", + }; + BaseType_t xIndex = ( BaseType_t ) ulState; + + if( ( xIndex < 0 ) || ( xIndex >= ARRAY_SIZE( pcStateNames ) ) ) + { + /* The last item is called 'eUNKNOWN' */ + xIndex = ARRAY_SIZE( pcStateNames ); + xIndex--; + } + + return pcStateNames[ xIndex ]; + } + + #endif /* ( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) */ + /*-----------------------------------------------------------*/ + +#endif /* ipconfigUSE_TCP == 1 */ diff --git a/FreeRTOS/source/FreeRTOS_TCP_Transmission.c b/FreeRTOS/source/FreeRTOS_TCP_Transmission.c new file mode 100644 index 0000000..a113d8f --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_TCP_Transmission.c @@ -0,0 +1,1508 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_TCP_Transmission.c + * @brief Module which prepares the packet to be sent through + * a socket for FreeRTOS+TCP. + * It depends on FreeRTOS_TCP_WIN.c, which handles the TCP windowing + * schemes. + * + * Endianness: in this module all ports and IP addresses are stored in + * host byte-order, except fields in the IP-packets + */ + +/* Standard includes. */ +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOSIPConfigDefaults.h" + +#include "FreeRTOS_TCP_IP.h" +#include "FreeRTOS_TCP_Reception.h" +#include "FreeRTOS_TCP_Transmission.h" +#include "FreeRTOS_TCP_State_Handling.h" +#include "FreeRTOS_TCP_Utils.h" + +/* Just make sure the contents doesn't get compiled if TCP is not enabled. */ +#if ipconfigUSE_TCP == 1 + +/* + * Common code for sending a TCP protocol control packet (i.e. no options, no + * payload, just flags). + */ + static BaseType_t prvTCPSendSpecialPacketHelper( NetworkBufferDescriptor_t * pxNetworkBuffer, + uint8_t ucTCPFlags ); + +/* + * Let ARP look-up the MAC-address of the peer and initialise the first SYN + * packet. + */ + static BaseType_t prvTCPPrepareConnect( FreeRTOS_Socket_t * pxSocket ); + +/*------------------------------------------------------------------------*/ + +/** + * @brief Check if the outgoing connection is already prepared, if not + * call prvTCPPrepareConnect() to continue the preparation. + * @param[in] pxSocket: The socket that wants to connect. + * @return Returns pdTRUE if the connection is prepared, i.e. the MAC- + * address of the peer is already known. */ + static BaseType_t prvTCPMakeSurePrepared( FreeRTOS_Socket_t * pxSocket ) + { + BaseType_t xReturn = pdTRUE; + + if( pxSocket->u.xTCP.bits.bConnPrepared == pdFALSE_UNSIGNED ) + { + if( prvTCPPrepareConnect( pxSocket ) != pdTRUE ) + { + /* The preparation of a connection ( ARP resolution ) is not yet ready. */ + xReturn = pdFALSE; + } + } + + return xReturn; + } +/*-----------------------------------------------------------*/ + +/** + * @brief prvTCPSendPacket() will be called when the socket time-out has been reached. + * + * @param[in] pxSocket: The socket owning the connection. + * + * @return Number of bytes to be sent. + * + * @note It is only called by xTCPSocketCheck(). + */ + int32_t prvTCPSendPacket( FreeRTOS_Socket_t * pxSocket ) + { + int32_t lResult = 0; + UBaseType_t uxOptionsLength, uxIntermediateResult = 0; + NetworkBufferDescriptor_t * pxNetworkBuffer; + + if( pxSocket->u.xTCP.eTCPState != eCONNECT_SYN ) + { + /* The connection is in a state other than SYN. */ + pxNetworkBuffer = NULL; + + /* prvTCPSendRepeated() will only create a network buffer if necessary, + * i.e. when data must be sent to the peer. */ + lResult = prvTCPSendRepeated( pxSocket, &pxNetworkBuffer ); + + if( pxNetworkBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + } + else + { + if( pxSocket->u.xTCP.ucRepCount >= 3U ) + { + /* The connection is in the SYN status. The packet will be repeated + * to most 3 times. When there is no response, the socket get the + * status 'eCLOSE_WAIT'. */ + FreeRTOS_debug_printf( ( "Connect: giving up %xip:%u\n", + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, /* IP address of remote machine. */ + pxSocket->u.xTCP.usRemotePort ) ); /* Port on remote machine. */ + vTCPStateChange( pxSocket, eCLOSE_WAIT ); + } + else if( prvTCPMakeSurePrepared( pxSocket ) == pdTRUE ) + { + ProtocolHeaders_t * pxProtocolHeaders; + const UBaseType_t uxHeaderSize = ipSIZE_OF_IPv4_HEADER; + + /* Or else, if the connection has been prepared, or can be prepared + * now, proceed to send the packet with the SYN flag. + * prvTCPPrepareConnect() prepares 'xPacket' and returns pdTRUE if + * the Ethernet address of the peer or the gateway is found. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxProtocolHeaders = ( ( ProtocolHeaders_t * ) &( pxSocket->u.xTCP.xPacket.u.ucLastPacket[ ipSIZE_OF_ETH_HEADER + uxHeaderSize ] ) ); + + /* About to send a SYN packet. Call prvSetSynAckOptions() to set + * the proper options: The size of MSS and whether SACK's are + * allowed. */ + uxOptionsLength = prvSetSynAckOptions( pxSocket, &( pxProtocolHeaders->xTCPHeader ) ); + + /* Return the number of bytes to be sent. */ + uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength; + lResult = ( int32_t ) uxIntermediateResult; + + /* Set the TCP offset field: ipSIZE_OF_TCP_HEADER equals 20 and + * uxOptionsLength is always a multiple of 4. The complete expression + * would be: + * ucTCPOffset = ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) / 4 ) << 4 */ + pxProtocolHeaders->xTCPHeader.ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + + /* Repeat Count is used for a connecting socket, to limit the number + * of tries. */ + pxSocket->u.xTCP.ucRepCount++; + + /* Send the SYN message to make a connection. The messages is + * stored in the socket field 'xPacket'. It will be wrapped in a + * pseudo network buffer descriptor before it will be sent. */ + prvTCPReturnPacket( pxSocket, NULL, ( uint32_t ) lResult, pdFALSE ); + } + else + { + /* Nothing to do. */ + } + } + + /* Return the total number of bytes sent. */ + return lResult; + } + /*-----------------------------------------------------------*/ + +/** + * @brief prvTCPSendRepeated will try to send a series of messages, as + * long as there is data to be sent and as long as the transmit + * window isn't full. + * + * @param[in] pxSocket: The socket owning the connection. + * @param[in,out] ppxNetworkBuffer: Pointer to pointer to the network buffer. + * + * @return Total number of bytes sent. + */ + int32_t prvTCPSendRepeated( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer ) + { + UBaseType_t uxIndex; + int32_t lResult = 0; + UBaseType_t uxOptionsLength = 0U; + int32_t xSendLength; + + for( uxIndex = 0U; uxIndex < ( UBaseType_t ) SEND_REPEATED_COUNT; uxIndex++ ) + { + /* prvTCPPrepareSend() might allocate a network buffer if there is data + * to be sent. */ + xSendLength = prvTCPPrepareSend( pxSocket, ppxNetworkBuffer, uxOptionsLength ); + + if( xSendLength <= 0 ) + { + break; + } + + /* And return the packet to the peer. */ + prvTCPReturnPacket( pxSocket, *ppxNetworkBuffer, ( uint32_t ) xSendLength, ipconfigZERO_COPY_TX_DRIVER ); + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + *ppxNetworkBuffer = NULL; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + lResult += xSendLength; + } + + /* Return the total number of bytes sent. */ + return lResult; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Return (or send) a packet to the peer. The data is stored in pxBuffer, + * which may either point to a real network buffer or to a TCP socket field + * called 'xTCP.xPacket'. A temporary xNetworkBuffer will be used to pass + * the data to the NIC. + * + * @param[in] pxSocket: The socket owning the connection. + * @param[in] pxDescriptor: The network buffer descriptor carrying the packet. + * @param[in] ulLen: Length of the packet being sent. + * @param[in] xReleaseAfterSend: pdTRUE if the ownership of the descriptor is + * transferred to the network interface. + */ + void prvTCPReturnPacket( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t * pxDescriptor, + uint32_t ulLen, + BaseType_t xReleaseAfterSend ) + { + TCPPacket_t * pxTCPPacket; + IPHeader_t * pxIPHeader; + BaseType_t xDoRelease = xReleaseAfterSend; + EthernetHeader_t * pxEthernetHeader; + uint32_t ulFrontSpace, ulSpace, ulSourceAddress, ulWinSize; + const TCPWindow_t * pxTCPWindow; + NetworkBufferDescriptor_t * pxNetworkBuffer = pxDescriptor; + NetworkBufferDescriptor_t xTempBuffer; + /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + + /* For sending, a pseudo network buffer will be used, as explained above. */ + + if( pxNetworkBuffer == NULL ) + { + pxNetworkBuffer = &xTempBuffer; + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + pxNetworkBuffer->pxNextBuffer = NULL; + } + #endif + pxNetworkBuffer->pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket; + pxNetworkBuffer->xDataLength = sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ); + xDoRelease = pdFALSE; + } + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + if( xDoRelease == pdFALSE ) + { + pxNetworkBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, ( size_t ) pxNetworkBuffer->xDataLength ); + + if( pxNetworkBuffer != NULL ) + { + xDoRelease = pdTRUE; + } + else + { + FreeRTOS_debug_printf( ( "prvTCPReturnPacket: duplicate failed\n" ) ); + } + } + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + #ifndef __COVERITY__ + if( pxNetworkBuffer != NULL ) /* LCOV_EXCL_BR_LINE the 2nd branch will never be reached */ + #endif + { + /* Map the ethernet buffer onto a TCPPacket_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxTCPPacket = ( ( TCPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); + pxIPHeader = &pxTCPPacket->xIPHeader; + pxEthernetHeader = &pxTCPPacket->xEthernetHeader; + + /* Fill the packet, using hton translations. */ + if( pxSocket != NULL ) + { + /* Calculate the space in the RX buffer in order to advertise the + * size of this socket's reception window. */ + pxTCPWindow = &( pxSocket->u.xTCP.xTCPWindow ); + + if( pxSocket->u.xTCP.rxStream != NULL ) + { + /* An RX stream was created already, see how much space is + * available. */ + ulFrontSpace = ( uint32_t ) uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ); + } + else + { + /* No RX stream has been created, the full stream size is + * available. */ + ulFrontSpace = ( uint32_t ) pxSocket->u.xTCP.uxRxStreamSize; + } + + /* Take the minimum of the RX buffer space and the RX window size. */ + ulSpace = FreeRTOS_min_uint32( pxTCPWindow->xSize.ulRxWindowLength, ulFrontSpace ); + + if( ( pxSocket->u.xTCP.bits.bLowWater != pdFALSE_UNSIGNED ) || ( pxSocket->u.xTCP.bits.bRxStopped != pdFALSE_UNSIGNED ) ) + { + /* The low-water mark was reached, meaning there was little + * space left. The socket will wait until the application has read + * or flushed the incoming data, and 'zero-window' will be + * advertised. */ + ulSpace = 0U; + } + + /* If possible, advertise an RX window size of at least 1 MSS, otherwise + * the peer might start 'zero window probing', i.e. sending small packets + * (1, 2, 4, 8... bytes). */ + if( ( ulSpace < pxSocket->u.xTCP.usMSS ) && ( ulFrontSpace >= pxSocket->u.xTCP.usMSS ) ) + { + ulSpace = pxSocket->u.xTCP.usMSS; + } + + /* Avoid overflow of the 16-bit win field. */ + #if ( ipconfigUSE_TCP_WIN != 0 ) + { + ulWinSize = ( ulSpace >> pxSocket->u.xTCP.ucMyWinScaleFactor ); + } + #else + { + ulWinSize = ulSpace; + } + #endif + + if( ulWinSize > 0xfffcU ) + { + ulWinSize = 0xfffcU; + } + + pxTCPPacket->xTCPHeader.usWindow = FreeRTOS_htons( ( uint16_t ) ulWinSize ); + + /* The new window size has been advertised, switch off the flag. */ + pxSocket->u.xTCP.bits.bWinChange = pdFALSE_UNSIGNED; + + /* Later on, when deciding to delay an ACK, a precise estimate is needed + * of the free RX space. At this moment, 'ulHighestRxAllowed' would be the + * highest sequence number minus 1 that the socket will accept. */ + pxSocket->u.xTCP.ulHighestRxAllowed = pxTCPWindow->rx.ulCurrentSequenceNumber + ulSpace; + + #if ( ipconfigTCP_KEEP_ALIVE == 1 ) + if( pxSocket->u.xTCP.bits.bSendKeepAlive != pdFALSE_UNSIGNED ) + { + /* Sending a keep-alive packet, send the current sequence number + * minus 1, which will be recognised as a keep-alive packet and + * responded to by acknowledging the last byte. */ + pxSocket->u.xTCP.bits.bSendKeepAlive = pdFALSE_UNSIGNED; + pxSocket->u.xTCP.bits.bWaitKeepAlive = pdTRUE_UNSIGNED; + + pxTCPPacket->xTCPHeader.ulSequenceNumber = pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - 1U; + pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxTCPPacket->xTCPHeader.ulSequenceNumber ); + } + else + #endif /* if ( ipconfigTCP_KEEP_ALIVE == 1 ) */ + { + pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber ); + + if( ( pxTCPPacket->xTCPHeader.ucTCPFlags & ( uint8_t ) tcpTCP_FLAG_FIN ) != 0U ) + { + /* Suppress FIN in case this packet carries earlier data to be + * retransmitted. */ + uint32_t ulDataLen = ( uint32_t ) ( ulLen - ( ipSIZE_OF_TCP_HEADER + ipSIZE_OF_IPv4_HEADER ) ); + + if( ( pxTCPWindow->ulOurSequenceNumber + ulDataLen ) != pxTCPWindow->tx.ulFINSequenceNumber ) + { + pxTCPPacket->xTCPHeader.ucTCPFlags &= ( ( uint8_t ) ~tcpTCP_FLAG_FIN ); + FreeRTOS_debug_printf( ( "Suppress FIN for %u + %u < %u\n", + ( unsigned ) ( pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ulDataLen, + ( unsigned ) ( pxTCPWindow->tx.ulFINSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ) ) ); + } + } + } + + /* Tell which sequence number is expected next time */ + pxTCPPacket->xTCPHeader.ulAckNr = FreeRTOS_htonl( pxTCPWindow->rx.ulCurrentSequenceNumber ); + } + else + { + /* Sending data without a socket, probably replying with a RST flag + * Just swap the two sequence numbers. */ + vFlip_32( pxTCPPacket->xTCPHeader.ulSequenceNumber, pxTCPPacket->xTCPHeader.ulAckNr ); + } + + pxIPHeader->ucTimeToLive = ( uint8_t ) ipconfigTCP_TIME_TO_LIVE; + pxIPHeader->usLength = FreeRTOS_htons( ulLen ); + + if( ( pxSocket == NULL ) || ( *ipLOCAL_IP_ADDRESS_POINTER == 0U ) ) + { + /* When pxSocket is NULL, this function is called by prvTCPSendReset() + * and the IP-addresses must be swapped. + * Also swap the IP-addresses in case the IP-tack doesn't have an + * IP-address yet, i.e. when ( *ipLOCAL_IP_ADDRESS_POINTER == 0U ). */ + ulSourceAddress = pxIPHeader->ulDestinationIPAddress; + } + else + { + ulSourceAddress = *ipLOCAL_IP_ADDRESS_POINTER; + } + + pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress; + pxIPHeader->ulSourceIPAddress = ulSourceAddress; + vFlip_16( pxTCPPacket->xTCPHeader.usSourcePort, pxTCPPacket->xTCPHeader.usDestinationPort ); + + /* Just an increasing number. */ + pxIPHeader->usIdentification = FreeRTOS_htons( usPacketIdentifier ); + usPacketIdentifier++; + + /* The stack doesn't support fragments, so the fragment offset field must always be zero. + * The header was never memset to zero, so set both the fragment offset and fragmentation flags in one go. + */ + #if ( ipconfigFORCE_IP_DONT_FRAGMENT != 0 ) + pxIPHeader->usFragmentOffset = ipFRAGMENT_FLAGS_DONT_FRAGMENT; + #else + pxIPHeader->usFragmentOffset = 0U; + #endif + + /* Important: tell NIC driver how many bytes must be sent. */ + pxNetworkBuffer->xDataLength = ( size_t ) ulLen; + pxNetworkBuffer->xDataLength += ipSIZE_OF_ETH_HEADER; + + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) + { + /* calculate the IP header checksum, in case the driver won't do that. */ + pxIPHeader->usHeaderChecksum = 0x00U; + pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); + pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); + + /* calculate the TCP checksum for an outgoing packet. */ + ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxTCPPacket, pxNetworkBuffer->xDataLength, pdTRUE ); + } + #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) */ + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + pxNetworkBuffer->pxNextBuffer = NULL; + } + #endif + + + { + MACAddress_t xMACAddress; + uint32_t ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress; + eARPLookupResult_t eResult; + + eResult = eARPGetCacheEntry( &ulDestinationIPAddress, &xMACAddress ); + + if( eResult == eARPCacheHit ) + { + pvCopySource = &xMACAddress; + } + else + { + pvCopySource = &pxEthernetHeader->xSourceAddress; + } + + /* Fill in the destination MAC addresses. */ + pvCopyDest = &pxEthernetHeader->xDestinationAddress; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxEthernetHeader->xDestinationAddress ) ); + } + + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + /* The source MAC addresses is fixed to 'ipLOCAL_MAC_ADDRESS'. */ + pvCopySource = ipLOCAL_MAC_ADDRESS; + pvCopyDest = &pxEthernetHeader->xSourceAddress; + ( void ) memcpy( pvCopyDest, pvCopySource, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); + + #if ( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) + { + if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES ) + { + BaseType_t xIndex; + + for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ ) + { + pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0U; + } + + pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; + } + } + #endif /* if( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) */ + + /* Send! */ + iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer ); + ( void ) xNetworkInterfaceOutput( pxNetworkBuffer, xDoRelease ); + + if( xDoRelease == pdFALSE ) + { + /* Swap-back some fields, as pxBuffer probably points to a socket field + * containing the packet header. */ + vFlip_16( pxTCPPacket->xTCPHeader.usSourcePort, pxTCPPacket->xTCPHeader.usDestinationPort ); + pxTCPPacket->xIPHeader.ulSourceIPAddress = pxTCPPacket->xIPHeader.ulDestinationIPAddress; + ( void ) memcpy( ( void * ) ( pxEthernetHeader->xSourceAddress.ucBytes ), ( const void * ) ( pxEthernetHeader->xDestinationAddress.ucBytes ), ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); + } + else + { + /* Nothing to do: the buffer has been passed to DMA and will be released after use */ + } + } /* if( pxNetworkBuffer != NULL ) */ + } + /*-----------------------------------------------------------*/ + +/** + * @brief Create the TCP window for the given socket. + * + * @param[in] pxSocket: The socket for which the window is being created. + * + * @note The SYN event is very important: the sequence numbers, which have a kind of + * random starting value, are being synchronized. The sliding window manager + * (in FreeRTOS_TCP_WIN.c) needs to know them, along with the Maximum Segment + * Size (MSS). + */ + void prvTCPCreateWindow( FreeRTOS_Socket_t * pxSocket ) + { + uint32_t ulRxWindowSize = ( uint32_t ) pxSocket->u.xTCP.uxRxWinSize; + uint32_t ulTxWindowSize = ( uint32_t ) pxSocket->u.xTCP.uxTxWinSize; + + if( xTCPWindowLoggingLevel != 0 ) + { + FreeRTOS_debug_printf( ( "Limits (using): TCP Win size %u Water %u <= %u <= %u\n", + ( unsigned ) ( pxSocket->u.xTCP.uxRxWinSize * ipconfigTCP_MSS ), + ( unsigned ) pxSocket->u.xTCP.uxLittleSpace, + ( unsigned ) pxSocket->u.xTCP.uxEnoughSpace, + ( unsigned ) pxSocket->u.xTCP.uxRxStreamSize ) ); + } + + vTCPWindowCreate( + &pxSocket->u.xTCP.xTCPWindow, + ulRxWindowSize * ipconfigTCP_MSS, + ulTxWindowSize * ipconfigTCP_MSS, + pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber, + pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber, + ( uint32_t ) pxSocket->u.xTCP.usMSS ); + } + /*-----------------------------------------------------------*/ + +/** + * @brief Let ARP look-up the MAC-address of the peer and initialise the first SYN + * packet. + * + * @param[in] pxSocket: The socket owning the TCP connection. The first packet shall + * be created in this socket. + * + * @return pdTRUE: if the packet was successfully created and the first SYN can be sent. + * Else pdFALSE. + * + * @note Connecting sockets have a special state: eCONNECT_SYN. In this phase, + * the Ethernet address of the target will be found using ARP. In case the + * target IP address is not within the netmask, the hardware address of the + * gateway will be used. + */ + static BaseType_t prvTCPPrepareConnect( FreeRTOS_Socket_t * pxSocket ) + { + TCPPacket_t * pxTCPPacket; + IPHeader_t * pxIPHeader; + eARPLookupResult_t eReturned; + uint32_t ulRemoteIP; + MACAddress_t xEthAddress; + BaseType_t xReturn = pdTRUE; + uint32_t ulInitialSequenceNumber = 0; + + #if ( ipconfigHAS_PRINTF != 0 ) + { + /* Only necessary for nicer logging. */ + ( void ) memset( xEthAddress.ucBytes, 0, sizeof( xEthAddress.ucBytes ) ); + } + #endif /* ipconfigHAS_PRINTF != 0 */ + + ulRemoteIP = FreeRTOS_htonl( pxSocket->u.xTCP.ulRemoteIP ); + + /* Determine the ARP cache status for the requested IP address. */ + eReturned = eARPGetCacheEntry( &( ulRemoteIP ), &( xEthAddress ) ); + + switch( eReturned ) + { + case eARPCacheHit: /* An ARP table lookup found a valid entry. */ + break; /* We can now prepare the SYN packet. */ + + case eARPCacheMiss: /* An ARP table lookup did not find a valid entry. */ + case eCantSendPacket: /* There is no IP address, or an ARP is still in progress. */ + default: + /* Count the number of times it could not find the ARP address. */ + pxSocket->u.xTCP.ucRepCount++; + + FreeRTOS_debug_printf( ( "ARP for %xip (using %xip): rc=%d %02X:%02X:%02X %02X:%02X:%02X\n", + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, + ( unsigned ) FreeRTOS_htonl( ulRemoteIP ), + eReturned, + xEthAddress.ucBytes[ 0 ], + xEthAddress.ucBytes[ 1 ], + xEthAddress.ucBytes[ 2 ], + xEthAddress.ucBytes[ 3 ], + xEthAddress.ucBytes[ 4 ], + xEthAddress.ucBytes[ 5 ] ) ); + + /* And issue a (new) ARP request */ + FreeRTOS_OutputARPRequest( ulRemoteIP ); + xReturn = pdFALSE; + break; + } + + if( xReturn != pdFALSE ) + { + /* Get a difficult-to-predict initial sequence number for this 4-tuple. */ + ulInitialSequenceNumber = ulApplicationGetNextSequenceNumber( *ipLOCAL_IP_ADDRESS_POINTER, + pxSocket->usLocalPort, + pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort ); + + /* Check for a random number generation error. */ + if( ulInitialSequenceNumber == 0U ) + { + xReturn = pdFALSE; + } + } + + if( xReturn != pdFALSE ) + { + uint16_t usLength; + + /* The MAC-address of the peer (or gateway) has been found, + * now prepare the initial TCP packet and some fields in the socket. Map + * the buffer onto the TCPPacket_t struct to easily access it's field. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxTCPPacket = ( ( TCPPacket_t * ) pxSocket->u.xTCP.xPacket.u.ucLastPacket ); + pxIPHeader = &pxTCPPacket->xIPHeader; + + /* reset the retry counter to zero. */ + pxSocket->u.xTCP.ucRepCount = 0U; + + /* And remember that the connect/SYN data are prepared. */ + pxSocket->u.xTCP.bits.bConnPrepared = pdTRUE_UNSIGNED; + + /* Now that the Ethernet address is known, the initial packet can be + * prepared. */ + ( void ) memset( pxSocket->u.xTCP.xPacket.u.ucLastPacket, 0, sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) ); + + /* Write the Ethernet address in Source, because it will be swapped by + * prvTCPReturnPacket(). */ + ( void ) memcpy( ( void * ) ( &pxTCPPacket->xEthernetHeader.xSourceAddress ), ( const void * ) ( &xEthAddress ), sizeof( xEthAddress ) ); + + /* 'ipIPv4_FRAME_TYPE' is already in network-byte-order. */ + pxTCPPacket->xEthernetHeader.usFrameType = ipIPv4_FRAME_TYPE; + + pxIPHeader->ucVersionHeaderLength = 0x45U; + usLength = ( uint16_t ) ( sizeof( TCPPacket_t ) - sizeof( pxTCPPacket->xEthernetHeader ) ); + pxIPHeader->usLength = FreeRTOS_htons( usLength ); + pxIPHeader->ucTimeToLive = ( uint8_t ) ipconfigTCP_TIME_TO_LIVE; + + pxIPHeader->ucProtocol = ( uint8_t ) ipPROTOCOL_TCP; + + /* Addresses and ports will be stored swapped because prvTCPReturnPacket + * will swap them back while replying. */ + pxIPHeader->ulDestinationIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; + pxIPHeader->ulSourceIPAddress = FreeRTOS_htonl( pxSocket->u.xTCP.ulRemoteIP ); + + pxTCPPacket->xTCPHeader.usSourcePort = FreeRTOS_htons( pxSocket->u.xTCP.usRemotePort ); + pxTCPPacket->xTCPHeader.usDestinationPort = FreeRTOS_htons( pxSocket->usLocalPort ); + + /* We are actively connecting, so the peer's Initial Sequence Number (ISN) + * isn't known yet. */ + pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber = 0U; + + /* Start with ISN (Initial Sequence Number). */ + pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulInitialSequenceNumber; + + /* The TCP header size is 20 bytes, divided by 4 equals 5, which is put in + * the high nibble of the TCP offset field. */ + pxTCPPacket->xTCPHeader.ucTCPOffset = 0x50U; + + /* Only set the SYN flag. */ + pxTCPPacket->xTCPHeader.ucTCPFlags = tcpTCP_FLAG_SYN; + + /* Set the value of usMSS for this socket. */ + prvSocketSetMSS( pxSocket ); + + /* The initial sequence numbers at our side are known. Later + * vTCPWindowInit() will be called to fill in the peer's sequence numbers, but + * first wait for a SYN+ACK reply. */ + prvTCPCreateWindow( pxSocket ); + } + + return xReturn; + } + /*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN != 0 ) + +/** + * @brief Get the window scaling factor for the TCP connection. + * + * @param[in] pxSocket: The socket owning the TCP connection. + * + * @return The scaling factor. + */ + static uint8_t prvWinScaleFactor( const FreeRTOS_Socket_t * pxSocket ) + { + size_t uxWinSize; + uint8_t ucFactor; + + + /* 'xTCP.uxRxWinSize' is the size of the reception window in units of MSS. */ + uxWinSize = pxSocket->u.xTCP.uxRxWinSize * ( size_t ) pxSocket->u.xTCP.usMSS; + ucFactor = 0U; + + while( uxWinSize > 0xffffU ) + { + /* Divide by two and increase the binary factor by 1. */ + uxWinSize >>= 1; + ucFactor++; + } + + FreeRTOS_debug_printf( ( "prvWinScaleFactor: uxRxWinSize %u MSS %u Factor %u\n", + ( unsigned ) pxSocket->u.xTCP.uxRxWinSize, + pxSocket->u.xTCP.usMSS, + ucFactor ) ); + + return ucFactor; + } + + #endif /* if ( ipconfigUSE_TCP_WIN != 0 ) */ + /*-----------------------------------------------------------*/ + +/** + * @brief When opening a TCP connection, while SYN's are being sent, the parties may + * communicate what MSS (Maximum Segment Size) they intend to use, whether Selective + * ACK's ( SACK ) are supported, and the size of the reception window ( WSOPT ). + * + * @param[in] pxSocket: The socket being used for communication. It is used to set + * the MSS. + * @param[in,out] pxTCPHeader: The TCP packet header being used in the SYN transmission. + * The MSS and corresponding options shall be set in this + * header itself. + * + * @return The option length after the TCP header was updated. + * + * @note MSS is the net size of the payload, an is always smaller than MTU. + */ + UBaseType_t prvSetSynAckOptions( FreeRTOS_Socket_t * pxSocket, + TCPHeader_t * pxTCPHeader ) + { + uint16_t usMSS = pxSocket->u.xTCP.usMSS; + UBaseType_t uxOptionsLength; + + /* We send out the TCP Maximum Segment Size option with our SYN[+ACK]. */ + + pxTCPHeader->ucOptdata[ 0 ] = ( uint8_t ) tcpTCP_OPT_MSS; + pxTCPHeader->ucOptdata[ 1 ] = ( uint8_t ) tcpTCP_OPT_MSS_LEN; + pxTCPHeader->ucOptdata[ 2 ] = ( uint8_t ) ( usMSS >> 8 ); + pxTCPHeader->ucOptdata[ 3 ] = ( uint8_t ) ( usMSS & 0xffU ); + + #if ( ipconfigUSE_TCP_WIN != 0 ) + { + pxSocket->u.xTCP.ucMyWinScaleFactor = prvWinScaleFactor( pxSocket ); + + pxTCPHeader->ucOptdata[ 4 ] = tcpTCP_OPT_NOOP; + pxTCPHeader->ucOptdata[ 5 ] = ( uint8_t ) ( tcpTCP_OPT_WSOPT ); + pxTCPHeader->ucOptdata[ 6 ] = ( uint8_t ) ( tcpTCP_OPT_WSOPT_LEN ); + pxTCPHeader->ucOptdata[ 7 ] = ( uint8_t ) pxSocket->u.xTCP.ucMyWinScaleFactor; + uxOptionsLength = 8U; + } + #else + { + uxOptionsLength = 4U; + } + #endif /* if ( ipconfigUSE_TCP_WIN != 0 ) */ + + #if ( ipconfigUSE_TCP_WIN != 0 ) + { + pxTCPHeader->ucOptdata[ uxOptionsLength ] = tcpTCP_OPT_NOOP; + pxTCPHeader->ucOptdata[ uxOptionsLength + 1U ] = tcpTCP_OPT_NOOP; + pxTCPHeader->ucOptdata[ uxOptionsLength + 2U ] = tcpTCP_OPT_SACK_P; /* 4: Sack-Permitted Option. */ + pxTCPHeader->ucOptdata[ uxOptionsLength + 3U ] = 2U; /* 2: length of this option. */ + uxOptionsLength += 4U; + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ + return uxOptionsLength; /* bytes, not words. */ + } + +/** + * @brief Check if the size of a network buffer is big enough to hold the outgoing message. + * Allocate a new bigger network buffer when necessary. + * + * @param[in] pxSocket: Socket whose buffer is being resized. + * @param[in] pxNetworkBuffer: The network buffer whose size is being increased. + * @param[in] lDataLen: Length of the data to be put in the buffer. + * @param[in] uxOptionsLength: Length of options. + * + * @return If the resizing is successful: The new buffer with the size being asked for + * with old data copied in it. + * Else, NULL. + * + * @note The old network buffer will be released if the resizing is successful and + * cannot be used any longer. + */ + NetworkBufferDescriptor_t * prvTCPBufferResize( const FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t * pxNetworkBuffer, + int32_t lDataLen, + UBaseType_t uxOptionsLength ) + { + NetworkBufferDescriptor_t * pxReturn; + size_t uxNeeded; + BaseType_t xResize; + + if( xBufferAllocFixedSize != pdFALSE ) + { + /* Network buffers are created with a fixed size and can hold the largest + * MTU. */ + uxNeeded = ( size_t ) ipTOTAL_ETHERNET_FRAME_SIZE; + + /* and therefore, the buffer won't be too small. + * Only ask for a new network buffer in case none was supplied. */ + if( pxNetworkBuffer == NULL ) + { + xResize = pdTRUE; + } + else + { + xResize = pdFALSE; + } + } + else + { + /* Network buffers are created with a variable size. See if it must + * grow. */ + uxNeeded = ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength; + uxNeeded += ( size_t ) lDataLen; + + if( uxNeeded < sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) ) + { + uxNeeded = sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ); + } + + /* In case we were called from a TCP timer event, a buffer must be + * created. Otherwise, test 'xDataLength' of the provided buffer. */ + if( ( pxNetworkBuffer == NULL ) || ( pxNetworkBuffer->xDataLength < uxNeeded ) ) + { + xResize = pdTRUE; + } + else + { + xResize = pdFALSE; + } + } + + if( xResize != pdFALSE ) + { + /* The caller didn't provide a network buffer or the provided buffer is + * too small. As we must send-out a data packet, a buffer will be created + * here. */ + pxReturn = pxGetNetworkBufferWithDescriptor( uxNeeded, 0U ); + + if( pxReturn != NULL ) + { + /* Set the actual packet size, in case the returned buffer is larger. */ + pxReturn->xDataLength = uxNeeded; + + /* Copy the existing data to the new created buffer. */ + if( pxNetworkBuffer != NULL ) + { + /* Either from the previous buffer... */ + ( void ) memcpy( pxReturn->pucEthernetBuffer, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); + + /* ...and release it. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + else + { + /* Or from the socket field 'xTCP.xPacket'. */ + ( void ) memcpy( pxReturn->pucEthernetBuffer, pxSocket->u.xTCP.xPacket.u.ucLastPacket, sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) ); + } + } + } + else + { + /* xResize is false, the network buffer provided was big enough. */ + configASSERT( pxNetworkBuffer != NULL ); /* LCOV_EXCL_BR_LINE this branch will not be covered, since it would never be NULL. to tell lint: when xResize is false, pxNetworkBuffer is not NULL. */ + pxReturn = pxNetworkBuffer; + + pxNetworkBuffer->xDataLength = ( size_t ) ( ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength ) + ( size_t ) lDataLen; + } + + return pxReturn; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Prepare an outgoing message, in case anything has to be sent. + * + * @param[in] pxSocket: The socket owning the connection. + * @param[in,out] ppxNetworkBuffer: Pointer to the pointer to the network buffer. + * @param[in] uxOptionsLength: The length of the TCP options. + * + * @return Length of the data to be sent if everything is correct. Else, -1 + * is returned in case of any error. + */ + int32_t prvTCPPrepareSend( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer, + UBaseType_t uxOptionsLength ) + { + int32_t lDataLen; + uint8_t * pucEthernetBuffer, * pucSendData; + ProtocolHeaders_t * pxProtocolHeaders; + size_t uxOffset; + uint32_t ulDataGot, ulDistance; + TCPWindow_t * pxTCPWindow; + NetworkBufferDescriptor_t * pxNewBuffer; + int32_t lStreamPos; + UBaseType_t uxIntermediateResult = 0; + + if( ( *ppxNetworkBuffer ) != NULL ) + { + /* A network buffer descriptor was already supplied */ + pucEthernetBuffer = ( *ppxNetworkBuffer )->pucEthernetBuffer; + } + else + { + /* For now let it point to the last packet header */ + pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket; + } + + /* Map the ethernet buffer onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxProtocolHeaders = ( ( ProtocolHeaders_t * ) &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) ] ) ); + pxTCPWindow = &( pxSocket->u.xTCP.xTCPWindow ); + lDataLen = 0; + lStreamPos = 0; + pxProtocolHeaders->xTCPHeader.ucTCPFlags |= tcpTCP_FLAG_ACK; + + if( pxSocket->u.xTCP.txStream != NULL ) + { + /* ulTCPWindowTxGet will return the amount of data which may be sent + * along with the position in the txStream. + * Why check for MSS > 1 ? + * Because some TCP-stacks (like uIP) use it for flow-control. */ + if( pxSocket->u.xTCP.usMSS > 1U ) + { + lDataLen = ( int32_t ) ulTCPWindowTxGet( pxTCPWindow, pxSocket->u.xTCP.ulWindowSize, &lStreamPos ); + } + + if( lDataLen > 0 ) + { + /* Check if the current network buffer is big enough, if not, + * resize it. */ + pxNewBuffer = prvTCPBufferResize( pxSocket, *ppxNetworkBuffer, lDataLen, uxOptionsLength ); + + if( pxNewBuffer != NULL ) + { + *ppxNetworkBuffer = pxNewBuffer; + pucEthernetBuffer = pxNewBuffer->pucEthernetBuffer; + + /* Map the byte stream onto ProtocolHeaders_t struct for easy + * access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxProtocolHeaders = ( ( ProtocolHeaders_t * ) &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) ] ) ); + + pucSendData = &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength ] ); + + /* Translate the position in txStream to an offset from the tail + * marker. */ + uxOffset = uxStreamBufferDistance( pxSocket->u.xTCP.txStream, pxSocket->u.xTCP.txStream->uxTail, ( size_t ) lStreamPos ); + + /* Here data is copied from the txStream in 'peek' mode. Only + * when the packets are acked, the tail marker will be updated. */ + ulDataGot = ( uint32_t ) uxStreamBufferGet( pxSocket->u.xTCP.txStream, uxOffset, pucSendData, ( size_t ) lDataLen, pdTRUE ); + + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + if( ulDataGot != ( uint32_t ) lDataLen ) + { + FreeRTOS_debug_printf( ( "uxStreamBufferGet: pos %d offs %u only %u != %d\n", + ( int ) lStreamPos, ( unsigned ) uxOffset, ( unsigned ) ulDataGot, ( int ) lDataLen ) ); + } + } + #endif + + /* If the owner of the socket requests a closure, add the FIN + * flag to the last packet. */ + if( pxSocket->u.xTCP.bits.bCloseRequested != pdFALSE_UNSIGNED ) + { + ulDistance = ( uint32_t ) uxStreamBufferDistance( pxSocket->u.xTCP.txStream, ( size_t ) lStreamPos, pxSocket->u.xTCP.txStream->uxHead ); + + if( ulDistance == ulDataGot ) + { + #if ( ipconfigHAS_DEBUG_PRINTF == 1 ) + { + /* the order of volatile accesses is undefined + * so such workaround */ + size_t uxHead = pxSocket->u.xTCP.txStream->uxHead; + size_t uxMid = pxSocket->u.xTCP.txStream->uxMid; + size_t uxTail = pxSocket->u.xTCP.txStream->uxTail; + + FreeRTOS_debug_printf( ( "CheckClose %u <= %u (%u <= %u <= %u)\n", + ( unsigned ) ulDataGot, ( unsigned ) ulDistance, + ( unsigned ) uxTail, ( unsigned ) uxMid, ( unsigned ) uxHead ) ); + } + #endif /* if ( ipconfigHAS_DEBUG_PRINTF == 1 ) */ + + /* Although the socket sends a FIN, it will stay in + * ESTABLISHED until all current data has been received or + * delivered. */ + pxProtocolHeaders->xTCPHeader.ucTCPFlags |= tcpTCP_FLAG_FIN; + pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->ulOurSequenceNumber + ( uint32_t ) lDataLen; + pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED; + } + } + } + else + { + lDataLen = -1; + } + } + } + + if( ( lDataLen >= 0 ) && ( pxSocket->u.xTCP.eTCPState == eESTABLISHED ) ) + { + /* See if the socket owner wants to shutdown this connection. */ + if( ( pxSocket->u.xTCP.bits.bUserShutdown != pdFALSE_UNSIGNED ) && + ( xTCPWindowTxDone( pxTCPWindow ) != pdFALSE ) ) + { + pxSocket->u.xTCP.bits.bUserShutdown = pdFALSE_UNSIGNED; + pxProtocolHeaders->xTCPHeader.ucTCPFlags |= tcpTCP_FLAG_FIN; + pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED; + pxSocket->u.xTCP.bits.bWinChange = pdTRUE_UNSIGNED; + pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber; + vTCPStateChange( pxSocket, eFIN_WAIT_1 ); + } + + #if ( ipconfigTCP_KEEP_ALIVE != 0 ) + { + if( pxSocket->u.xTCP.ucKeepRepCount > 3U ) /*_RB_ Magic number. */ + { + FreeRTOS_debug_printf( ( "keep-alive: giving up %xip:%u\n", + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, /* IP address of remote machine. */ + pxSocket->u.xTCP.usRemotePort ) ); /* Port on remote machine. */ + vTCPStateChange( pxSocket, eCLOSE_WAIT ); + lDataLen = -1; + } + + if( ( lDataLen == 0 ) && ( pxSocket->u.xTCP.bits.bWinChange == pdFALSE_UNSIGNED ) ) + { + /* If there is no data to be sent, and no window-update message, + * we might want to send a keep-alive message. */ + TickType_t xAge = xTaskGetTickCount() - pxSocket->u.xTCP.xLastAliveTime; + TickType_t xMax; + xMax = ( ( TickType_t ) ipconfigTCP_KEEP_ALIVE_INTERVAL * ( TickType_t ) configTICK_RATE_HZ ); + + if( pxSocket->u.xTCP.ucKeepRepCount != 0U ) + { + xMax = 3U * configTICK_RATE_HZ; + } + + if( xAge > xMax ) + { + pxSocket->u.xTCP.xLastAliveTime = xTaskGetTickCount(); + + if( xTCPWindowLoggingLevel != 0 ) + { + FreeRTOS_debug_printf( ( "keep-alive: %xip:%u count %u\n", + ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort, + pxSocket->u.xTCP.ucKeepRepCount ) ); + } + + pxSocket->u.xTCP.bits.bSendKeepAlive = pdTRUE_UNSIGNED; + pxSocket->u.xTCP.usTimeout = ( ( uint16_t ) pdMS_TO_TICKS( 2500U ) ); + pxSocket->u.xTCP.ucKeepRepCount++; + } + } + } + #endif /* ipconfigTCP_KEEP_ALIVE */ + } + + if( lDataLen >= 0 ) + { + /* Anything to send, a change of the advertised window size, or maybe send a + * keep-alive message? */ + if( ( lDataLen > 0 ) || + ( pxSocket->u.xTCP.bits.bWinChange != pdFALSE_UNSIGNED ) || + ( pxSocket->u.xTCP.bits.bSendKeepAlive != pdFALSE_UNSIGNED ) ) + { + pxProtocolHeaders->xTCPHeader.ucTCPFlags &= ( ( uint8_t ) ~tcpTCP_FLAG_PSH ); + pxProtocolHeaders->xTCPHeader.ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); /*_RB_ "2" needs comment. */ + + pxProtocolHeaders->xTCPHeader.ucTCPFlags |= ( uint8_t ) tcpTCP_FLAG_ACK; + + if( lDataLen != 0L ) + { + pxProtocolHeaders->xTCPHeader.ucTCPFlags |= ( uint8_t ) tcpTCP_FLAG_PSH; + } + + uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength; + lDataLen += ( int32_t ) uxIntermediateResult; + } + } + + return lDataLen; + } + /*-----------------------------------------------------------*/ + + +/** + * @brief The API FreeRTOS_send() adds data to the TX stream. Add + * this data to the windowing system to it can be transmitted. + * + * @param[in] pxSocket: The socket owning the connection. + */ + void prvTCPAddTxData( FreeRTOS_Socket_t * pxSocket ) + { + int32_t lCount, lLength; + + /* A txStream has been created already, see if the socket has new data for + * the sliding window. + * + * uxStreamBufferMidSpace() returns the distance between rxHead and rxMid. It + * contains new Tx data which has not been passed to the sliding window yet. + * The oldest data not-yet-confirmed can be found at rxTail. */ + lLength = ( int32_t ) uxStreamBufferMidSpace( pxSocket->u.xTCP.txStream ); + + if( lLength > 0 ) + { + /* All data between txMid and rxHead will now be passed to the sliding + * window manager, so it can start transmitting them. + * + * Hand over the new data to the sliding window handler. It will be + * split-up in chunks of 1460 bytes each (or less, depending on + * ipconfigTCP_MSS). */ + lCount = lTCPWindowTxAdd( &pxSocket->u.xTCP.xTCPWindow, + ( uint32_t ) lLength, + ( int32_t ) pxSocket->u.xTCP.txStream->uxMid, + ( int32_t ) pxSocket->u.xTCP.txStream->LENGTH ); + + /* Move the rxMid pointer forward up to rxHead. */ + if( lCount > 0 ) + { + vStreamBufferMoveMid( pxSocket->u.xTCP.txStream, ( size_t ) lCount ); + } + } + } + /*-----------------------------------------------------------*/ + + +/** + * @brief Set the TCP options (if any) for the outgoing packet. + * + * @param[in] pxSocket: The socket owning the connection. + * @param[in] pxNetworkBuffer: The network buffer holding the packet. + * + * @return Length of the TCP options after they are set. + */ + UBaseType_t prvSetOptions( FreeRTOS_Socket_t * pxSocket, + const NetworkBufferDescriptor_t * pxNetworkBuffer ) + { + /* Map the ethernet buffer onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * ) + &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( pxNetworkBuffer ) ] ) ); + TCPHeader_t * pxTCPHeader = &pxProtocolHeaders->xTCPHeader; + const TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; + UBaseType_t uxOptionsLength = pxTCPWindow->ucOptionLength; + + #if ( ipconfigUSE_TCP_WIN == 1 ) + /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + if( uxOptionsLength != 0U ) + { + /* TCP options must be sent because a packet which is out-of-order + * was received. */ + if( xTCPWindowLoggingLevel >= 0 ) + { + FreeRTOS_debug_printf( ( "SACK[%u,%u]: optlen %u sending %u - %u\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + ( unsigned ) uxOptionsLength, + ( unsigned ) ( FreeRTOS_ntohl( pxTCPWindow->ulOptionsData[ 1 ] ) - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber ), + ( unsigned ) ( FreeRTOS_ntohl( pxTCPWindow->ulOptionsData[ 2 ] ) - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber ) ) ); + } + + /* + * Use helper variables for memcpy() source & dest to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = pxTCPWindow->ulOptionsData; + pvCopyDest = pxTCPHeader->ucOptdata; + ( void ) memcpy( pvCopyDest, pvCopySource, ( size_t ) uxOptionsLength ); + + /* The header length divided by 4, goes into the higher nibble, + * effectively a shift-left 2. */ + pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + } + else + #endif /* ipconfigUSE_TCP_WIN */ + + if( ( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) && ( pxSocket->u.xTCP.bits.bMssChange != pdFALSE_UNSIGNED ) ) + { + /* TCP options must be sent because the MSS has changed. */ + pxSocket->u.xTCP.bits.bMssChange = pdFALSE_UNSIGNED; + + if( xTCPWindowLoggingLevel >= 0 ) + { + FreeRTOS_debug_printf( ( "MSS: sending %u\n", pxSocket->u.xTCP.usMSS ) ); + } + + pxTCPHeader->ucOptdata[ 0 ] = tcpTCP_OPT_MSS; + pxTCPHeader->ucOptdata[ 1 ] = tcpTCP_OPT_MSS_LEN; + pxTCPHeader->ucOptdata[ 2 ] = ( uint8_t ) ( ( pxSocket->u.xTCP.usMSS ) >> 8 ); + pxTCPHeader->ucOptdata[ 3 ] = ( uint8_t ) ( ( pxSocket->u.xTCP.usMSS ) & 0xffU ); + uxOptionsLength = 4U; + pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + } + else + { + /* Nothing. */ + } + + return uxOptionsLength; + } + /*-----------------------------------------------------------*/ + + +/** + * @brief Called from prvTCPHandleState(). There is data to be sent. If + * ipconfigUSE_TCP_WIN is defined, and if only an ACK must be sent, it will be + * checked if it would better be postponed for efficiency. + * + * @param[in] pxSocket: The socket owning the TCP connection. + * @param[in] ppxNetworkBuffer: Pointer to pointer to the network buffer. + * @param[in] ulReceiveLength: The length of the received buffer. + * @param[in] xByteCount: Length of the data to be sent. + * + * @return The number of bytes actually sent. + */ + BaseType_t prvSendData( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer, + uint32_t ulReceiveLength, + BaseType_t xByteCount ) + { + /* Map the buffer onto the ProtocolHeader_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * ) + &( ( *ppxNetworkBuffer )->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( *ppxNetworkBuffer ) ] ) ); + const TCPHeader_t * pxTCPHeader = &pxProtocolHeaders->xTCPHeader; + const TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; + /* Find out what window size we may advertised. */ + int32_t lRxSpace; + BaseType_t xSendLength = xByteCount; + uint32_t ulRxBufferSpace; + /* Two steps to please MISRA. */ + size_t uxSize = ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER; + BaseType_t xSizeWithoutData = ( BaseType_t ) uxSize; + + #if ( ipconfigUSE_TCP_WIN == 1 ) + int32_t lMinLength; + #endif + + /* Set the time-out field, so that we'll be called by the IP-task in case no + * next message will be received. */ + ulRxBufferSpace = pxSocket->u.xTCP.ulHighestRxAllowed - pxTCPWindow->rx.ulCurrentSequenceNumber; + lRxSpace = ( int32_t ) ulRxBufferSpace; + + #if ipconfigUSE_TCP_WIN == 1 + { + /* An ACK may be delayed if the peer has space for at least 2 x MSS. */ + lMinLength = ( ( int32_t ) 2 ) * ( ( int32_t ) pxSocket->u.xTCP.usMSS ); + + /* In case we're receiving data continuously, we might postpone sending + * an ACK to gain performance. */ + /* lint e9007 is OK because 'uxIPHeaderSizeSocket()' has no side-effects. */ + if( ( ulReceiveLength > 0U ) && /* Data was sent to this socket. */ + ( lRxSpace >= lMinLength ) && /* There is Rx space for more data. */ + ( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED ) && /* Not in a closure phase. */ + ( xSendLength == xSizeWithoutData ) && /* No Tx data or options to be sent. */ + ( pxSocket->u.xTCP.eTCPState == eESTABLISHED ) && /* Connection established. */ + ( pxTCPHeader->ucTCPFlags == tcpTCP_FLAG_ACK ) ) /* There are no other flags than an ACK. */ + { + uint32_t ulCurMSS = ( uint32_t ) pxSocket->u.xTCP.usMSS; + + if( pxSocket->u.xTCP.pxAckMessage != *ppxNetworkBuffer ) + { + /* There was still a delayed in queue, delete it. */ + if( pxSocket->u.xTCP.pxAckMessage != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); + } + + pxSocket->u.xTCP.pxAckMessage = *ppxNetworkBuffer; + } + + if( ulReceiveLength < ulCurMSS ) /* Received a small message. */ + { + pxSocket->u.xTCP.usTimeout = ( uint16_t ) tcpDELAYED_ACK_SHORT_DELAY_MS; + } + else + { + /* Normally a delayed ACK should wait 200 ms for a next incoming + * packet. Only wait 20 ms here to gain performance. A slow ACK + * for full-size message. */ + pxSocket->u.xTCP.usTimeout = ( uint16_t ) pdMS_TO_TICKS( tcpDELAYED_ACK_LONGER_DELAY_MS ); + + if( pxSocket->u.xTCP.usTimeout < 1U ) /* LCOV_EXCL_BR_LINE, the second branch will never be hit */ + { + pxSocket->u.xTCP.usTimeout = 1U; /* LCOV_EXCL_LINE, this line will not be reached */ + } + } + + if( ( xTCPWindowLoggingLevel > 1 ) && ( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) ) ) + { + FreeRTOS_debug_printf( ( "Send[%u->%u] del ACK %u SEQ %u (len %u) tmout %u d %d\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + ( unsigned ) ( pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ), + ( unsigned ) ( pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) xSendLength, + pxSocket->u.xTCP.usTimeout, + ( int ) lRxSpace ) ); + } + + *ppxNetworkBuffer = NULL; + xSendLength = 0; + } + else if( pxSocket->u.xTCP.pxAckMessage != NULL ) + { + /* As an ACK is not being delayed, remove any earlier delayed ACK + * message. */ + if( pxSocket->u.xTCP.pxAckMessage != *ppxNetworkBuffer ) + { + vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); + } + + pxSocket->u.xTCP.pxAckMessage = NULL; + } + else + { + /* The ack will not be postponed, and there was no stored ack ( in 'pxAckMessage' ). */ + } + } + #else /* if ipconfigUSE_TCP_WIN == 1 */ + { + /* Remove compiler warnings. */ + ( void ) ulReceiveLength; + ( void ) pxTCPHeader; + ( void ) lRxSpace; + } + #endif /* ipconfigUSE_TCP_WIN */ + + if( xSendLength != 0 ) + { + if( ( xTCPWindowLoggingLevel > 1 ) && ( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) ) ) + { + FreeRTOS_debug_printf( ( "Send[%u->%u] imm ACK %u SEQ %u (len %u)\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + ( unsigned ) ( pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ), + ( unsigned ) ( pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) xSendLength ) ); + } + + /* Set the parameter 'xReleaseAfterSend' to the value of + * ipconfigZERO_COPY_TX_DRIVER. */ + prvTCPReturnPacket( pxSocket, *ppxNetworkBuffer, ( uint32_t ) xSendLength, ipconfigZERO_COPY_TX_DRIVER ); + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* The driver has taken ownership of the Network Buffer. */ + *ppxNetworkBuffer = NULL; + } + #endif + } + + return xSendLength; + } + /*-----------------------------------------------------------*/ + +/** + * @brief Common code for sending a TCP protocol control packet (i.e. no options, no + * payload, just flags). + * + * @param[in] pxNetworkBuffer: The network buffer received from the peer. + * @param[in] ucTCPFlags: The flags to determine what kind of packet this is. + * + * @return pdFAIL always indicating that the packet was not consumed. + */ + static BaseType_t prvTCPSendSpecialPacketHelper( NetworkBufferDescriptor_t * pxNetworkBuffer, + uint8_t ucTCPFlags ) + { + #if ( ipconfigIGNORE_UNKNOWN_PACKETS == 1 ) + /* Configured to ignore unknown packets just suppress a compiler warning. */ + ( void ) pxNetworkBuffer; + ( void ) ucTCPFlags; + #else + { + /* Map the ethernet buffer onto the TCPPacket_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + TCPPacket_t * pxTCPPacket = ( ( TCPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); + const uint32_t ulSendLength = + ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ); /* Plus 0 options. */ + + pxTCPPacket->xTCPHeader.ucTCPFlags = ucTCPFlags; + pxTCPPacket->xTCPHeader.ucTCPOffset = ( ipSIZE_OF_TCP_HEADER ) << 2; + + prvTCPReturnPacket( NULL, pxNetworkBuffer, ulSendLength, pdFALSE ); + } + #endif /* !ipconfigIGNORE_UNKNOWN_PACKETS */ + + /* The packet was not consumed. */ + return pdFAIL; + } + /*-----------------------------------------------------------*/ + +/** + * @brief A "challenge ACK" is as per https://tools.ietf.org/html/rfc5961#section-3.2, + * case #3. In summary, an RST was received with a sequence number that is + * unexpected but still within the window. + * + * @param[in] pxNetworkBuffer: The network buffer descriptor with the packet. + * + * @return Returns the value back from #prvTCPSendSpecialPacketHelper. + */ + BaseType_t prvTCPSendChallengeAck( NetworkBufferDescriptor_t * pxNetworkBuffer ) + { + return prvTCPSendSpecialPacketHelper( pxNetworkBuffer, tcpTCP_FLAG_ACK ); + } + /*-----------------------------------------------------------*/ + +/** + * @brief Send a RST (Reset) to peer in case the packet cannot be handled. + * + * @param[in] pxNetworkBuffer: The network buffer descriptor with the packet. + * + * @return Returns the value back from #prvTCPSendSpecialPacketHelper. + */ + BaseType_t prvTCPSendReset( NetworkBufferDescriptor_t * pxNetworkBuffer ) + { + return prvTCPSendSpecialPacketHelper( pxNetworkBuffer, + ( uint8_t ) tcpTCP_FLAG_ACK | ( uint8_t ) tcpTCP_FLAG_RST ); + } + /*-----------------------------------------------------------*/ + +#endif /* ipconfigUSE_TCP == 1 */ diff --git a/FreeRTOS/source/FreeRTOS_TCP_Utils.c b/FreeRTOS/source/FreeRTOS_TCP_Utils.c new file mode 100644 index 0000000..7a0bdb8 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_TCP_Utils.c @@ -0,0 +1,118 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_TCP_Utils.c + * @brief Module contains utility functions used by FreeRTOS+TCP module. + * + * Endianness: in this module all ports and IP addresses are stored in + * host byte-order, except fields in the IP-packets + */ +/* Standard includes. */ +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" + +#include "FreeRTOS_TCP_Utils.h" + +/* Just make sure the contents doesn't get compiled if TCP is not enabled. */ +#if ipconfigUSE_TCP == 1 + + +/* For logging and debugging: make a string showing the TCP flags + */ + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + +/** + * @brief Print out the value of flags in a human readable manner. + * + * @param[in] xFlags: The TCP flags. + * + * @return The string containing the flags. + */ + + static char retString[ 10 ]; + const char * prvTCPFlagMeaning( UBaseType_t xFlags ) + { + size_t uxFlags = ( size_t ) xFlags; + + ( void ) snprintf( retString, + sizeof( retString ), "%c%c%c%c%c%c%c%c", + ( ( uxFlags & ( size_t ) tcpTCP_FLAG_FIN ) != 0 ) ? 'F' : '.', /* 0x0001: No more data from sender */ + ( ( uxFlags & ( size_t ) tcpTCP_FLAG_SYN ) != 0 ) ? 'S' : '.', /* 0x0002: Synchronize sequence numbers */ + ( ( uxFlags & ( size_t ) tcpTCP_FLAG_RST ) != 0 ) ? 'R' : '.', /* 0x0004: Reset the connection */ + ( ( uxFlags & ( size_t ) tcpTCP_FLAG_PSH ) != 0 ) ? 'P' : '.', /* 0x0008: Push function: please push buffered data to the recv application */ + ( ( uxFlags & ( size_t ) tcpTCP_FLAG_ACK ) != 0 ) ? 'A' : '.', /* 0x0010: Acknowledgment field is significant */ + ( ( uxFlags & ( size_t ) tcpTCP_FLAG_URG ) != 0 ) ? 'U' : '.', /* 0x0020: Urgent pointer field is significant */ + ( ( uxFlags & ( size_t ) tcpTCP_FLAG_ECN ) != 0 ) ? 'E' : '.', /* 0x0040: ECN-Echo */ + ( ( uxFlags & ( size_t ) tcpTCP_FLAG_CWR ) != 0 ) ? 'C' : '.' ); /* 0x0080: Congestion Window Reduced */ + return retString; + } + /*-----------------------------------------------------------*/ + + #endif /* ipconfigHAS_DEBUG_PRINTF */ + +/** + * @brief Set the MSS (Maximum segment size) associated with the given socket. + * + * @param[in] pxSocket: The socket whose MSS is to be set. + */ + void prvSocketSetMSS( FreeRTOS_Socket_t * pxSocket ) + { + uint32_t ulMSS; + + /* Do not allow MSS smaller than tcpMINIMUM_SEGMENT_LENGTH. */ + #if ( ipconfigTCP_MSS >= tcpMINIMUM_SEGMENT_LENGTH ) + { + ulMSS = ipconfigTCP_MSS; + } + #else + { + ulMSS = tcpMINIMUM_SEGMENT_LENGTH; + } + #endif + + if( ( ( FreeRTOS_ntohl( pxSocket->u.xTCP.ulRemoteIP ) ^ *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) != 0U ) + { + /* Data for this peer will pass through a router, and maybe through + * the internet. Limit the MSS to 1400 bytes or less. */ + ulMSS = FreeRTOS_min_uint32( ( uint32_t ) tcpREDUCED_MSS_THROUGH_INTERNET, ulMSS ); + } + + FreeRTOS_debug_printf( ( "prvSocketSetMSS: %u bytes for %xip:%u\n", ( unsigned ) ulMSS, ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, pxSocket->u.xTCP.usRemotePort ) ); + + pxSocket->u.xTCP.usMSS = ( uint16_t ) ulMSS; + } + /*-----------------------------------------------------------*/ + +#endif /* ipconfigUSE_TCP == 1 */ diff --git a/FreeRTOS/source/FreeRTOS_TCP_WIN.c b/FreeRTOS/source/FreeRTOS_TCP_WIN.c new file mode 100644 index 0000000..d09b83b --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_TCP_WIN.c @@ -0,0 +1,2229 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + + +/** + * @file FreeRTOS_TCP_WIN.c + * @brief Module which handles the TCP windowing schemes for FreeRTOS+TCP. Many + * functions have two versions - one for FreeRTOS+TCP (full) and one for + * FreeRTOS+TCP (lite). + * + * In this module all ports and IP addresses and sequence numbers are + * being stored in host byte-order. + */ + +/* Standard includes. */ +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" + +#if ( ipconfigUSE_TCP == 1 ) + +/* Constants used for Smoothed Round Trip Time (SRTT). */ + #define winSRTT_INCREMENT_NEW 2 /**< New increment for the smoothed RTT. */ + #define winSRTT_INCREMENT_CURRENT 6 /**< Current increment for the smoothed RTT. */ + #define winSRTT_DECREMENT_NEW 1 /**< New decrement for the smoothed RTT. */ + #define winSRTT_DECREMENT_CURRENT 7 /**< Current decrement for the smoothed RTT. */ + #define winSRTT_CAP_mS ( ipconfigTCP_SRTT_MINIMUM_VALUE_MS ) /**< Cap in milliseconds. */ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** @brief Create a new Rx window. */ + #define xTCPWindowRxNew( pxWindow, ulSequenceNumber, lCount ) xTCPWindowNew( pxWindow, ulSequenceNumber, lCount, pdTRUE ) + +/** @brief Create a new Tx window. */ + #define xTCPWindowTxNew( pxWindow, ulSequenceNumber, lCount ) xTCPWindowNew( pxWindow, ulSequenceNumber, lCount, pdFALSE ) + +/** @brief The code to send a single Selective ACK (SACK): + * NOP (0x01), NOP (0x01), SACK (0x05), LEN (0x0a), + * followed by a lower and a higher sequence number, + * where LEN is 2 + 2*4 = 10 bytes. */ + #if ( ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN ) + #define OPTION_CODE_SINGLE_SACK ( 0x0101050aU ) + #else + #define OPTION_CODE_SINGLE_SACK ( 0x0a050101U ) + #endif + +/** @brief Normal retransmission: + * A packet will be retransmitted after a Retransmit Time-Out (RTO). + * Fast retransmission: + * When 3 packets with a higher sequence number have been acknowledged + * by the peer, it is very unlikely a current packet will ever arrive. + * It will be retransmitted far before the RTO. + */ + #define DUPLICATE_ACKS_BEFORE_FAST_RETRANSMIT ( 3U ) + +/** @brief If there have been several retransmissions (4), decrease the + * size of the transmission window to at most 2 times MSS. + */ + #define MAX_TRANSMIT_COUNT_USING_LARGE_WINDOW ( 4U ) + + #endif /* configUSE_TCP_WIN */ +/*-----------------------------------------------------------*/ + + static void vListInsertGeneric( List_t * const pxList, + ListItem_t * const pxNewListItem, + MiniListItem_t * pxWhere ); + +/* + * All TCP sockets share a pool of segment descriptors (TCPSegment_t) + * Available descriptors are stored in the 'xSegmentList' + * When a socket owns a descriptor, it will either be stored in + * 'xTxSegments' or 'xRxSegments' + * As soon as a package has been confirmed, the descriptor will be returned + * to the segment pool + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static BaseType_t prvCreateSectors( void ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * Find a segment with a given sequence number in the list of received + * segments: 'pxWindow->xRxSegments'. + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static TCPSegment_t * xTCPWindowRxFind( const TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * Allocate a new segment + * The socket will borrow all segments from a common pool: 'xSegmentList', + * which is a list of 'TCPSegment_t' + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static TCPSegment_t * xTCPWindowNew( TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber, + int32_t lCount, + BaseType_t xIsForRx ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * Detaches and returns the head of a queue + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static TCPSegment_t * xTCPWindowGetHead( const List_t * pxList ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * Returns the head of a queue but it won't be detached + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static TCPSegment_t * xTCPWindowPeekHead( const List_t * pxList ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * Free entry pxSegment because it's not used anymore + * The ownership will be passed back to the segment pool + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static void vTCPWindowFree( TCPSegment_t * pxSegment ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * A segment has been received with sequence number 'ulSequenceNumber', where + * 'ulCurrentSequenceNumber == ulSequenceNumber', which means that exactly this + * segment was expected. xTCPWindowRxConfirm() will check if there is already + * another segment with a sequence number between (ulSequenceNumber) and + * (ulSequenceNumber+xLength). Normally none will be found, because the next Rx + * segment should have a sequence number equal to '(ulSequenceNumber+xLength)'. + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static TCPSegment_t * xTCPWindowRxConfirm( const TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber, + uint32_t ulLength ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * FreeRTOS+TCP stores data in circular buffers. Calculate the next position to + * store. + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static int32_t lTCPIncrementTxPosition( int32_t lPosition, + int32_t lMax, + int32_t lCount ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * This function will look if there is new transmission data. It will return + * true if there is data to be sent. + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t const * pxWindow, + uint32_t ulWindowSize ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * An acknowledge was received. See if some outstanding data may be removed + * from the transmission queue(s). + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static uint32_t prvTCPWindowTxCheckAck( TCPWindow_t * pxWindow, + uint32_t ulFirst, + uint32_t ulLast ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/* + * A higher Tx block has been acknowledged. Now iterate through the xWaitQueue + * to find a possible condition for a FAST retransmission. + */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static uint32_t prvTCPWindowFastRetransmit( TCPWindow_t * pxWindow, + uint32_t ulFirst ); + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/*-----------------------------------------------------------*/ + +/**< TCP segment pool. */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + static TCPSegment_t * xTCPSegments = NULL; + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/**< List of free TCP segments. */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + _static List_t xSegmentList; + #endif + + #if ( ipconfigUSE_TCP_WIN == 1 ) +/** @brief Logging verbosity level. */ + BaseType_t xTCPWindowLoggingLevel = 0; + #endif + + #if ( ipconfigUSE_TCP_WIN == 1 ) + /* Some 32-bit arithmetic: comparing sequence numbers */ + static portINLINE BaseType_t xSequenceLessThanOrEqual( uint32_t a, + uint32_t b ); + +/** + * @brief Check if a <= b. + * + * @param[in] a: The value on the left-hand side. + * @param[in] b: The value on the right-hand side. + * + * @return pdTRUE when "( b - a ) < 0x80000000". Else, pdFALSE. + */ + static portINLINE BaseType_t xSequenceLessThanOrEqual( uint32_t a, + uint32_t b ) + { + BaseType_t xResult = pdFALSE; + + /* Test if a <= b + * Return true if the unsigned subtraction of (b-a) doesn't generate an + * arithmetic overflow. */ + if( ( ( b - a ) & 0x80000000U ) == 0U ) + { + xResult = pdTRUE; + } + + return xResult; + } + + #endif /* ipconfigUSE_TCP_WIN */ +/*-----------------------------------------------------------*/ + +/** + * @brief Check if a < b. + * + * @param[in] a: The value on the left-hand side. + * @param[in] b: The value on the right-hand side. + * + * @return pdTRUE when "( b - ( a + 1 ) ) < 0x80000000", else pdFALSE. + */ + BaseType_t xSequenceLessThan( uint32_t a, + uint32_t b ) + { + BaseType_t xResult = pdFALSE; + + /* Test if a < b */ + if( ( ( b - ( a + 1U ) ) & 0x80000000U ) == 0U ) + { + xResult = pdTRUE; + } + + return xResult; + } + +/*-----------------------------------------------------------*/ + +/** + * @brief Check if a > b. + * + * @param[in] a: The value on the left-hand side. + * @param[in] b: The value on the right-hand side. + * + * @return pdTRUE when "( a - b ) < 0x80000000", else pdFALSE. + */ + BaseType_t xSequenceGreaterThan( uint32_t a, + uint32_t b ) + { + BaseType_t xResult = pdFALSE; + + /* Test if a > b */ + if( ( ( a - ( b + 1U ) ) & 0x80000000U ) == 0U ) + { + xResult = pdTRUE; + } + + return xResult; + } + + +/*-----------------------------------------------------------*/ + static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a, + uint32_t b ); + +/** + * @brief Test if a>=b. This function is required since the sequence numbers can roll over. + * + * @param[in] a: The first sequence number. + * @param[in] b: The second sequence number. + * + * @return pdTRUE if a>=b, else pdFALSE. + */ + static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a, + uint32_t b ) + { + BaseType_t xResult = pdFALSE; + + /* Test if a >= b */ + if( ( ( a - b ) & 0x80000000U ) == 0U ) + { + xResult = pdTRUE; + } + + return xResult; + } +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + static portINLINE void vListInsertFifo( List_t * const pxList, + ListItem_t * const pxNewListItem ); + +/** + * @brief Insert the given item in the list in FIFO manner. + * + * @param[in] pxList: The list in which the item is to inserted. + * @param[in] pxNewListItem: The item to be inserted. + */ + static portINLINE void vListInsertFifo( List_t * const pxList, + ListItem_t * const pxNewListItem ) + { + vListInsertGeneric( pxList, pxNewListItem, &pxList->xListEnd ); + } + #endif +/*-----------------------------------------------------------*/ + + static portINLINE void vTCPTimerSet( TCPTimer_t * pxTimer ); + +/** + * @brief Set the timer's "born" time. + * + * @param[in] pxTimer: The TCP timer. + */ + static portINLINE void vTCPTimerSet( TCPTimer_t * pxTimer ) + { + pxTimer->uxBorn = xTaskGetTickCount(); + } +/*-----------------------------------------------------------*/ + + static portINLINE uint32_t ulTimerGetAge( const TCPTimer_t * pxTimer ); + +/** + * @brief Get the timer age in milliseconds. + * + * @param[in] pxTimer: The timer whose age is to be fetched. + * + * @return The time in milliseconds since the timer was born. + */ + static portINLINE uint32_t ulTimerGetAge( const TCPTimer_t * pxTimer ) + { + TickType_t uxNow = xTaskGetTickCount(); + TickType_t uxDiff = uxNow - pxTimer->uxBorn; + + return uxDiff * portTICK_PERIOD_MS; + } +/*-----------------------------------------------------------*/ + +/** + * @brief Insert a new list item into a list. + * + * @param[in] pxList: The list in which the item is to be inserted. + * @param[in] pxNewListItem: The item to be inserted. + * @param[in] pxWhere: Where should the item be inserted. + */ + static void vListInsertGeneric( List_t * const pxList, + ListItem_t * const pxNewListItem, + MiniListItem_t * pxWhere ) + { + /* Insert a new list item into pxList, it does not sort the list, + * but it puts the item just before xListEnd, so it will be the last item + * returned by listGET_HEAD_ENTRY() */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxNewListItem->pxNext = ( ( ListItem_t * ) pxWhere ); + + pxNewListItem->pxPrevious = pxWhere->pxPrevious; + pxWhere->pxPrevious->pxNext = pxNewListItem; + pxWhere->pxPrevious = pxNewListItem; + + /* Remember which list the item is in. */ + listLIST_ITEM_CONTAINER( pxNewListItem ) = ( struct xLIST * configLIST_VOLATILE ) pxList; + + ( pxList->uxNumberOfItems )++; + } +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Creates a pool of 'ipconfigTCP_WIN_SEG_COUNT' sector buffers. Should be called once only. + * + * @return When the allocation was successful: pdPASS, otherwise pdFAIL. + */ + static BaseType_t prvCreateSectors( void ) + { + BaseType_t xIndex; + BaseType_t xReturn; + + /* Allocate space for 'xTCPSegments' and store them in 'xSegmentList'. */ + + vListInitialise( &xSegmentList ); + xTCPSegments = ( ( TCPSegment_t * ) pvPortMallocLarge( ( size_t ) ipconfigTCP_WIN_SEG_COUNT * sizeof( xTCPSegments[ 0 ] ) ) ); + + if( xTCPSegments == NULL ) + { + FreeRTOS_debug_printf( ( "prvCreateSectors: malloc %u failed\n", + ( unsigned ) ( ipconfigTCP_WIN_SEG_COUNT * sizeof( xTCPSegments[ 0 ] ) ) ) ); + + xReturn = pdFAIL; + } + else + { + /* Clear the allocated space. */ + ( void ) memset( xTCPSegments, 0, ( size_t ) ipconfigTCP_WIN_SEG_COUNT * sizeof( xTCPSegments[ 0 ] ) ); + + for( xIndex = 0; xIndex < ipconfigTCP_WIN_SEG_COUNT; xIndex++ ) + { + /* Could call vListInitialiseItem here but all data has been + * nulled already. Set the owner to a segment descriptor. */ + + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + { + vListInitialiseItem( &( xTCPSegments[ xIndex ].xSegmentItem ) ); + vListInitialiseItem( &( xTCPSegments[ xIndex ].xQueueItem ) ); + } + #endif + + listSET_LIST_ITEM_OWNER( &( xTCPSegments[ xIndex ].xSegmentItem ), ( void * ) &( xTCPSegments[ xIndex ] ) ); + listSET_LIST_ITEM_OWNER( &( xTCPSegments[ xIndex ].xQueueItem ), ( void * ) &( xTCPSegments[ xIndex ] ) ); + + /* And add it to the pool of available segments */ + vListInsertFifo( &xSegmentList, &( xTCPSegments[ xIndex ].xSegmentItem ) ); + } + + xReturn = pdPASS; + } + + return xReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Find a segment with a given sequence number in the list of received segments. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulSequenceNumber: the sequence number to look-up + * + * @return The address of the segment descriptor found, or NULL when not found. + */ + static TCPSegment_t * xTCPWindowRxFind( const TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber ) + { + const ListItem_t * pxIterator; + const ListItem_t * pxEnd; + TCPSegment_t * pxSegment, * pxReturn = NULL; + + /* Find a segment with a given sequence number in the list of received + * segments. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxEnd = ( ( const ListItem_t * ) &( pxWindow->xRxSegments.xListEnd ) ); + + for( pxIterator = listGET_NEXT( pxEnd ); + pxIterator != pxEnd; + pxIterator = listGET_NEXT( pxIterator ) ) + { + pxSegment = ( ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + + if( pxSegment->ulSequenceNumber == ulSequenceNumber ) + { + pxReturn = pxSegment; + break; + } + } + + return pxReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Allocate a new segment object, either for transmission or reception. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulSequenceNumber: The sequence number. + * @param[in] lCount: The number of bytes stored in this segment. + * @param[in] xIsForRx: True when this is a reception segment. + * + * @return Allocate and initialise a segment descriptor, or NULL when none was available. + */ + static TCPSegment_t * xTCPWindowNew( TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber, + int32_t lCount, + BaseType_t xIsForRx ) + { + TCPSegment_t * pxSegment; + ListItem_t * pxItem; + + /* Allocate a new segment. The socket will borrow all segments from a + * common pool: 'xSegmentList', which is a list of 'TCPSegment_t' */ + if( listLIST_IS_EMPTY( &xSegmentList ) != pdFALSE ) + { + /* If the TCP-stack runs out of segments, you might consider + * increasing 'ipconfigTCP_WIN_SEG_COUNT'. */ + FreeRTOS_debug_printf( ( "xTCPWindow%cxNew: Error: all segments occupied\n", ( xIsForRx != 0 ) ? 'R' : 'T' ) ); + pxSegment = NULL; + } + else + { + /* Pop the item at the head of the list. Semaphore protection is + * not required as only the IP task will call these functions. */ + pxItem = ( ListItem_t * ) listGET_HEAD_ENTRY( &xSegmentList ); + pxSegment = ( ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxItem ) ); + + configASSERT( pxItem != NULL ); + configASSERT( pxSegment != NULL ); + + /* Remove the item from xSegmentList. */ + ( void ) uxListRemove( pxItem ); + + /* Add it to either the connections' Rx or Tx queue. */ + if( xIsForRx != 0 ) + { + vListInsertFifo( &pxWindow->xRxSegments, pxItem ); + } + else + { + vListInsertFifo( &pxWindow->xTxSegments, pxItem ); + } + + /* And set the segment's timer to zero */ + vTCPTimerSet( &pxSegment->xTransmitTimer ); + + pxSegment->u.ulFlags = 0; + pxSegment->u.bits.bIsForRx = ( xIsForRx != 0 ) ? 1U : 0U; + pxSegment->lMaxLength = lCount; + pxSegment->lDataLength = lCount; + pxSegment->ulSequenceNumber = ulSequenceNumber; + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + static UBaseType_t xLowestLength = ipconfigTCP_WIN_SEG_COUNT; + UBaseType_t xLength = listCURRENT_LIST_LENGTH( &xSegmentList ); + + if( xLowestLength > xLength ) + { + xLowestLength = xLength; + } + } + #endif /* ipconfigHAS_DEBUG_PRINTF */ + } + + return pxSegment; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief See if the peer has more packets for this node, before allowing to shut down the connection. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * + * @return pdTRUE if the connection can be closed. Else, pdFALSE. + */ + BaseType_t xTCPWindowRxEmpty( const TCPWindow_t * pxWindow ) + { + BaseType_t xReturn; + + /* When the peer has a close request (FIN flag), the driver will check + * if there are missing packets in the Rx-queue. It will accept the + * closure of the connection if both conditions are true: + * - the Rx-queue is empty + * - the highest Rx sequence number has been ACK'ed */ + if( listLIST_IS_EMPTY( ( &pxWindow->xRxSegments ) ) == pdFALSE ) + { + /* Rx data has been stored while earlier packets were missing. */ + xReturn = pdFALSE; + } + else if( xSequenceGreaterThanOrEqual( pxWindow->rx.ulCurrentSequenceNumber + 1U, pxWindow->rx.ulHighestSequenceNumber ) != pdFALSE ) + { + /* No Rx packets are being stored and the highest sequence number + * that has been received has been ACKed. */ + xReturn = pdTRUE; + } + else + { + FreeRTOS_debug_printf( ( "xTCPWindowRxEmpty: cur %u highest %u (empty)\n", + ( unsigned ) ( pxWindow->rx.ulCurrentSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ), + ( unsigned ) ( pxWindow->rx.ulHighestSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ) ) ); + xReturn = pdFALSE; + } + + return xReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Remove the head item of a list (generic function). + * + * @param[in] pxList: The list of segment descriptors. + * + * @return The address of the segment descriptor, or NULL when not found. + */ + static TCPSegment_t * xTCPWindowGetHead( const List_t * pxList ) + { + TCPSegment_t * pxSegment; + ListItem_t * pxItem; + + /* Detaches and returns the head of a queue. */ + if( listLIST_IS_EMPTY( pxList ) != pdFALSE ) + { + pxSegment = NULL; + } + else + { + pxItem = ( ListItem_t * ) listGET_HEAD_ENTRY( pxList ); + pxSegment = ( ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxItem ) ); + + ( void ) uxListRemove( pxItem ); + } + + return pxSegment; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Return the head item of a list (generic function). + * + * @param[in] pxList: The list of segment descriptors. + * + * @return The address of the segment descriptor, or NULL when the list is empty. + */ + static TCPSegment_t * xTCPWindowPeekHead( const List_t * pxList ) + { + const ListItem_t * pxItem; + TCPSegment_t * pxReturn; + + /* Returns the head of a queue but it won't be detached. */ + if( listLIST_IS_EMPTY( pxList ) != pdFALSE ) + { + pxReturn = NULL; + } + else + { + pxItem = ( ListItem_t * ) listGET_HEAD_ENTRY( pxList ); + pxReturn = ( ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxItem ) ); + } + + return pxReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Release a segment object, return it to the list of available segment holders. + * + * @param[in] pxSegment: The segment descriptor that must be freed. + */ + static void vTCPWindowFree( TCPSegment_t * pxSegment ) + { + /* Free entry pxSegment because it's not used any more. The ownership + * will be passed back to the segment pool. + * + * Unlink it from one of the queues, if any. */ + if( listLIST_ITEM_CONTAINER( &( pxSegment->xQueueItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxSegment->xQueueItem ) ); + } + + pxSegment->ulSequenceNumber = 0U; + pxSegment->lDataLength = 0; + pxSegment->u.ulFlags = 0U; + + /* Take it out of xRxSegments/xTxSegments */ + if( listLIST_ITEM_CONTAINER( &( pxSegment->xSegmentItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxSegment->xSegmentItem ) ); + } + + /* Return it to xSegmentList */ + vListInsertFifo( &xSegmentList, &( pxSegment->xSegmentItem ) ); + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Return all segment descriptor to the poll of descriptors, before deleting a socket. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + */ + void vTCPWindowDestroy( TCPWindow_t const * pxWindow ) + { + const List_t * pxSegments; + BaseType_t xRound; + TCPSegment_t * pxSegment; + + /* Destroy a window. A TCP window doesn't serve any more. Return all + * owned segments to the pool. In order to save code, it will make 2 rounds, + * one to remove the segments from xRxSegments, and a second round to clear + * xTxSegments*/ + for( xRound = 0; xRound < 2; xRound++ ) + { + if( xRound != 0 ) + { + pxSegments = &( pxWindow->xRxSegments ); + } + else + { + pxSegments = &( pxWindow->xTxSegments ); + } + + if( listLIST_IS_INITIALISED( pxSegments ) ) + { + while( listCURRENT_LIST_LENGTH( pxSegments ) > 0U ) + { + pxSegment = ( ( TCPSegment_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxSegments ) ); + vTCPWindowFree( pxSegment ); + } + } + } + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + +/** + * @brief Create a window for TCP. + * + * @param[in] pxWindow: The window to be created. + * @param[in] ulRxWindowLength: The length of the receive window. + * @param[in] ulTxWindowLength: The length of the transmit window. + * @param[in] ulAckNumber: The first ACK number. + * @param[in] ulSequenceNumber: The first sequence number. + * @param[in] ulMSS: The MSS of the connection. + */ + void vTCPWindowCreate( TCPWindow_t * pxWindow, + uint32_t ulRxWindowLength, + uint32_t ulTxWindowLength, + uint32_t ulAckNumber, + uint32_t ulSequenceNumber, + uint32_t ulMSS ) + { + /* Create and initialize a window. */ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + { + if( xTCPSegments == NULL ) + { + ( void ) prvCreateSectors(); + } + + vListInitialise( &( pxWindow->xTxSegments ) ); + vListInitialise( &( pxWindow->xRxSegments ) ); + + vListInitialise( &( pxWindow->xPriorityQueue ) ); /* Priority queue: segments which must be sent immediately */ + vListInitialise( &( pxWindow->xTxQueue ) ); /* Transmit queue: segments queued for transmission */ + vListInitialise( &( pxWindow->xWaitQueue ) ); /* Waiting queue: outstanding segments */ + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ + + if( xTCPWindowLoggingLevel != 0 ) + { + FreeRTOS_debug_printf( ( "vTCPWindowCreate: for WinLen = Rx/Tx: %u/%u\n", + ( unsigned ) ulRxWindowLength, ( unsigned ) ulTxWindowLength ) ); + } + + pxWindow->xSize.ulRxWindowLength = ulRxWindowLength; + pxWindow->xSize.ulTxWindowLength = ulTxWindowLength; + + vTCPWindowInit( pxWindow, ulAckNumber, ulSequenceNumber, ulMSS ); + } +/*-----------------------------------------------------------*/ + +/** + * @brief Initialise a TCP window. + * + * @param[in] pxWindow: The window to be initialised. + * @param[in] ulAckNumber: The number of the first ACK. + * @param[in] ulSequenceNumber: The first sequence number. + * @param[in] ulMSS: The MSS of the connection. + */ + void vTCPWindowInit( TCPWindow_t * pxWindow, + uint32_t ulAckNumber, + uint32_t ulSequenceNumber, + uint32_t ulMSS ) + { + const int32_t l500ms = 500; + + pxWindow->u.ulFlags = 0U; + pxWindow->u.bits.bHasInit = pdTRUE_UNSIGNED; + + if( ulMSS != 0U ) + { + if( pxWindow->usMSSInit != 0U ) + { + pxWindow->usMSSInit = ( uint16_t ) ulMSS; + } + + if( ( ulMSS < ( uint32_t ) pxWindow->usMSS ) || ( pxWindow->usMSS == 0U ) ) + { + pxWindow->xSize.ulRxWindowLength = ( pxWindow->xSize.ulRxWindowLength / ulMSS ) * ulMSS; + pxWindow->usMSS = ( uint16_t ) ulMSS; + } + } + + #if ( ipconfigUSE_TCP_WIN == 0 ) + { + pxWindow->xTxSegment.lMaxLength = ( int32_t ) pxWindow->usMSS; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ + + /*Start with a timeout of 2 * 500 ms (1 sec). */ + pxWindow->lSRTT = l500ms; + + /* Just for logging, to print relative sequence numbers. */ + pxWindow->rx.ulFirstSequenceNumber = ulAckNumber; + + /* The segment asked for in the next transmission. */ + pxWindow->rx.ulCurrentSequenceNumber = ulAckNumber; + + /* The right-hand side of the receive window. */ + pxWindow->rx.ulHighestSequenceNumber = ulAckNumber; + + pxWindow->tx.ulFirstSequenceNumber = ulSequenceNumber; + + /* The segment asked for in next transmission. */ + pxWindow->tx.ulCurrentSequenceNumber = ulSequenceNumber; + + /* The sequence number given to the next outgoing byte to be added is + * maintained by lTCPWindowTxAdd(). */ + pxWindow->ulNextTxSequenceNumber = ulSequenceNumber; + + /* The right-hand side of the transmit window. */ + pxWindow->tx.ulHighestSequenceNumber = ulSequenceNumber; + pxWindow->ulOurSequenceNumber = ulSequenceNumber; + } +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Free the space occupied by the pool of segment descriptors, normally never used + */ + void vTCPSegmentCleanup( void ) + { + /* Free and clear the TCP segments pointer. This function should only be called + * once FreeRTOS+TCP will no longer be used. No thread-safety is provided for this + * function. */ + if( xTCPSegments != NULL ) + { + vPortFreeLarge( xTCPSegments ); + xTCPSegments = NULL; + } + } + #endif /* ipconfgiUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + +/*============================================================================= + * + * ###### # # + * # # # # + * # # # # + * # # #### + * ###### ## + * # ## #### + * # # # # + * # # # # + * ### ## # # + * Rx functions + * + *=============================================================================*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief A expected segment has been received, see if there is overlap with earlier segments. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulSequenceNumber: The sequence number of the segment that was received. + * @param[in] ulLength: The number of bytes that were received. + * + * @return The first segment descriptor involved, or NULL when no matching descriptor was found. + */ + static TCPSegment_t * xTCPWindowRxConfirm( const TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber, + uint32_t ulLength ) + { + TCPSegment_t * pxBest = NULL; + const ListItem_t * pxIterator; + uint32_t ulNextSequenceNumber = ulSequenceNumber + ulLength; + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( pxWindow->xRxSegments.xListEnd ) ); + TCPSegment_t * pxSegment; + + /* A segment has been received with sequence number 'ulSequenceNumber', + * where 'ulCurrentSequenceNumber == ulSequenceNumber', which means that + * exactly this segment was expected. xTCPWindowRxConfirm() will check if + * there is already another segment with a sequence number between (ulSequenceNumber) + * and (ulSequenceNumber+ulLength). Normally none will be found, because + * the next RX segment should have a sequence number equal to + * '(ulSequenceNumber+ulLength)'. */ + + /* Iterate through all RX segments that are stored: */ + for( pxIterator = listGET_NEXT( pxEnd ); + pxIterator != pxEnd; + pxIterator = listGET_NEXT( pxIterator ) ) + { + pxSegment = ( ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + + /* And see if there is a segment for which: + * 'ulSequenceNumber' <= 'pxSegment->ulSequenceNumber' < 'ulNextSequenceNumber' + * If there are more matching segments, the one with the lowest sequence number + * shall be taken */ + if( ( xSequenceGreaterThanOrEqual( pxSegment->ulSequenceNumber, ulSequenceNumber ) != 0 ) && + ( xSequenceLessThan( pxSegment->ulSequenceNumber, ulNextSequenceNumber ) != 0 ) ) + { + if( ( pxBest == NULL ) || ( xSequenceLessThan( pxSegment->ulSequenceNumber, pxBest->ulSequenceNumber ) != 0 ) ) + { + pxBest = pxSegment; + } + } + } + + if( ( pxBest != NULL ) && + ( ( pxBest->ulSequenceNumber != ulSequenceNumber ) || ( pxBest->lDataLength != ( int32_t ) ulLength ) ) ) + { + FreeRTOS_debug_printf( ( "xTCPWindowRxConfirm[%u]: search %u (+%u=%u) found %u (+%d=%u)\n", + pxWindow->usPeerPortNumber, + ( unsigned ) ( ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ), + ( unsigned ) ulLength, + ( unsigned ) ( ulSequenceNumber + ulLength - pxWindow->rx.ulFirstSequenceNumber ), + ( unsigned ) ( pxBest->ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ), + ( int ) pxBest->lDataLength, + ( unsigned ) ( pxBest->ulSequenceNumber + ( ( uint32_t ) pxBest->lDataLength ) - pxWindow->rx.ulFirstSequenceNumber ) ) ); + } + + return pxBest; + } + #endif /* ipconfgiUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Data has been received with the correct ( expected ) sequence number. + * It can be added to the RX stream buffer. + * @param[in] pxWindow: The TCP sliding window data of the socket. + * @param[in] ulLength: The number of bytes that can be added. + */ + static void prvTCPWindowRx_ExpectedRX( TCPWindow_t * pxWindow, + uint32_t ulLength ) + { + uint32_t ulSequenceNumber = pxWindow->rx.ulCurrentSequenceNumber; + uint32_t ulCurrentSequenceNumber = ulSequenceNumber + ulLength; + + if( listCURRENT_LIST_LENGTH( &( pxWindow->xRxSegments ) ) != 0U ) + { + uint32_t ulSavedSequenceNumber = ulCurrentSequenceNumber; + TCPSegment_t * pxFound; + + /* Clean up all sequence received between ulSequenceNumber and ulSequenceNumber + ulLength since they are duplicated. + * If the server is forced to retransmit packets several time in a row it might send a batch of concatenated packet for speed. + * So we cannot rely on the packets between ulSequenceNumber and ulSequenceNumber + ulLength to be sequential and it is better to just + * clean them out. */ + do + { + pxFound = xTCPWindowRxConfirm( pxWindow, ulSequenceNumber, ulLength ); + + if( pxFound != NULL ) + { + /* Remove it because it will be passed to user directly. */ + vTCPWindowFree( pxFound ); + } + } while( pxFound != NULL ); + + /* Check for following segments that are already in the + * queue and increment ulCurrentSequenceNumber. */ + for( ; ; ) + { + pxFound = xTCPWindowRxFind( pxWindow, ulCurrentSequenceNumber ); + + if( pxFound == NULL ) + { + break; + } + + ulCurrentSequenceNumber += ( uint32_t ) pxFound->lDataLength; + + /* As all packet below this one have been passed to the + * user it can be discarded. */ + vTCPWindowFree( pxFound ); + } + + if( ulSavedSequenceNumber != ulCurrentSequenceNumber ) + { + /* After the current data-package, there is more data + * to be popped. */ + pxWindow->ulUserDataLength = ulCurrentSequenceNumber - ulSavedSequenceNumber; + + if( xTCPWindowLoggingLevel >= 1 ) + { + FreeRTOS_debug_printf( ( "lTCPWindowRxCheck[%u,%u]: retran %u (Found %u bytes at %u cnt %d)\n", + pxWindow->usPeerPortNumber, + pxWindow->usOurPortNumber, + ( unsigned ) ( ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ), + ( unsigned ) pxWindow->ulUserDataLength, + ( unsigned ) ( ulSavedSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ), + ( int ) listCURRENT_LIST_LENGTH( &pxWindow->xRxSegments ) ) ); + } + } + } + + pxWindow->rx.ulCurrentSequenceNumber = ulCurrentSequenceNumber; + } + #endif /* ipconfgiUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Data has been received with a non-expected sequence number. + * This function will check if the RX data can be accepted. + * @param[in] pxWindow: The TCP sliding window data of the socket. + * @param[in] ulSequenceNumber: The sequence number at which the data should be placed. + * @param[in] ulLength: The number of bytes that can be added. + * @return Return -1 if the data must be refused, otherwise it returns the + * offset ( from the head ) at which the data can be placed. + */ + static int32_t prvTCPWindowRx_UnexpectedRX( TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber, + uint32_t ulLength ) + { + int32_t lReturn = -1; + uint32_t ulLast = ulSequenceNumber + ulLength; + uint32_t ulCurrentSequenceNumber = pxWindow->rx.ulCurrentSequenceNumber; + const TCPSegment_t * pxFound; + + /* See if there is more data in a contiguous block to make the + * SACK describe a longer range of data. */ + + /* TODO: SACK's may also be delayed for a short period + * This is useful because subsequent packets will be SACK'd with + * single one message + */ + for( ; ; ) + { + pxFound = xTCPWindowRxFind( pxWindow, ulLast ); + + if( pxFound == NULL ) + { + break; + } + + ulLast += ( uint32_t ) pxFound->lDataLength; + } + + if( xTCPWindowLoggingLevel >= 1 ) + { + FreeRTOS_debug_printf( ( "lTCPWindowRxCheck[%d,%d]: seqnr %u exp %u (dist %d) SACK to %u\n", + ( int ) pxWindow->usPeerPortNumber, + ( int ) pxWindow->usOurPortNumber, + ( unsigned ) ( ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ), + ( unsigned ) ( ulCurrentSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ), + ( int ) ( ulSequenceNumber - ulCurrentSequenceNumber ), /* want this signed */ + ( unsigned ) ( ulLast - pxWindow->rx.ulFirstSequenceNumber ) ) ); + } + + /* Now prepare the SACK message. + * Code OPTION_CODE_SINGLE_SACK already in network byte order. */ + pxWindow->ulOptionsData[ 0 ] = OPTION_CODE_SINGLE_SACK; + + /* First sequence number that we received. */ + pxWindow->ulOptionsData[ 1 ] = FreeRTOS_htonl( ulSequenceNumber ); + + /* Last + 1 */ + pxWindow->ulOptionsData[ 2 ] = FreeRTOS_htonl( ulLast ); + + /* Which make 12 (3*4) option bytes. */ + pxWindow->ucOptionLength = ( uint8_t ) ( 3U * sizeof( pxWindow->ulOptionsData[ 0 ] ) ); + + pxFound = xTCPWindowRxFind( pxWindow, ulSequenceNumber ); + + if( pxFound != NULL ) + { + /* This out-of-sequence packet has been received for a + * second time. It is already stored but do send a SACK + * again. */ + /* A negative value will be returned to indicate than error. */ + } + else + { + pxFound = xTCPWindowRxNew( pxWindow, ulSequenceNumber, ( int32_t ) ulLength ); + + if( pxFound == NULL ) + { + /* Can not send a SACK, because the segment cannot be + * stored. */ + pxWindow->ucOptionLength = 0U; + + /* Needs to be stored but there is no segment + * available. A negative value will be returned. */ + } + else + { + uint32_t ulIntermediateResult; + + if( xTCPWindowLoggingLevel != 0 ) + { + FreeRTOS_debug_printf( ( "lTCPWindowRxCheck[%u,%u]: seqnr %u (cnt %u)\n", + pxWindow->usPeerPortNumber, + pxWindow->usOurPortNumber, + ( unsigned ) ( ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ), + ( unsigned ) listCURRENT_LIST_LENGTH( &pxWindow->xRxSegments ) ) ); + FreeRTOS_flush_logging(); + } + + /* Return a positive value. The packet may be accepted + * and stored but an earlier packet is still missing. */ + ulIntermediateResult = ulSequenceNumber - ulCurrentSequenceNumber; + lReturn = ( int32_t ) ulIntermediateResult; + } + } + + return lReturn; + } + #endif /* ipconfgiUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Check what to do with a new incoming packet: store or ignore. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulSequenceNumber: The sequence number of the packet received. + * @param[in] ulLength: The number of bytes received. + * @param[in] ulSpace: The available space in the RX stream buffer. + * @param[out] pulSkipCount: the number of bytes to skip in the receive buffer. + * + * @return 0 or positive value indicating the offset at which the packet is to + * be stored, -1 if the packet is to be ignored. + */ + int32_t lTCPWindowRxCheck( TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber, + uint32_t ulLength, + uint32_t ulSpace, + uint32_t * pulSkipCount ) + { + uint32_t ulCurrentSequenceNumber; + uint32_t ulIntermediateResult; + int32_t lReturn = -1; + int32_t lStartDistance; + int32_t lLastDistance; + uint32_t ulLast; + uint32_t ulRxSequenceNumber = ulSequenceNumber; + uint32_t ulRxLength = ulLength; + + /* If lTCPWindowRxCheck( ) returns == 0, the packet will be passed + * directly to user (segment is expected). If it returns a positive + * number, an earlier packet is missing, but this packet may be stored. + * If negative, the packet has already been stored, or it is out-of-order, + * or there is not enough space. + * + * As a side-effect, pxWindow->ulUserDataLength will get set to non-zero, + * if more Rx data may be passed to the user after this packet. */ + + /* Only in an exceptional case, where a packet starts before + * ulCurrentSequenceNumber, and ends after it, the skip-count + * will be set. See below. */ + + *( pulSkipCount ) = 0U; + + ulCurrentSequenceNumber = pxWindow->rx.ulCurrentSequenceNumber; + + ulLast = ulRxSequenceNumber + ulRxLength; + ulIntermediateResult = ulLast - ulCurrentSequenceNumber; + /* The cast from unsigned long to signed long is on purpose. */ + lLastDistance = ( int32_t ) ulIntermediateResult; + + ulIntermediateResult = ulRxSequenceNumber - ulCurrentSequenceNumber; + lStartDistance = ( int32_t ) ulIntermediateResult; + + if( ( lStartDistance < 0 ) && ( lLastDistance > 0 ) ) + { + FreeRTOS_debug_printf( ( "lTCPWindowRxCheck: Received +%u bytes for %u, only using %d\n", + ( unsigned ) ulRxLength, + ( unsigned ) ( ulRxSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ), + ( int ) lLastDistance ) ); + /* Increase the sequence number, decrease the length. */ + ulRxSequenceNumber += ( uint32_t ) ( -lStartDistance ); + ulRxLength += ( uint32_t ) lStartDistance; + + /* Tell the caller that the first 'pulSkipCount' bytes don't + * need to be stored. */ + *( pulSkipCount ) = ( uint32_t ) ( -lStartDistance ); + } + + /* For Selective Ack (SACK), used when out-of-sequence data come in. */ + pxWindow->ucOptionLength = 0U; + + /* Non-zero if TCP-windows contains data which must be popped. */ + pxWindow->ulUserDataLength = 0U; + + if( ulCurrentSequenceNumber == ulRxSequenceNumber ) + { + /* This is the packet with the lowest sequence number we're waiting + * for. It can be passed directly to the rx stream. */ + if( ulRxLength > ulSpace ) + { + FreeRTOS_debug_printf( ( "lTCPWindowRxCheck: Refuse %u bytes, due to lack of space (%u)\n", ( unsigned ) ulRxLength, ( unsigned ) ulSpace ) ); + } + else + { + /* Packet was expected, may be passed directly to the socket + * buffer or application. Store the packet at offset 0. */ + prvTCPWindowRx_ExpectedRX( pxWindow, ulRxLength ); + lReturn = 0; + } + } + else if( ulCurrentSequenceNumber == ( ulRxSequenceNumber + 1U ) ) + { + /* Looks like a TCP keep-alive message. Do not accept/store Rx data + * ulUserDataLength = 0. Not packet out-of-sync. Just reply to it. */ + } + else + { + /* The packet is not the one expected. See if it falls within the Rx + * window so it can be stored. */ + + /* An "out-of-sequence" segment was received, must have missed one. + * Prepare a SACK (Selective ACK). */ + + if( lLastDistance <= 0 ) + { + /* An earlier packet has been received, must be a retransmission of a + * packet that has been accepted already. No need to send out a + * Selective ACK (SACK). */ + } + else if( lLastDistance > ( int32_t ) ulSpace ) + { + /* The new segment is ahead of rx.ulCurrentSequenceNumber. The + * sequence number of this packet is too far ahead, ignore it. */ + FreeRTOS_debug_printf( ( "lTCPWindowRxCheck: Refuse %d+%u bytes, due to lack of space (%u)\n", + ( int ) lLastDistance, + ( unsigned ) ulRxLength, + ( unsigned ) ulSpace ) ); + } + else + { + lReturn = prvTCPWindowRx_UnexpectedRX( pxWindow, ulRxSequenceNumber, ulRxLength ); + } + } + + return lReturn; + } + #endif /* ipconfgiUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + +/*============================================================================= + * + * ######### # # + * # # # # # + * # # # + * # #### + * # ## + * # #### + * # # # + * # # # + * ##### # # + * + * Tx functions + * + *=============================================================================*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Increment the position in a circular buffer of size 'lMax'. + * + * @param[in] lPosition: The current index in the buffer. + * @param[in] lMax: The total number of items in this buffer. + * @param[in] lCount: The number of bytes that must be advanced. + * + * @return The new incremented position, or "( lPosition + lCount ) % lMax". + */ + static int32_t lTCPIncrementTxPosition( int32_t lPosition, + int32_t lMax, + int32_t lCount ) + { + int32_t lReturn; + + + /* +TCP stores data in circular buffers. Calculate the next position to + * store. */ + lReturn = lPosition + lCount; + + if( lReturn >= lMax ) + { + lReturn -= lMax; + } + + return lReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Adding data to a segment that was already in the TX queue. It + * will be filled-up to a maximum of MSS ( maximum segment size ). + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] pxSegment: The TX segment with the highest sequence number, + * i.e. the "front segment". + * @param[in] lBytesLeft: The number of bytes that must be added. + * + * @return lToWrite: the number of bytes added to the segment. + */ + static int32_t prvTCPWindowTxAdd_FrontSegment( TCPWindow_t * pxWindow, + TCPSegment_t * pxSegment, + int32_t lBytesLeft ) + { + int32_t lToWrite = FreeRTOS_min_int32( lBytesLeft, pxSegment->lMaxLength - pxSegment->lDataLength ); + + pxSegment->lDataLength += lToWrite; + + if( pxSegment->lDataLength >= pxSegment->lMaxLength ) + { + /* This segment is full, don't add more bytes. */ + pxWindow->pxHeadSegment = NULL; + } + + /* ulNextTxSequenceNumber is the sequence number of the next byte to + * be stored for transmission. */ + pxWindow->ulNextTxSequenceNumber += ( uint32_t ) lToWrite; + + /* Some detailed logging, for those who're interested. */ + if( ( xTCPWindowLoggingLevel >= 2 ) && ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) ) + { + FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: Add %4d bytes for seqNr %u len %4d (nxt %u) pos %d\n", + ( int ) lBytesLeft, + ( unsigned ) ( pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( int ) pxSegment->lDataLength, + ( unsigned ) ( pxWindow->ulNextTxSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( int ) pxSegment->lStreamPos ) ); + FreeRTOS_flush_logging(); + } + + return lToWrite; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Will add data to be transmitted to the front of the segment fifo. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulLength: The number of bytes that will be sent. + * @param[in] lPosition: The index in the TX stream buffer. + * @param[in] lMax: The size of the ( circular ) TX stream buffer. + * + * @return The number of bytes added to the sliding window for transmission. + * + */ + int32_t lTCPWindowTxAdd( TCPWindow_t * pxWindow, + uint32_t ulLength, + int32_t lPosition, + int32_t lMax ) + { + int32_t lBytesLeft = ( int32_t ) ulLength; + int32_t lToWrite; + int32_t lDone = 0; + int32_t lBufferIndex = lPosition; + TCPSegment_t * pxSegment = pxWindow->pxHeadSegment; + + /* Puts a message in the Tx-window (after buffer size has been + * verified). */ + if( ( pxSegment != NULL ) && + ( pxSegment->lDataLength < pxSegment->lMaxLength ) && + ( pxSegment->u.bits.bOutstanding == pdFALSE_UNSIGNED ) && + ( pxSegment->lDataLength != 0 ) ) + { + lToWrite = prvTCPWindowTxAdd_FrontSegment( pxWindow, pxSegment, lBytesLeft ); + lBytesLeft -= lToWrite; + /* Increased the return value. */ + lDone += lToWrite; + + /* Calculate the next position in the circular data buffer, knowing + * its maximum length 'lMax'. */ + lBufferIndex = lTCPIncrementTxPosition( lBufferIndex, lMax, lToWrite ); + } + + while( lBytesLeft > 0 ) + { + /* The current transmission segment is full, create new segments as + * needed. */ + pxSegment = xTCPWindowTxNew( pxWindow, pxWindow->ulNextTxSequenceNumber, ( int32_t ) pxWindow->usMSS ); + + if( pxSegment != NULL ) + { + /* Store as many as needed, but no more than the maximum + * (MSS). */ + lToWrite = FreeRTOS_min_int32( lBytesLeft, pxSegment->lMaxLength ); + + pxSegment->lDataLength = lToWrite; + pxSegment->lStreamPos = lBufferIndex; + lBytesLeft -= lToWrite; + lBufferIndex = lTCPIncrementTxPosition( lBufferIndex, lMax, lToWrite ); + pxWindow->ulNextTxSequenceNumber += ( uint32_t ) lToWrite; + lDone += lToWrite; + + /* Link this segment in the Tx-Queue. */ + vListInsertFifo( &( pxWindow->xTxQueue ), &( pxSegment->xQueueItem ) ); + + /* Let 'pxHeadSegment' point to this segment if there is still + * space. */ + if( pxSegment->lDataLength < pxSegment->lMaxLength ) + { + pxWindow->pxHeadSegment = pxSegment; + } + else + { + pxWindow->pxHeadSegment = NULL; + } + } + else + { + /* A sever situation: running out of segments for transmission. + * No more data can be sent at the moment. */ + if( lDone != 0 ) + { + FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: Sorry all buffers full (cancel %d bytes)\n", ( int ) lBytesLeft ) ); + } + + break; + } + } + + return lDone; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Returns true if there are no more outstanding TX segments. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * + * @return pdTRUE if there are no more outstanding Tx segments, else pdFALSE. + */ + BaseType_t xTCPWindowTxDone( const TCPWindow_t * pxWindow ) + { + return listLIST_IS_EMPTY( ( &pxWindow->xTxSegments ) ); + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Find out if the peer is able to receive more data. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulWindowSize: The number of bytes in this segment. + * + * @return True if the peer has space in it window to receive more data. + */ + static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t const * pxWindow, + uint32_t ulWindowSize ) + { + uint32_t ulTxOutstanding; + BaseType_t xHasSpace; + const TCPSegment_t * pxSegment; + uint32_t ulNettSize; + + /* This function will look if there is new transmission data. It will + * return true if there is data to be sent. */ + + pxSegment = xTCPWindowPeekHead( &( pxWindow->xTxQueue ) ); + + if( pxSegment == NULL ) + { + xHasSpace = pdFALSE; + } + else + { + /* How much data is outstanding, i.e. how much data has been sent + * but not yet acknowledged ? */ + if( pxWindow->tx.ulHighestSequenceNumber >= pxWindow->tx.ulCurrentSequenceNumber ) + { + ulTxOutstanding = pxWindow->tx.ulHighestSequenceNumber - pxWindow->tx.ulCurrentSequenceNumber; + } + else + { + ulTxOutstanding = 0U; + } + + /* Subtract this from the peer's space. */ + ulNettSize = ulWindowSize - FreeRTOS_min_uint32( ulWindowSize, ulTxOutstanding ); + + /* See if the next segment may be sent. */ + if( ulNettSize >= ( uint32_t ) pxSegment->lDataLength ) + { + xHasSpace = pdTRUE; + } + else + { + xHasSpace = pdFALSE; + } + + /* If 'xHasSpace', it looks like the peer has at least space for 1 + * more new segment of size MSS. xSize.ulTxWindowLength is the self-imposed + * limitation of the transmission window (in case of many resends it + * may be decreased). */ + if( ( ulTxOutstanding != 0U ) && + ( pxWindow->xSize.ulTxWindowLength < + ( ulTxOutstanding + ( ( uint32_t ) pxSegment->lDataLength ) ) ) ) + { + xHasSpace = pdFALSE; + } + } + + return xHasSpace; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Returns true if there is TX data that can be sent right now. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulWindowSize: The current size of the sliding RX window of the peer. + * @param[out] pulDelay: The delay before the packet may be sent. + * + * @return pdTRUE if there is Tx data that can be sent, else pdFALSE. + */ + BaseType_t xTCPWindowTxHasData( TCPWindow_t const * pxWindow, + uint32_t ulWindowSize, + TickType_t * pulDelay ) + { + TCPSegment_t const * pxSegment; + BaseType_t xReturn; + TickType_t ulAge, ulMaxAge; + + *pulDelay = 0U; + + if( listLIST_IS_EMPTY( &pxWindow->xPriorityQueue ) == pdFALSE ) + { + /* No need to look at retransmissions or new transmission as long as + * there are priority segments. *pulDelay equals zero, meaning it must + * be sent out immediately. */ + xReturn = pdTRUE; + } + else + { + pxSegment = xTCPWindowPeekHead( &( pxWindow->xWaitQueue ) ); + + if( pxSegment != NULL ) + { + uint32_t ulSRTT = ( uint32_t ) pxWindow->lSRTT; + + /* There is an outstanding segment, see if it is time to resend + * it. */ + ulAge = ulTimerGetAge( &pxSegment->xTransmitTimer ); + + /* After a packet has been sent for the first time, it will wait + * '1 * ulSRTT' ms for an ACK. A second time it will wait '2 * ulSRTT' ms, + * each time doubling the time-out */ + ulMaxAge = ( ( uint32_t ) 1U << pxSegment->u.bits.ucTransmitCount ); + ulMaxAge *= ulSRTT; + + if( ulMaxAge > ulAge ) + { + /* A segment must be sent after this amount of msecs */ + *pulDelay = ulMaxAge - ulAge; + } + + xReturn = pdTRUE; + } + else + { + /* No priority segment, no outstanding data, see if there is new + * transmission data. */ + pxSegment = xTCPWindowPeekHead( &pxWindow->xTxQueue ); + + /* See if it fits in the peer's reception window. */ + if( pxSegment == NULL ) + { + xReturn = pdFALSE; + } + else if( prvTCPWindowTxHasSpace( pxWindow, ulWindowSize ) == pdFALSE ) + { + /* Too many outstanding messages. */ + xReturn = pdFALSE; + } + else if( ( pxWindow->u.bits.bSendFullSize != pdFALSE_UNSIGNED ) && + ( pxSegment->lDataLength < pxSegment->lMaxLength ) ) + { + /* 'bSendFullSize' is a special optimisation. If true, the + * driver will only sent completely filled packets (of MSS + * bytes). */ + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + } + } + + return xReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Three type of queues are used for transmission: priority, waiting, and + * the normal TX queue of unsent data. Message in the waiting queue will + * be sent when their timer has expired. + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + */ + static TCPSegment_t * pxTCPWindowTx_GetWaitQueue( const TCPWindow_t * pxWindow ) + { + TCPSegment_t * pxSegment = xTCPWindowPeekHead( &( pxWindow->xWaitQueue ) ); + + if( pxSegment != NULL ) + { + /* Do check the timing. */ + uint32_t ulMaxTime; + + ulMaxTime = ( ( uint32_t ) 1U ) << pxSegment->u.bits.ucTransmitCount; + ulMaxTime *= ( uint32_t ) pxWindow->lSRTT; + + if( ulTimerGetAge( &pxSegment->xTransmitTimer ) > ulMaxTime ) + { + /* A normal (non-fast) retransmission. Move it from the + * head of the waiting queue. */ + pxSegment = xTCPWindowGetHead( &( pxWindow->xWaitQueue ) ); + pxSegment->u.bits.ucDupAckCount = ( uint8_t ) pdFALSE_UNSIGNED; + + /* Some detailed logging. */ + if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) ) ) + { + FreeRTOS_debug_printf( ( "ulTCPWindowTxGet[%u,%u]: WaitQueue %d bytes for sequence number %u (0x%X)\n", + pxWindow->usPeerPortNumber, + pxWindow->usOurPortNumber, + ( int ) pxSegment->lDataLength, + ( unsigned ) ( pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) pxSegment->ulSequenceNumber ) ); + FreeRTOS_flush_logging(); + } + } + else + { + pxSegment = NULL; + } + } + + return pxSegment; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ + +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief See if there is a transmission in the normal TX queue. It is the + * first time these data are being sent. After sending they will move + * the waiting queue. + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulWindowSize: The available space that the peer has in his + * reception window. + * @return Either a segment that has to be sent, or NULL. + */ + static TCPSegment_t * pxTCPWindowTx_GetTXQueue( TCPWindow_t * pxWindow, + uint32_t ulWindowSize ) + { + TCPSegment_t * pxSegment = xTCPWindowPeekHead( &( pxWindow->xTxQueue ) ); + + if( pxSegment == NULL ) + { + /* No segments queued. */ + } + else if( ( pxWindow->u.bits.bSendFullSize != pdFALSE_UNSIGNED ) && + ( pxSegment->lDataLength < pxSegment->lMaxLength ) ) + { + /* A segment has been queued but the driver waits until it + * has a full size of MSS. */ + pxSegment = NULL; + } + else if( prvTCPWindowTxHasSpace( pxWindow, ulWindowSize ) == pdFALSE ) + { + /* Peer has no more space at this moment. */ + pxSegment = NULL; + } + else + { + /* pxSegment was just obtained with a peek function, + * now remove it from of the Tx queue. */ + pxSegment = xTCPWindowGetHead( &( pxWindow->xTxQueue ) ); + + /* Don't let pxHeadSegment point to this segment any more, + * so no more data will be added. */ + if( pxWindow->pxHeadSegment == pxSegment ) + { + pxWindow->pxHeadSegment = NULL; + } + + /* pxWindow->tx.highest registers the highest sequence + * number in our transmission window. */ + pxWindow->tx.ulHighestSequenceNumber = pxSegment->ulSequenceNumber + ( ( uint32_t ) pxSegment->lDataLength ); + + /* ...and more detailed logging */ + if( ( xTCPWindowLoggingLevel >= 2 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) ) ) + { + FreeRTOS_debug_printf( ( "ulTCPWindowTxGet[%u,%u]: XmitQueue %d bytes for sequence number %u (ws %u)\n", + pxWindow->usPeerPortNumber, + pxWindow->usOurPortNumber, + ( int ) pxSegment->lDataLength, + ( unsigned ) ( pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ulWindowSize ) ); + FreeRTOS_flush_logging(); + } + } + + return pxSegment; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Get data that can be transmitted right now. There are three types of + * outstanding segments: Priority queue, Waiting queue, Normal TX queue. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulWindowSize: The current size of the sliding RX window of the peer. + * @param[out] plPosition: The index within the TX stream buffer of the first byte to be sent. + * + * @return The amount of data in bytes that can be transmitted right now. + */ + uint32_t ulTCPWindowTxGet( TCPWindow_t * pxWindow, + uint32_t ulWindowSize, + int32_t * plPosition ) + { + TCPSegment_t * pxSegment; + uint32_t ulReturn = 0U; + + /* Fetches data to be sent-out now. + * + * Priority messages: segments with a resend need no check current sliding + * window size. */ + pxSegment = xTCPWindowGetHead( &( pxWindow->xPriorityQueue ) ); + pxWindow->ulOurSequenceNumber = pxWindow->tx.ulHighestSequenceNumber; + + if( pxSegment != NULL ) + { + /* There is a priority segment. It doesn't need any checking for + * space or timeouts. */ + if( xTCPWindowLoggingLevel != 0 ) + { + FreeRTOS_debug_printf( ( "ulTCPWindowTxGet[%u,%u]: PrioQueue %d bytes for sequence number %u (ws %u)\n", + pxWindow->usPeerPortNumber, + pxWindow->usOurPortNumber, + ( int ) pxSegment->lDataLength, + ( unsigned ) ( pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ulWindowSize ) ); + FreeRTOS_flush_logging(); + } + } + else + { + /* Waiting messages: outstanding messages with a running timer + * neither check peer's reception window size because these packets + * have been sent earlier. */ + pxSegment = pxTCPWindowTx_GetWaitQueue( pxWindow ); + + if( pxSegment == NULL ) + { + /* New messages: sent-out for the first time. Check current + * sliding window size of peer. */ + pxSegment = pxTCPWindowTx_GetTXQueue( pxWindow, ulWindowSize ); + } + } + + /* See if it has already been determined to return 0. */ + if( pxSegment != NULL ) + { + configASSERT( listLIST_ITEM_CONTAINER( &( pxSegment->xQueueItem ) ) == NULL ); + + /* Now that the segment will be transmitted, add it to the tail of + * the waiting queue. */ + vListInsertFifo( &pxWindow->xWaitQueue, &pxSegment->xQueueItem ); + + /* And mark it as outstanding. */ + pxSegment->u.bits.bOutstanding = pdTRUE_UNSIGNED; + + /* Administer the transmit count, needed for fast + * retransmissions. */ + ( pxSegment->u.bits.ucTransmitCount )++; + + /* If there have been several retransmissions (4), decrease the + * size of the transmission window to at most 2 times MSS. */ + if( ( pxSegment->u.bits.ucTransmitCount == MAX_TRANSMIT_COUNT_USING_LARGE_WINDOW ) && + ( pxWindow->xSize.ulTxWindowLength > ( 2U * ( ( uint32_t ) pxWindow->usMSS ) ) ) ) + { + uint16_t usMSS2 = pxWindow->usMSS * 2U; + FreeRTOS_debug_printf( ( "ulTCPWindowTxGet[%u - %u]: Change Tx window: %u -> %u\n", + pxWindow->usPeerPortNumber, + pxWindow->usOurPortNumber, + ( unsigned ) pxWindow->xSize.ulTxWindowLength, + usMSS2 ) ); + pxWindow->xSize.ulTxWindowLength = usMSS2; + } + + /* Clear the transmit timer. */ + vTCPTimerSet( &( pxSegment->xTransmitTimer ) ); + + pxWindow->ulOurSequenceNumber = pxSegment->ulSequenceNumber; + + /* Inform the caller where to find the data within the queue. */ + *plPosition = pxSegment->lStreamPos; + + /* And return the length of the data segment */ + ulReturn = ( uint32_t ) pxSegment->lDataLength; + } + + return ulReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Data has been sent, and an ACK has been received. Make an estimate + * of the round-trip time, and calculate the new timeout for transmissions. + * More explanation in a comment here below. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] pxSegment: The segment that was just acknowledged. + */ + static void prvTCPWindowTxCheckAck_CalcSRTT( TCPWindow_t * pxWindow, + const TCPSegment_t * pxSegment ) + { + int32_t mS = ( int32_t ) ulTimerGetAge( &( pxSegment->xTransmitTimer ) ); + + if( pxWindow->lSRTT >= mS ) + { + /* RTT becomes smaller: adapt slowly. */ + pxWindow->lSRTT = ( ( winSRTT_DECREMENT_NEW * mS ) + ( winSRTT_DECREMENT_CURRENT * pxWindow->lSRTT ) ) / ( winSRTT_DECREMENT_NEW + winSRTT_DECREMENT_CURRENT ); + } + else + { + /* RTT becomes larger: adapt quicker */ + pxWindow->lSRTT = ( ( winSRTT_INCREMENT_NEW * mS ) + ( winSRTT_INCREMENT_CURRENT * pxWindow->lSRTT ) ) / ( winSRTT_INCREMENT_NEW + winSRTT_INCREMENT_CURRENT ); + } + + /* Cap to the minimum of 50ms. */ + if( pxWindow->lSRTT < winSRTT_CAP_mS ) + { + pxWindow->lSRTT = winSRTT_CAP_mS; + } + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief An acknowledgement or a selective ACK (SACK) was received. See if some outstanding data + * may be removed from the transmission queue(s). All TX segments for which + * ( ( ulSequenceNumber >= ulFirst ) && ( ulSequenceNumber < ulLast ) in a contiguous block. + * Note that the segments are stored in xTxSegments in a strict sequential order. + * + * @param[in] pxWindow: The TCP-window object of the current connection. + * @param[in] ulFirst: The sequence number of the first byte that was acknowledged. + * @param[in] ulLast: The sequence number of the last byte ( minus one ) that was acknowledged. + * + * @return number of bytes that the tail of txStream may be advanced. + */ + static uint32_t prvTCPWindowTxCheckAck( TCPWindow_t * pxWindow, + uint32_t ulFirst, + uint32_t ulLast ) + { + uint32_t ulBytesConfirmed = 0U; + uint32_t ulSequenceNumber = ulFirst; + uint32_t ulDataLength; + const ListItem_t * pxIterator; + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( pxWindow->xTxSegments.xListEnd ) ); + BaseType_t xDoUnlink; + TCPSegment_t * pxSegment; + + /* An acknowledgement or a selective ACK (SACK) was received. See if some outstanding data + * may be removed from the transmission queue(s). + * All TX segments for which + * ( ( ulSequenceNumber >= ulFirst ) && ( ulSequenceNumber < ulLast ) in a + * contiguous block. Note that the segments are stored in xTxSegments in a + * strict sequential order. */ + + /* SRTT[i] = (1-a) * SRTT[i-1] + a * RTT + * + * 0 < a < 1; usually a = 1/8 + * + * RTO = 2 * SRTT + * + * where: + * RTT is Round Trip Time + * SRTT is Smoothed RTT + * RTO is Retransmit timeout + * + * A Smoothed RTT will increase quickly, but it is conservative when + * becoming smaller. */ + + pxIterator = listGET_NEXT( pxEnd ); + + while( ( pxIterator != pxEnd ) && ( xSequenceLessThan( ulSequenceNumber, ulLast ) != 0 ) ) + { + xDoUnlink = pdFALSE; + pxSegment = ( ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + + /* Move to the next item because the current item might get + * removed. */ + pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ); + + /* Continue if this segment does not fall within the ACK'd range. */ + if( xSequenceGreaterThan( ulSequenceNumber, pxSegment->ulSequenceNumber ) != pdFALSE ) + { + continue; + } + + /* Is it ready? */ + if( ulSequenceNumber != pxSegment->ulSequenceNumber ) + { + /* coverity[break_stmt] : Break statement terminating the loop */ + break; + } + + ulDataLength = ( uint32_t ) pxSegment->lDataLength; + + if( pxSegment->u.bits.bAcked == pdFALSE_UNSIGNED ) + { + if( xSequenceGreaterThan( pxSegment->ulSequenceNumber + ( uint32_t ) ulDataLength, ulLast ) != pdFALSE ) + { + /* What happens? Only part of this segment was accepted, + * probably due to WND limits + * + * AAAAAAA BBBBBBB << acked + * aaaaaaa aaaa << sent */ + #if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + uint32_t ulFirstSeq = pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber; + FreeRTOS_debug_printf( ( "prvTCPWindowTxCheckAck[%u.%u]: %u - %u Partial sequence number %u - %u\n", + pxWindow->usPeerPortNumber, + pxWindow->usOurPortNumber, + ( unsigned ) ( ulFirstSeq - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( ulLast - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ulFirstSeq, + ( unsigned ) ( ulFirstSeq + ulDataLength ) ) ); + } + #endif /* ( ipconfigHAS_DEBUG_PRINTF != 0 ) */ + + break; + } + + /* This segment is fully ACK'd, set the flag. */ + pxSegment->u.bits.bAcked = pdTRUE; + + /* Calculate the RTT only if the segment was sent-out for the + * first time and if this is the last ACK'd segment in a range. */ + if( ( pxSegment->u.bits.ucTransmitCount == 1U ) && + ( ( pxSegment->ulSequenceNumber + ulDataLength ) == ulLast ) ) + { + prvTCPWindowTxCheckAck_CalcSRTT( pxWindow, pxSegment ); + } + + /* Unlink it from the 3 queues, but do not destroy it (yet). */ + xDoUnlink = pdTRUE; + } + + /* pxSegment->u.bits.bAcked is now true. Is it located at the left + * side of the transmission queue? If so, it may be freed. */ + if( ulSequenceNumber == pxWindow->tx.ulCurrentSequenceNumber ) + { + if( ( xTCPWindowLoggingLevel >= 2 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) ) ) + { + FreeRTOS_debug_printf( ( "prvTCPWindowTxCheckAck: %u - %u Ready sequence number %u\n", + ( unsigned ) ( ulFirst - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( ulLast - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ) ) ); + } + + /* Increase the left-hand value of the transmission window. */ + pxWindow->tx.ulCurrentSequenceNumber += ulDataLength; + + /* This function will return the number of bytes that the tail + * of txStream may be advanced. */ + ulBytesConfirmed += ulDataLength; + + /* All segments below tx.ulCurrentSequenceNumber may be freed. */ + vTCPWindowFree( pxSegment ); + + /* No need to unlink it any more. */ + xDoUnlink = pdFALSE; + } + + if( ( xDoUnlink != pdFALSE ) && ( listLIST_ITEM_CONTAINER( &( pxSegment->xQueueItem ) ) != NULL ) ) + { + /* Remove item from its queues. */ + ( void ) uxListRemove( &pxSegment->xQueueItem ); + } + + ulSequenceNumber += ulDataLength; + } + + return ulBytesConfirmed; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief See if there are segments that need a fast retransmission. + * + * @param[in] pxWindow: The descriptor of the TCP sliding windows. + * @param[in] ulFirst: The sequence number of the first segment that must be checked. + * + * @return The number of segments that need a fast retransmission. + */ + static uint32_t prvTCPWindowFastRetransmit( TCPWindow_t * pxWindow, + uint32_t ulFirst ) + { + const ListItem_t * pxIterator; + const ListItem_t * pxEnd; + TCPSegment_t * pxSegment; + uint32_t ulCount = 0U; + + /* A higher Tx block has been acknowledged. Now iterate through the + * xWaitQueue to find a possible condition for a FAST retransmission. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxEnd = ( ( const ListItem_t * ) &( pxWindow->xWaitQueue.xListEnd ) ); + + pxIterator = listGET_NEXT( pxEnd ); + + while( pxIterator != pxEnd ) + { + /* Get the owner, which is a TCP segment. */ + pxSegment = ( ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) ); + + /* Hop to the next item before the current gets unlinked. */ + pxIterator = listGET_NEXT( pxIterator ); + + /* Fast retransmission: + * When 3 packets with a higher sequence number have been acknowledged + * by the peer, it is very unlikely a current packet will ever arrive. + * It will be retransmitted far before the RTO. */ + if( pxSegment->u.bits.bAcked == pdFALSE_UNSIGNED ) + { + if( xSequenceLessThan( pxSegment->ulSequenceNumber, ulFirst ) != pdFALSE ) + { + pxSegment->u.bits.ucDupAckCount++; + + if( pxSegment->u.bits.ucDupAckCount == DUPLICATE_ACKS_BEFORE_FAST_RETRANSMIT ) + { + pxSegment->u.bits.ucTransmitCount = ( uint8_t ) pdFALSE; + + /* Not clearing 'ucDupAckCount' yet as more SACK's might come in + * which might lead to a second fast rexmit. */ + if( ( xTCPWindowLoggingLevel >= 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) ) ) + { + FreeRTOS_debug_printf( ( "prvTCPWindowFastRetransmit: Requeue sequence number %u < %u\n", + ( unsigned ) ( pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( ulFirst - pxWindow->tx.ulFirstSequenceNumber ) ) ); + FreeRTOS_flush_logging(); + } + + /* Remove it from xWaitQueue. */ + ( void ) uxListRemove( &pxSegment->xQueueItem ); + + /* Add this segment to the priority queue so it gets + * retransmitted immediately. */ + vListInsertFifo( &( pxWindow->xPriorityQueue ), &( pxSegment->xQueueItem ) ); + ulCount++; + } + } + } + } + + return ulCount; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Receive a normal ACK. + * + * @param[in] pxWindow: Window in which a data is receive. + * @param[in] ulSequenceNumber: The sequence number of the ACK. + * + * @return The location where the packet should be added. + */ + uint32_t ulTCPWindowTxAck( TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber ) + { + uint32_t ulFirstSequence; + uint32_t ulReturn; + + /* Receive a normal ACK. */ + + ulFirstSequence = pxWindow->tx.ulCurrentSequenceNumber; + + if( xSequenceLessThanOrEqual( ulSequenceNumber, ulFirstSequence ) != pdFALSE ) + { + ulReturn = 0U; + } + else + { + ulReturn = prvTCPWindowTxCheckAck( pxWindow, ulFirstSequence, ulSequenceNumber ); + } + + return ulReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 1 ) + +/** + * @brief Receive a SACK option. + * + * @param[in] pxWindow: Window in which the data is received. + * @param[in] ulFirst: Index of starting position of options. + * @param[in] ulLast: Index of end position of the options. + * + * @return returns the number of bytes which have been acked starting from + * the head position. + */ + uint32_t ulTCPWindowTxSack( TCPWindow_t * pxWindow, + uint32_t ulFirst, + uint32_t ulLast ) + { + uint32_t ulAckCount; + uint32_t ulCurrentSequenceNumber = pxWindow->tx.ulCurrentSequenceNumber; + + /* Receive a SACK option. */ + ulAckCount = prvTCPWindowTxCheckAck( pxWindow, ulFirst, ulLast ); + ( void ) prvTCPWindowFastRetransmit( pxWindow, ulFirst ); + + if( ( xTCPWindowLoggingLevel >= 1 ) && ( xSequenceGreaterThan( ulFirst, ulCurrentSequenceNumber ) != pdFALSE ) ) + { + FreeRTOS_debug_printf( ( "ulTCPWindowTxSack[%u,%u]: from %u to %u (ack = %u)\n", + pxWindow->usPeerPortNumber, + pxWindow->usOurPortNumber, + ( unsigned ) ( ulFirst - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( ulLast - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( pxWindow->tx.ulCurrentSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ) ) ); + FreeRTOS_flush_logging(); + } + + return ulAckCount; + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ +/*-----------------------------------------------------------*/ + +#endif /* ipconfigUSE_TCP == 1 */ diff --git a/FreeRTOS/source/FreeRTOS_Tiny_TCP.c b/FreeRTOS/source/FreeRTOS_Tiny_TCP.c new file mode 100644 index 0000000..acc5f52 --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_Tiny_TCP.c @@ -0,0 +1,501 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/*============================================================================= + * + * ##### # ##### #### ###### + * # # # # # # # # # # # + * # # # # # # + * # ### ##### # # # # # # + * # # # # # # # # ##### + * # # # # # # #### # # # + * # # # # # # # # # # + * # # # # #### # # # # + * #### ##### # # # #### #### #### + * # + * ### + * Tiny-TCP: TCP without sliding windows. + * + *=============================================================================*/ + +/** + * @file FreeRTOS_TINY_TCP.c + * @brief Module which handles TCP when windowing is disabled + * + * In this module all ports and IP addresses and sequence numbers are + * being stored in host byte-order. + */ + + +/* Standard includes. */ +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" + +#if ( ipconfigUSE_TCP == 1 ) + + #if ( ipconfigUSE_TCP_WIN == 0 ) + +/** @brief Logging verbosity level. */ + BaseType_t xTCPWindowLoggingLevel = 0; + + static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a, + uint32_t b ); + +/** + * @brief Test if a>=b. This function is required since the sequence numbers can roll over. + * + * @param[in] a: The first sequence number. + * @param[in] b: The second sequence number. + * + * @return pdTRUE if a>=b, else pdFALSE. + */ + + static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a, + uint32_t b ) + { + BaseType_t xResult = pdFALSE; + + /* Test if a >= b */ + if( ( ( a - b ) & 0x80000000U ) == 0U ) + { + xResult = pdTRUE; + } + + return xResult; + } + + static portINLINE void vTCPTimerSet( TCPTimer_t * pxTimer ); + +/** + * @brief Set the timer's "born" time. + * + * @param[in] pxTimer: The TCP timer. + */ + static portINLINE void vTCPTimerSet( TCPTimer_t * pxTimer ) + { + pxTimer->uxBorn = xTaskGetTickCount(); + } +/*-----------------------------------------------------------*/ + + static portINLINE uint32_t ulTimerGetAge( const TCPTimer_t * pxTimer ); + +/** + * @brief Get the timer age in milliseconds. + * + * @param[in] pxTimer: The timer whose age is to be fetched. + * + * @return The time in milliseconds since the timer was born. + */ + static portINLINE uint32_t ulTimerGetAge( const TCPTimer_t * pxTimer ) + { + TickType_t uxNow = xTaskGetTickCount(); + TickType_t uxDiff = uxNow - pxTimer->uxBorn; + + return uxDiff * portTICK_PERIOD_MS; + } +/*-----------------------------------------------------------*/ + +/** + * @brief Data was received at 'ulSequenceNumber'. See if it was expected + * and if there is enough space to store the new data. + * + * @param[in] pxWindow: The window to be checked. + * @param[in] ulSequenceNumber: Sequence number of the data received. + * @param[in] ulLength: Length of the data received. + * @param[in] ulSpace: Space in the buffer. + * + * @return A 0 is returned if there is enough space and the sequence number is correct, + * if not then a -1 is returned. + * + * @note if true may be passed directly to user (segment expected and window is empty). + * But pxWindow->ackno should always be used to set "BUF->ackno". + */ + int32_t lTCPWindowRxCheck( TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber, + uint32_t ulLength, + uint32_t ulSpace, + uint32_t * pulSkipCount ) + { + int32_t lReturn = -1; + + *pulSkipCount = 0; + + /* Data was received at 'ulSequenceNumber'. See if it was expected + * and if there is enough space to store the new data. */ + if( ( pxWindow->rx.ulCurrentSequenceNumber != ulSequenceNumber ) || ( ulSpace < ulLength ) ) + { + lReturn = -1; + } + else + { + pxWindow->rx.ulCurrentSequenceNumber += ( uint32_t ) ulLength; + lReturn = 0; + } + + return lReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 0 ) + +/** + * @brief Add data to the Tx Window. + * + * @param[in] pxWindow: The window to which the data is to be added. + * @param[in] ulLength: The length of the data to be added. + * @param[in] lPosition: Position in the stream. + * @param[in] lMax: Size of the Tx stream. + * + * @return The data actually added. + */ + int32_t lTCPWindowTxAdd( TCPWindow_t * pxWindow, + uint32_t ulLength, + int32_t lPosition, + int32_t lMax ) + { + TCPSegment_t * pxSegment = &( pxWindow->xTxSegment ); + int32_t lResult; + + /* Data is being scheduled for transmission. */ + + /* lMax would indicate the size of the txStream. */ + ( void ) lMax; + + /* This is tiny TCP: there is only 1 segment for outgoing data. + * As long as 'lDataLength' is unequal to zero, the segment is still occupied. */ + if( pxSegment->lDataLength > 0 ) + { + lResult = 0L; + } + else + { + if( ulLength > ( uint32_t ) pxSegment->lMaxLength ) + { + if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) ) + { + FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: can only store %u / %d bytes\n", ( unsigned ) ulLength, ( int ) pxSegment->lMaxLength ) ); + } + + ulLength = ( uint32_t ) pxSegment->lMaxLength; + } + + if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) ) + { + FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: SeqNr %u (%u) Len %u\n", + ( unsigned ) ( pxWindow->ulNextTxSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( pxWindow->tx.ulCurrentSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ulLength ) ); + } + + /* The sequence number of the first byte in this packet. */ + pxSegment->ulSequenceNumber = pxWindow->ulNextTxSequenceNumber; + pxSegment->lDataLength = ( int32_t ) ulLength; + pxSegment->lStreamPos = lPosition; + pxSegment->u.ulFlags = 0U; + vTCPTimerSet( &( pxSegment->xTransmitTimer ) ); + + /* Increase the sequence number of the next data to be stored for + * transmission. */ + pxWindow->ulNextTxSequenceNumber += ulLength; + lResult = ( int32_t ) ulLength; + } + + return lResult; + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 0 ) + +/** + * @brief Fetches data to be sent. + * + * @param[in] pxWindow: The window for the connection. + * @param[in] ulWindowSize: The size of the window. + * @param[out] plPosition: plPosition will point to a location with the circular data buffer: txStream. + * + * @return return the amount of data which may be sent along with the position in the txStream. + */ + uint32_t ulTCPWindowTxGet( TCPWindow_t * pxWindow, + uint32_t ulWindowSize, + int32_t * plPosition ) + { + TCPSegment_t * pxSegment = &( pxWindow->xTxSegment ); + uint32_t ulLength = ( uint32_t ) pxSegment->lDataLength; + uint32_t ulMaxTime; + + if( ulLength != 0U ) + { + /* _HT_ Still under investigation */ + ( void ) ulWindowSize; + + if( pxSegment->u.bits.bOutstanding != pdFALSE_UNSIGNED ) + { + /* As 'ucTransmitCount' has a minimum of 1, take 2 * RTT */ + ulMaxTime = ( ( uint32_t ) 1U ) << pxSegment->u.bits.ucTransmitCount; + ulMaxTime *= ( uint32_t ) pxWindow->lSRTT; + + if( ulTimerGetAge( &( pxSegment->xTransmitTimer ) ) < ulMaxTime ) + { + ulLength = 0U; + } + } + + if( ulLength != 0U ) + { + pxSegment->u.bits.bOutstanding = pdTRUE_UNSIGNED; + pxSegment->u.bits.ucTransmitCount++; + vTCPTimerSet( &pxSegment->xTransmitTimer ); + pxWindow->ulOurSequenceNumber = pxSegment->ulSequenceNumber; + *plPosition = pxSegment->lStreamPos; + } + } + + return ulLength; + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 0 ) + +/** + * @brief Has the transmission completed. + * + * @param[in] pxWindow: The window whose transmission window is to be checked. + * + * @return If there is no outstanding data then pdTRUE is returned, + * else pdFALSE. + */ + BaseType_t xTCPWindowTxDone( const TCPWindow_t * pxWindow ) + { + BaseType_t xReturn; + + /* Has the outstanding data been sent because user wants to shutdown? */ + if( pxWindow->xTxSegment.lDataLength == 0 ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 0 ) + static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t const * pxWindow, + uint32_t ulWindowSize ); + +/** + * @brief Check if the window has space for one message. + * + * @param[in] pxWindow: The window to be checked. + * @param[in] ulWindowSize: Size of the window. + * + * @return pdTRUE if the window has space, pdFALSE otherwise. + */ + static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t const * pxWindow, + uint32_t ulWindowSize ) + { + BaseType_t xReturn; + + if( ulWindowSize >= pxWindow->usMSSInit ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 0 ) + +/** + * @brief Check data to be sent and calculate the time period the process may sleep. + * + * @param[in] pxWindow: The window to be checked. + * @param[in] ulWindowSize: Size of the window. + * @param[out] pulDelay: The time period (in ticks) that the process may sleep. + * + * @return pdTRUE if the process should sleep or pdFALSE. + */ + BaseType_t xTCPWindowTxHasData( TCPWindow_t const * pxWindow, + uint32_t ulWindowSize, + TickType_t * pulDelay ) + { + TCPSegment_t const * pxSegment = &( pxWindow->xTxSegment ); + BaseType_t xReturn; + TickType_t ulAge, ulMaxAge; + + /* Check data to be sent. */ + *pulDelay = ( TickType_t ) 0; + + if( pxSegment->lDataLength == 0 ) + { + /* Got nothing to send right now. */ + xReturn = pdFALSE; + } + else + { + if( pxSegment->u.bits.bOutstanding != pdFALSE_UNSIGNED ) + { + ulAge = ulTimerGetAge( &pxSegment->xTransmitTimer ); + ulMaxAge = ( ( TickType_t ) 1U << pxSegment->u.bits.ucTransmitCount ) * ( ( uint32_t ) pxWindow->lSRTT ); + + if( ulMaxAge > ulAge ) + { + *pulDelay = ulMaxAge - ulAge; + } + + xReturn = pdTRUE; + } + else if( prvTCPWindowTxHasSpace( pxWindow, ulWindowSize ) == pdFALSE ) + { + /* Too many outstanding messages. */ + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + } + + return xReturn; + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 0 ) + +/** + * @brief Receive a normal ACK. + * + * @param[in] pxWindow: The window for this particular connection. + * @param[in] ulSequenceNumber: The sequence number of the packet. + * + * @return Number of bytes to send. + */ + uint32_t ulTCPWindowTxAck( TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber ) + { + TCPSegment_t * pxSegment = &( pxWindow->xTxSegment ); + uint32_t ulDataLength = ( uint32_t ) pxSegment->lDataLength; + + /* Receive a normal ACK */ + + if( ulDataLength != 0U ) + { + if( ulSequenceNumber < ( pxWindow->tx.ulCurrentSequenceNumber + ulDataLength ) ) + { + if( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) + { + FreeRTOS_debug_printf( ( "win_tx_ack: acked %u expc %u len %u\n", + ( unsigned ) ( ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ( pxWindow->tx.ulCurrentSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ulDataLength ) ); + } + + /* Nothing to send right now. */ + ulDataLength = 0U; + } + else + { + pxWindow->tx.ulCurrentSequenceNumber += ulDataLength; + + if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) ) + { + FreeRTOS_debug_printf( ( "win_tx_ack: acked seqnr %u len %u\n", + ( unsigned ) ( ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), + ( unsigned ) ulDataLength ) ); + } + + pxSegment->lDataLength = 0; + } + } + + return ulDataLength; + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 0 ) + +/** + * @brief This function will be called as soon as a FIN is received to check + * whether all transmit queues are empty or not. + * + * @param[in] pxWindow: The window to be checked. + * + * @return It will return true if there are no 'open' reception segments. + */ + BaseType_t xTCPWindowRxEmpty( const TCPWindow_t * pxWindow ) + { + /* Return true if 'ulCurrentSequenceNumber >= ulHighestSequenceNumber' + * 'ulCurrentSequenceNumber' is the highest sequence number stored, + * 'ulHighestSequenceNumber' is the highest sequence number seen. */ + return xSequenceGreaterThanOrEqual( pxWindow->rx.ulCurrentSequenceNumber, pxWindow->rx.ulHighestSequenceNumber ); + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +/*-----------------------------------------------------------*/ + + #if ( ipconfigUSE_TCP_WIN == 0 ) + +/** + * @brief Destroy a window. + * + * @param[in] pxWindow: Pointer to the window to be destroyed. + * + * @return Always returns a NULL. + */ + void vTCPWindowDestroy( const TCPWindow_t * pxWindow ) + { + /* As in tiny TCP there are no shared segments descriptors, there is + * nothing to release. */ + ( void ) pxWindow; + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +/*-----------------------------------------------------------*/ + +#endif /* ipconfigUSE_TCP == 1 */ diff --git a/FreeRTOS/source/FreeRTOS_UDP_IP.c b/FreeRTOS/source/FreeRTOS_UDP_IP.c new file mode 100644 index 0000000..d8819da --- /dev/null +++ b/FreeRTOS/source/FreeRTOS_UDP_IP.c @@ -0,0 +1,501 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_UDP_IP.c + * @brief This file has the source code for the UDP-IP functionality of the FreeRTOS+TCP + * network stack. + */ + +/* Standard includes. */ +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" +#include "event_groups.h" +#include "list.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_DNS.h" +#include "FreeRTOS_DHCP.h" +#include "FreeRTOS_IP_Utils.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" + +#if ( ipconfigUSE_DNS == 1 ) + #include "FreeRTOS_DNS.h" +#endif + +/** @brief The expected IP version and header length coded into the IP header itself. */ +#define ipIP_VERSION_AND_HEADER_LENGTH_BYTE ( ( uint8_t ) 0x45 ) + +/** @brief Part of the Ethernet and IP headers are always constant when sending an IPv4 + * UDP packet. This array defines the constant parts, allowing this part of the + * packet to be filled in using a simple memcpy() instead of individual writes. */ +/*lint -e708 (Info -- union initialization). */ +UDPPacketHeader_t xDefaultPartUDPPacketHeader = +{ + /* .ucBytes : */ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Ethernet source MAC address. */ + 0x08, 0x00, /* Ethernet frame type. */ + ipIP_VERSION_AND_HEADER_LENGTH_BYTE, /* ucVersionHeaderLength. */ + 0x00, /* ucDifferentiatedServicesCode. */ + 0x00, 0x00, /* usLength. */ + 0x00, 0x00, /* usIdentification. */ + 0x00, 0x00, /* usFragmentOffset. */ + ipconfigUDP_TIME_TO_LIVE, /* ucTimeToLive */ + ipPROTOCOL_UDP, /* ucProtocol. */ + 0x00, 0x00, /* usHeaderChecksum. */ + 0x00, 0x00, 0x00, 0x00 /* Source IP address. */ + } +}; +/*-----------------------------------------------------------*/ + +/** + * @brief Process the generated UDP packet and do other checks before sending the + * packet such as ARP cache check and address resolution. + * + * @param[in] pxNetworkBuffer: The network buffer carrying the packet. + */ +void vProcessGeneratedUDPPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ + UDPPacket_t * pxUDPPacket; + IPHeader_t * pxIPHeader; + eARPLookupResult_t eReturned; + uint32_t ulIPAddress = pxNetworkBuffer->ulIPAddress; + size_t uxPayloadSize; + /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ + const void * pvCopySource; + void * pvCopyDest; + + /* Map the UDP packet onto the start of the frame. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxUDPPacket = ( ( UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); + + #if ipconfigSUPPORT_OUTGOING_PINGS == 1 + if( pxNetworkBuffer->usPort == ( uint16_t ) ipPACKET_CONTAINS_ICMP_DATA ) + { + uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( ICMPPacket_t ); + } + else + #endif + { + uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t ); + } + + /* Determine the ARP cache status for the requested IP address. */ + eReturned = eARPGetCacheEntry( &( ulIPAddress ), &( pxUDPPacket->xEthernetHeader.xDestinationAddress ) ); + + if( eReturned != eCantSendPacket ) + { + if( eReturned == eARPCacheHit ) + { + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) + uint8_t ucSocketOptions; + #endif + iptraceSENDING_UDP_PACKET( pxNetworkBuffer->ulIPAddress ); + + /* Create short cuts to the data within the packet. */ + pxIPHeader = &( pxUDPPacket->xIPHeader ); + + #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) + + /* Is it possible that the packet is not actually a UDP packet + * after all, but an ICMP packet. */ + if( pxNetworkBuffer->usPort != ( uint16_t ) ipPACKET_CONTAINS_ICMP_DATA ) + #endif /* ipconfigSUPPORT_OUTGOING_PINGS */ + { + UDPHeader_t * pxUDPHeader; + + pxUDPHeader = &( pxUDPPacket->xUDPHeader ); + + pxUDPHeader->usDestinationPort = pxNetworkBuffer->usPort; + pxUDPHeader->usSourcePort = pxNetworkBuffer->usBoundPort; + pxUDPHeader->usLength = ( uint16_t ) ( uxPayloadSize + sizeof( UDPHeader_t ) ); + pxUDPHeader->usLength = FreeRTOS_htons( pxUDPHeader->usLength ); + pxUDPHeader->usChecksum = 0U; + } + + /* memcpy() the constant parts of the header information into + * the correct location within the packet. This fills in: + * xEthernetHeader.xSourceAddress + * xEthernetHeader.usFrameType + * xIPHeader.ucVersionHeaderLength + * xIPHeader.ucDifferentiatedServicesCode + * xIPHeader.usLength + * xIPHeader.usIdentification + * xIPHeader.usFragmentOffset + * xIPHeader.ucTimeToLive + * xIPHeader.ucProtocol + * and + * xIPHeader.usHeaderChecksum + */ + + /* Save options now, as they will be overwritten by memcpy */ + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) + { + ucSocketOptions = pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ]; + } + #endif + + /* + * Offset the memcpy by the size of a MAC address to start at the packet's + * Ethernet header 'source' MAC address; the preceding 'destination' should not be altered. + */ + + /* + * Use helper variables for memcpy() to remain + * compliant with MISRA Rule 21.15. These should be + * optimized away. + */ + pvCopySource = xDefaultPartUDPPacketHeader.ucBytes; + /* The Ethernet source address is at offset 6. */ + pvCopyDest = &pxNetworkBuffer->pucEthernetBuffer[ sizeof( MACAddress_t ) ]; + ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( xDefaultPartUDPPacketHeader ) ); + + #if ipconfigSUPPORT_OUTGOING_PINGS == 1 + if( pxNetworkBuffer->usPort == ( uint16_t ) ipPACKET_CONTAINS_ICMP_DATA ) + { + pxIPHeader->ucProtocol = ipPROTOCOL_ICMP; + pxIPHeader->usLength = ( uint16_t ) ( uxPayloadSize + sizeof( IPHeader_t ) + sizeof( ICMPHeader_t ) ); + } + else + #endif /* ipconfigSUPPORT_OUTGOING_PINGS */ + { + pxIPHeader->usLength = ( uint16_t ) ( uxPayloadSize + sizeof( IPHeader_t ) + sizeof( UDPHeader_t ) ); + } + + pxIPHeader->usLength = FreeRTOS_htons( pxIPHeader->usLength ); + pxIPHeader->ulDestinationIPAddress = pxNetworkBuffer->ulIPAddress; + + /* The stack doesn't support fragments, so the fragment offset field must always be zero. + * The header was never memset to zero, so set both the fragment offset and fragmentation flags in one go. + */ + #if ( ipconfigFORCE_IP_DONT_FRAGMENT != 0 ) + pxIPHeader->usFragmentOffset = ipFRAGMENT_FLAGS_DONT_FRAGMENT; + #else + pxIPHeader->usFragmentOffset = 0U; + #endif + + #if ( ipconfigUSE_LLMNR == 1 ) + { + /* LLMNR messages are typically used on a LAN and they're + * not supposed to cross routers */ + if( pxNetworkBuffer->ulIPAddress == ipLLMNR_IP_ADDR ) + { + pxIPHeader->ucTimeToLive = 0x01; + } + } + #endif + + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) + { + pxIPHeader->usHeaderChecksum = 0U; + pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); + pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); + + if( ( ucSocketOptions & ( uint8_t ) FREERTOS_SO_UDPCKSUM_OUT ) != 0U ) + { + ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxUDPPacket, pxNetworkBuffer->xDataLength, pdTRUE ); + } + else + { + pxUDPPacket->xUDPHeader.usChecksum = 0U; + } + } + #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) */ + } + else if( eReturned == eARPCacheMiss ) + { + /* Add an entry to the ARP table with a null hardware address. + * This allows the ARP timer to know that an ARP reply is + * outstanding, and perform retransmissions if necessary. */ + vARPRefreshCacheEntry( NULL, ulIPAddress ); + + /* Generate an ARP for the required IP address. */ + iptracePACKET_DROPPED_TO_GENERATE_ARP( pxNetworkBuffer->ulIPAddress ); + pxNetworkBuffer->ulIPAddress = ulIPAddress; + vARPGenerateRequestPacket( pxNetworkBuffer ); + } + else + { + /* The lookup indicated that an ARP request has already been + * sent out for the queried IP address. */ + eReturned = eCantSendPacket; + } + } + + if( eReturned != eCantSendPacket ) + { + /* The network driver is responsible for freeing the network buffer + * after the packet has been sent. */ + + #if ( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) + { + if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES ) + { + BaseType_t xIndex; + + for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ ) + { + pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0U; + } + + pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; + } + } + #endif /* if( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) */ + iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer ); + ( void ) xNetworkInterfaceOutput( pxNetworkBuffer, pdTRUE ); + } + else + { + /* The packet can't be sent (DHCP not completed?). Just drop the + * packet. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief Process the received UDP packet. + * + * @param[in] pxNetworkBuffer: The network buffer carrying the UDP packet. + * @param[in] usPort: The port number on which this packet was received. + * @param[out] pxIsWaitingForARPResolution: If the packet is awaiting ARP resolution, + * this pointer will be set to pdTRUE. pdFALSE otherwise. + * + * @return pdPASS in case the UDP packet could be processed. Else pdFAIL is returned. + */ +BaseType_t xProcessReceivedUDPPacket( NetworkBufferDescriptor_t * pxNetworkBuffer, + uint16_t usPort, + BaseType_t * pxIsWaitingForARPResolution ) +{ + BaseType_t xReturn = pdPASS; + FreeRTOS_Socket_t * pxSocket; + + configASSERT( pxNetworkBuffer != NULL ); + configASSERT( pxNetworkBuffer->pucEthernetBuffer != NULL ); + + /* Map the ethernet buffer to the UDPPacket_t struct for easy access to the fields. */ + + /* MISRA Ref 11.3.1 [Misaligned access] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + const UDPPacket_t * pxUDPPacket = ( ( const UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); + + /* Caller must check for minimum packet size. */ + pxSocket = pxUDPSocketLookup( usPort ); + + *pxIsWaitingForARPResolution = pdFALSE; + + do + { + if( pxSocket != NULL ) + { + if( *ipLOCAL_IP_ADDRESS_POINTER != 0U ) + { + if( xCheckRequiresARPResolution( pxNetworkBuffer ) == pdTRUE ) + { + /* Mark this packet as waiting for ARP resolution. */ + *pxIsWaitingForARPResolution = pdTRUE; + + /* Return a fail to show that the frame will not be processed right now. */ + xReturn = pdFAIL; + break; + } + else + { + /* IP address is not on the same subnet, ARP table can be updated. + * When refreshing the ARP cache with received UDP packets we must be + * careful; hundreds of broadcast messages may pass and if we're not + * handling them, no use to fill the ARP cache with those IP addresses. + */ + vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress ); + } + } + else + { + /* During DHCP, IP address is not assigned and therefore ARP verification + * is not possible. */ + } + + #if ( ipconfigUSE_CALLBACKS == 1 ) + { + /* Did the owner of this socket register a reception handler ? */ + if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xUDP.pxHandleReceive ) ) + { + struct freertos_sockaddr xSourceAddress, destinationAddress; + void * pcData = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ); + FOnUDPReceive_t xHandler = ( FOnUDPReceive_t ) pxSocket->u.xUDP.pxHandleReceive; + xSourceAddress.sin_port = pxNetworkBuffer->usPort; + xSourceAddress.sin_addr = pxNetworkBuffer->ulIPAddress; + destinationAddress.sin_port = usPort; + destinationAddress.sin_addr = pxUDPPacket->xIPHeader.ulDestinationIPAddress; + + /* The value of 'xDataLength' was proven to be at least the size of a UDP packet in prvProcessIPPacket(). */ + if( xHandler( ( Socket_t ) pxSocket, + ( void * ) pcData, + ( size_t ) ( pxNetworkBuffer->xDataLength - ipUDP_PAYLOAD_OFFSET_IPv4 ), + &( xSourceAddress ), + &( destinationAddress ) ) != 0 ) + { + xReturn = pdFAIL; /* xHandler has consumed the data, do not add it to .xWaitingPacketsList'. */ + } + } + } + #endif /* ipconfigUSE_CALLBACKS */ + + #if ( ipconfigUDP_MAX_RX_PACKETS > 0U ) + { + if( xReturn == pdPASS ) + { + if( listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) >= pxSocket->u.xUDP.uxMaxPackets ) + { + FreeRTOS_debug_printf( ( "xProcessReceivedUDPPacket: buffer full %ld >= %ld port %u\n", + listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ), + pxSocket->u.xUDP.uxMaxPackets, pxSocket->usLocalPort ) ); + xReturn = pdFAIL; /* we did not consume or release the buffer */ + } + } + } + #endif /* if ( ipconfigUDP_MAX_RX_PACKETS > 0U ) */ + + #if ( ipconfigUSE_CALLBACKS == 1 ) || ( ipconfigUDP_MAX_RX_PACKETS > 0U ) + if( xReturn == pdPASS ) /*lint !e774: Boolean within 'if' always evaluates to True, depending on configuration. [MISRA 2012 Rule 14.3, required. */ + #else + /* xReturn is still pdPASS. */ + #endif + { + vTaskSuspendAll(); + { + taskENTER_CRITICAL(); + { + /* Add the network packet to the list of packets to be + * processed by the socket. */ + vListInsertEnd( &( pxSocket->u.xUDP.xWaitingPacketsList ), &( pxNetworkBuffer->xBufferListItem ) ); + } + taskEXIT_CRITICAL(); + } + ( void ) xTaskResumeAll(); + + /* Set the socket's receive event */ + if( pxSocket->xEventGroup != NULL ) + { + ( void ) xEventGroupSetBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_RECEIVE ); + } + + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + if( ( pxSocket->pxSocketSet != NULL ) && ( ( pxSocket->xSelectBits & ( ( EventBits_t ) eSELECT_READ ) ) != 0U ) ) + { + ( void ) xEventGroupSetBits( pxSocket->pxSocketSet->xSelectGroup, ( EventBits_t ) eSELECT_READ ); + } + } + #endif + + #if ( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 ) + { + if( pxSocket->pxUserSemaphore != NULL ) + { + ( void ) xSemaphoreGive( pxSocket->pxUserSemaphore ); + } + } + #endif + + #if ( ipconfigUSE_DHCP == 1 ) + { + if( xIsDHCPSocket( pxSocket ) != 0 ) + { + ( void ) xSendDHCPEvent(); + } + } + #endif + } + } + else + { + /* There is no socket listening to the target port, but still it might + * be for this node. */ + + #if ( ipconfigUSE_DNS == 1 ) && ( ipconfigDNS_USE_CALLBACKS == 1 ) + + /* A DNS reply, check for the source port. Although the DNS client + * does open a UDP socket to send a messages, this socket will be + * closed after a short timeout. Messages that come late (after the + * socket is closed) will be treated here. */ + if( FreeRTOS_ntohs( pxUDPPacket->xUDPHeader.usSourcePort ) == ( uint16_t ) ipDNS_PORT ) + { + vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress ); + xReturn = ( BaseType_t ) ulDNSHandlePacket( pxNetworkBuffer ); + } + else + #endif + + #if ( ipconfigUSE_LLMNR == 1 ) + /* A LLMNR request, check for the destination port. */ + if( ( usPort == FreeRTOS_ntohs( ipLLMNR_PORT ) ) || + ( pxUDPPacket->xUDPHeader.usSourcePort == FreeRTOS_ntohs( ipLLMNR_PORT ) ) ) + { + vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress ); + xReturn = ( BaseType_t ) ulDNSHandlePacket( pxNetworkBuffer ); + } + else + #endif /* ipconfigUSE_LLMNR */ + + #if ( ipconfigUSE_NBNS == 1 ) + /* a NetBIOS request, check for the destination port */ + if( ( usPort == FreeRTOS_ntohs( ipNBNS_PORT ) ) || + ( pxUDPPacket->xUDPHeader.usSourcePort == FreeRTOS_ntohs( ipNBNS_PORT ) ) ) + { + vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress ); + xReturn = ( BaseType_t ) ulNBNSHandlePacket( pxNetworkBuffer ); + } + else + #endif /* ipconfigUSE_NBNS */ + { + xReturn = pdFAIL; + } + } + } while( ipFALSE_BOOL ); + + return xReturn; +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/include/FreeRTOSIPConfigDefaults.h b/FreeRTOS/source/include/FreeRTOSIPConfigDefaults.h new file mode 100644 index 0000000..ede30e7 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOSIPConfigDefaults.h @@ -0,0 +1,1071 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_DEFAULT_IP_CONFIG_H +#define FREERTOS_DEFAULT_IP_CONFIG_H + +/* This file must be included only after the configuration files FreeRTOSConfig.h and + * FreeRTOSIPConfig.h have been included already. + * Mentioned header files are private to the project, whereas this header + * file is part of FreeRTOS+TCP. + * + * The complete documentation of the configuration parameters can be found here: + * + * https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/TCP_IP_Configuration.html + * + */ + +/* The error numbers defined in this file will be moved to the core FreeRTOS + * code in future versions of FreeRTOS - at which time the following header file + * will be removed. */ +#include "FreeRTOS_errno_TCP.h" + +/* This file provides default values for configuration options that are missing + * from the FreeRTOSIPConfig.h configuration header file. */ + +/* These macros are used to define away static keyword for CBMC proofs */ +#ifndef _static + #define _static static +#endif + +/* Through time some macro names have changed. This always + * happened for a good reason: clarity or consistency. + * Here below there are some ifdef's that will issue an error if any of the + * deprecated macros is still defined. */ + +/* Ensure defined configuration constants are using the most up to date naming. */ +#ifdef tcpconfigIP_TIME_TO_LIVE + #error now called: ipconfigTCP_TIME_TO_LIVE +#endif + +#ifdef updconfigIP_TIME_TO_LIVE + #error now called: ipconfigUDP_TIME_TO_LIVE +#endif + +#ifdef ipFILLER_SIZE + #error now called: ipconfigPACKET_FILLER_SIZE +#endif + +#ifdef dnsMAX_REQUEST_ATTEMPTS + #error now called: ipconfigDNS_REQUEST_ATTEMPTS +#endif + +#ifdef ipconfigUDP_TASK_PRIORITY + #error now called: ipconfigIP_TASK_PRIORITY +#endif + +#ifdef ipconfigUDP_TASK_STACK_SIZE_WORDS + #error now called: ipconfigIP_TASK_STACK_SIZE_WORDS +#endif + +#ifdef ipconfigDRIVER_INCLUDED_RX_IP_FILTERING + #error now called: ipconfigETHERNET_DRIVER_FILTERS_PACKETS +#endif + +#ifdef ipconfigMAX_SEND_BLOCK_TIME_TICKS + #error now called: ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS +#endif + +#ifdef ipconfigUSE_RECEIVE_CONNECT_CALLBACKS + #error now called: ipconfigUSE_CALLBACKS +#endif + +#ifdef ipconfigNUM_NETWORK_BUFFERS + #error now called: ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS +#endif + +#ifdef ipconfigTCP_HANG_PROT + #error now called: ipconfigTCP_HANG_PROTECTION +#endif + +#ifdef ipconfigTCP_HANG_PROT_TIME + #error now called: ipconfigTCP_HANG_PROTECTION_TIME +#endif + +#ifdef FreeRTOS_lprintf + #error now called: FreeRTOS_debug_printf +#endif + +#ifdef ipconfigBUFFER_ALLOC_FIXED_SIZE + #error ipconfigBUFFER_ALLOC_FIXED_SIZE was dropped and replaced by a const value, declared in BufferAllocation[12].c +#endif + +#ifdef ipconfigNIC_SEND_PASSES_DMA + #error now called: ipconfigZERO_COPY_TX_DRIVER +#endif + +#ifdef HAS_TX_CRC_OFFLOADING + #error now called: ipconfigHAS_TX_CRC_OFFLOADING +#endif + +#ifdef HAS_RX_CRC_OFFLOADING + #error now called: ipconfigHAS_RX_CRC_OFFLOADING +#endif + +#ifdef ipconfigTCP_RX_BUF_LEN + #error ipconfigTCP_RX_BUF_LEN is now called ipconfigTCP_RX_BUFFER_LENGTH +#endif + +#ifdef ipconfigTCP_TX_BUF_LEN + #error ipconfigTCP_TX_BUF_LEN is now called ipconfigTCP_TX_BUFFER_LENGTH +#endif + +#ifdef ipconfigDHCP_USES_USER_HOOK + #error ipconfigDHCP_USES_USER_HOOK and its associated callback have been superseded - see http: /*www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/TCP_IP_Configuration.html#ipconfigUSE_DHCP_HOOK */ +#endif + +/* The macro 'ipconfigBYTE_ORDER' must be defined as either 'pdFREERTOS_LITTLE_ENDIAN' + * or as 'pdFREERTOS_BIG_ENDIAN'. See also 'projdefs.h'. + */ +#ifndef ipconfigBYTE_ORDER + #error The macro 'ipconfigBYTE_ORDER' must be defined at this point +#endif + +/* So far the warnings about deprecated configuration macros. */ + +/*-----------------------------------------------------------*/ + +/* The IP stack executes it its own task (although any application task can make + * use of its services through the published sockets API). ipconfigUDP_TASK_PRIORITY + * sets the priority of the task that executes the IP stack. The priority is a + * standard FreeRTOS task priority so can take any value from 0 (the lowest + * priority) to (configMAX_PRIORITIES - 1) (the highest priority). + * configMAX_PRIORITIES is a standard FreeRTOS configuration parameter defined in + * FreeRTOSConfig.h, not FreeRTOSIPConfig.h. Consideration needs to be given as to + * the priority assigned to the task executing the IP stack relative to the + * priority assigned to tasks that use the IP stack. */ +#ifndef ipconfigIP_TASK_PRIORITY + #define ipconfigIP_TASK_PRIORITY ( configMAX_PRIORITIES - 2 ) +#endif + +/* The size, in words (not bytes), of the stack allocated to the FreeRTOS+TCP + * task. This setting is less important when the FreeRTOS Win32 simulator is used + * as the Win32 simulator only stores a fixed amount of information on the task + * stack. FreeRTOS includes optional stack overflow detection, see: + * http://www.freertos.org/Stacks-and-stack-overflow-checking.html */ +#ifndef ipconfigIP_TASK_STACK_SIZE_WORDS + #define ipconfigIP_TASK_STACK_SIZE_WORDS ( configMINIMAL_STACK_SIZE * 5U ) +#endif + +/* Include all API's and code that is needed for the TCP protocol. + * When defined as zero, the application is UDP-only. */ +#ifndef ipconfigUSE_TCP + #define ipconfigUSE_TCP ( 1 ) +#endif + +#if ( ipconfigUSE_TCP != 0 ) + +/* 'ipconfigUSE_TCP_WIN' enables support for TCP sliding windows. When + * defined as zero, each TCP packet must be acknowledged individually. + * That will be slower, but it will result less code. */ + #ifndef ipconfigUSE_TCP_WIN + #define ipconfigUSE_TCP_WIN ( 1 ) + #endif + +/* The total number of outstanding TCP segments, either outgoing or incoming. + * This only applies when 'ipconfigUSE_TCP_WIN' is enabled. */ + #ifndef ipconfigTCP_WIN_SEG_COUNT + #define ipconfigTCP_WIN_SEG_COUNT ( 256 ) + #endif + +/* When non-zero, TCP will not send RST packets in reply to + * TCP packets which are unknown, or out-of-order. + * This is an option used for testing. It is recommended to + * define it as '0'. */ + #ifndef ipconfigIGNORE_UNKNOWN_PACKETS + #define ipconfigIGNORE_UNKNOWN_PACKETS ( 0 ) + #endif +#endif /* if ipconfigUSE_TCP */ + +/* + * For debugging/logging: check if the port number is used for e.g. telnet. + * Some events will not be logged for telnet connections + * because it would produce logging about the transmission of the logging... + * This macro will only be used if FreeRTOS_debug_printf() is defined for logging. + * Note that the parameter 'xPort' is already converted to host-endian. + */ +#ifndef ipconfigTCP_MAY_LOG_PORT + #define ipconfigTCP_MAY_LOG_PORT( xPort ) ( ( xPort ) != 23U ) +#endif + +/* Determine the number of clock ticks that the API's FreeRTOS_recv() and + * FreeRTOS_recvfrom() must wait for incoming data. */ +#ifndef ipconfigSOCK_DEFAULT_RECEIVE_BLOCK_TIME + #define ipconfigSOCK_DEFAULT_RECEIVE_BLOCK_TIME portMAX_DELAY +#endif + +/* Determine the number of clock ticks that FreeRTOS_send() must wait + * for space in the transmission buffer. + * For FreeRTOS_sendto(), it limits how long the application + * should wait for a network buffer to become available. + */ +#ifndef ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME + #define ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME portMAX_DELAY +#endif + +/* When sending a UDP packet, a network buffer must be obtained. This macro + * will limit the maximum waiting time that is configured with e.g. + * ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME, or when setting the socket option + * FREERTOS_SO_SNDTIMEO. + * It serves as a protection against a possible dead-lock; a task waiting + * for itself to release a network buffer. + */ +#ifndef ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS + #define ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS ( pdMS_TO_TICKS( 20U ) ) +#endif + +/* + * FreeRTOS debug logging routines. + * The macro will be called with a printf() format as a parameter. Users + * can define their own logging routine as: + * + * extern void my_printf( const char * pcFormat, ... ); + * #define FreeRTOS_debug_printf( MSG ) my_printf MSG + * + * The FreeRTOS_debug_printf() must be thread-safe but does not have to be + * interrupt-safe. + */ +#ifdef ipconfigHAS_DEBUG_PRINTF + #if ( ipconfigHAS_DEBUG_PRINTF == 0 ) + #ifdef FreeRTOS_debug_printf + #error Do not define FreeRTOS_debug_print if ipconfigHAS_DEBUG_PRINTF is set to 0 + #endif /* ifdef FreeRTOS_debug_printf */ + #endif /* ( ipconfigHAS_DEBUG_PRINTF == 0 ) */ +#endif /* ifdef ipconfigHAS_DEBUG_PRINTF */ + +#ifndef FreeRTOS_debug_printf + #define FreeRTOS_debug_printf( MSG ) do {} while( ipFALSE_BOOL ) + /* MISRA Ref 20.5.1 [Use of undef] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-2051 */ + /* coverity[misra_c_2012_rule_20_5_violation] */ + #undef ipconfigHAS_DEBUG_PRINTF + #define ipconfigHAS_DEBUG_PRINTF 0 +#endif + +/* + * FreeRTOS general logging routine (proposal) + * Used in some utility functions such as FreeRTOS_netstat() and FreeRTOS_PrintARPCache() + * + * extern void my_printf( const char * pcFormat, ... ); + * #define FreeRTOS_printf( MSG ) my_printf MSG + * + * The FreeRTOS_printf() must be thread-safe but does not have to be interrupt-safe + */ +#ifdef ipconfigHAS_PRINTF + #if ( ipconfigHAS_PRINTF == 0 ) + #ifdef FreeRTOS_printf + #error Do not define FreeRTOS_print if ipconfigHAS_PRINTF is set to 0 + #endif /* ifdef FreeRTOS_debug_printf */ + #endif /* ( ipconfigHAS_PRINTF == 0 ) */ +#endif /* ifdef ipconfigHAS_PRINTF */ + +#ifndef FreeRTOS_printf + #define FreeRTOS_printf( MSG ) do {} while( ipFALSE_BOOL ) + /* MISRA Ref 20.5.1 [Use of undef] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-2051 */ + /* coverity[misra_c_2012_rule_20_5_violation] */ + #undef ipconfigHAS_PRINTF + #define ipconfigHAS_PRINTF 0 +#endif + +/* + * In cases where a lot of logging is produced, FreeRTOS_flush_logging( ) + * will be called to give the logging module a chance to flush the data. + */ +#ifndef FreeRTOS_flush_logging + #define FreeRTOS_flush_logging() do {} while( ipFALSE_BOOL ) +#endif + +/* Malloc functions. Within most applications of FreeRTOS, the couple + * pvPortMalloc()/vPortFree() will be used. + * If there are different types of RAM, the user may decide to use a different + * memory allocator for different purposes: + * MallocLarge is used to allocate large TCP buffers (for Rx/Tx) + * MallocSocket is used to allocate the space for the sockets + */ +#ifndef pvPortMallocLarge + #define pvPortMallocLarge( x ) pvPortMalloc( x ) +#endif + +#ifndef vPortFreeLarge + #define vPortFreeLarge( ptr ) vPortFree( ptr ) +#endif + +#ifndef pvPortMallocSocket + #define pvPortMallocSocket( x ) pvPortMalloc( x ) +#endif + +#ifndef vPortFreeSocket + #define vPortFreeSocket( ptr ) vPortFree( ptr ) +#endif + +/* + * At several places within the library, random numbers are needed: + * - DHCP: For creating a DHCP transaction number + * - TCP: Set the Initial Sequence Number: this is the value of the first outgoing + * sequence number being used when connecting to a peer. + * Having a well randomized ISN is important to avoid spoofing + * - UDP/TCP: for setting the first port number to be used, in case a socket + * uses a 'random' or anonymous port number + * + * The function is now **deprecated**, in stead the following function must be provided: + * BaseType_t xApplicationGetRandomNumber( uint32_t *pulValue ); + * It will return pdPASS if the random number could be created, otherwise pdFAIL. + */ +#ifndef ipconfigRAND32 + #define ipconfigRAND32() rand() +#endif + +/* 'ipconfigUSE_NETWORK_EVENT_HOOK' indicates if an application hook is available + * called 'vApplicationIPNetworkEventHook()'. This function will be called when + * the network goes up and when it goes down. See also FREERTOS_IP.h for further + * explanation. */ +#ifndef ipconfigUSE_NETWORK_EVENT_HOOK + #define ipconfigUSE_NETWORK_EVENT_HOOK 0 +#endif + +/* Define the number of entries in the ARP cache table. */ +#ifndef ipconfigARP_CACHE_ENTRIES + #define ipconfigARP_CACHE_ENTRIES 10 +#endif + +/* The number of times an ARP request is sent when looking + * up an IP-address. + * The name should have been 'max transmissions', and not + * 'max re-transmissions'. */ +#ifndef ipconfigMAX_ARP_RETRANSMISSIONS + #define ipconfigMAX_ARP_RETRANSMISSIONS ( 5U ) +#endif + +/* The maximum age of an entry in the ARP cache table can be + * calculated as 'ipARP_TIMER_PERIOD_MS' x 'ipconfigMAX_ARP_AGE'. + * The macro 'ipARP_TIMER_PERIOD_MS' is local to FreeRTOSIP.c + * but it can be overwritten from FreeRTOSIPConfig.h + * The default is 10000 x 150 = 1500000 ms or 1500 seconds + */ +#ifndef ipconfigMAX_ARP_AGE + #define ipconfigMAX_ARP_AGE 150U +#endif + +/* 'ipconfigUSE_ARP_REVERSED_LOOKUP' when non-zero, the function + * eARPGetCacheEntryByMac() will be included in the code. + */ +#ifndef ipconfigUSE_ARP_REVERSED_LOOKUP + #define ipconfigUSE_ARP_REVERSED_LOOKUP 0 +#endif + +/* 'ipconfigUSE_ARP_REMOVE_ENTRY' when non-zero, the function + * ulARPRemoveCacheEntryByMac() will be included in the code. + */ +#ifndef ipconfigUSE_ARP_REMOVE_ENTRY + #define ipconfigUSE_ARP_REMOVE_ENTRY 0 +#endif + +/* Normally, the ARP table will only store IP-addresses that are located + * in the local subnet. + * When enabled, the option 'ipconfigARP_STORES_REMOTE_ADDRESSES' will allow + * that remote IP-addresses will also be stored, along with the MAC-address of + * the gateway. + */ +#ifndef ipconfigARP_STORES_REMOTE_ADDRESSES + #define ipconfigARP_STORES_REMOTE_ADDRESSES 0 +#endif + +/* 'ipconfigINCLUDE_FULL_INET_ADDR' used to determine if + * the function 'FreeRTOS_inet_addr()' is included. + * The macro is now deprecated and the function is included + * unconditionally. + */ +#ifndef ipconfigINCLUDE_FULL_INET_ADDR + #define ipconfigINCLUDE_FULL_INET_ADDR 1 +#endif + +/* This is about how new packets are passed from the network interface + * to the IP-task. By default they will be sent one-by-one. + * When 'ipconfigUSE_LINKED_RX_MESSAGES' is non-zero, each message + * buffer gets a 'pxNextBuffer' field, to that linked packets can be passed + * to the IP-task in a single call to 'xSendEventStructToIPTask()'. + * Note that this only works if the Network Interface also supports this + * option. + */ +#ifndef ipconfigUSE_LINKED_RX_MESSAGES + #define ipconfigUSE_LINKED_RX_MESSAGES 0 +#endif + +/* 'ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS' is an important macro: it + * determines the number of network buffers that are available in the + * entire application. + * Note that the default of 45 may be pretty high for smaller + * applications. + * Also note that when the network interface uses zero-copy reception + * ( ipconfigZERO_COPY_RX_DRIVER ), it will reserve a set of network + * buffers permanently. + * For zero-copy transmission, no network buffers are permanently + * "reserved" for transmission. + */ +#ifndef ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS + #define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS 45U +#endif + +/* Every task, and also the network interface can send messages + * to the IP-task by calling API's. These messages pass through a + * queue which has a maximum size of 'ipconfigEVENT_QUEUE_LENGTH' + * items. + * When developing an application, it is important to monitor the + * actual usage of the queue. See 'ipconfigCHECK_IP_QUEUE_SPACE' + * here below. + */ +#ifndef ipconfigEVENT_QUEUE_LENGTH + #define ipconfigEVENT_QUEUE_LENGTH ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS + 5U ) +#endif + +#if ( ipconfigEVENT_QUEUE_LENGTH < ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS + 5U ) ) + #error The ipconfigEVENT_QUEUE_LENGTH parameter must be at least ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS + 5 +#endif + +/* Related to the macro 'ipconfigEVENT_QUEUE_LENGTH' here above: + * when developing a new networking application, it can be helpful + * to monitor the length of the message queue of the IP-task. + * This code is only enabled when 'ipconfigCHECK_IP_QUEUE_SPACE' + * is set to 1. See also the function 'uxGetMinimumIPQueueSpace()'. + */ +#ifndef ipconfigCHECK_IP_QUEUE_SPACE + #define ipconfigCHECK_IP_QUEUE_SPACE 0 +#endif + +/* When defined as non-zero, this macro allows to use a socket + * without first binding it explicitly to a port number. + * In that case, it will be bound to a random free port number. */ +#ifndef ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND + #define ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND 1 +#endif + +/* Configuration to control whether packets with IP options, + * received over the network, should be passed up to the + * software stack OR should be dropped. + * If set to 1, the stack accepts IP packets that contain IP options, but does + * not process the options (IP options are not supported). + * If set to 0, the stack will drop IP packets that contain IP options. + */ +#ifndef ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS + #define ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS 1 +#endif + +/* Configuration to control whether all outgoing IP datagrams get their + * "don't fragment" flag set. + * If set to 1, the stack will set the "don't fragment" flag on all outgoing IP + * packets. If a packet needs to be fragmented somewhere along it's path, it will get + * discarded instead of fragmented. + * If set to 0, the stack will clear the "don't fragment" flag an all outgoing IP + * packets therefore allowing fragmentation if it is needed. + */ +#ifndef ipconfigFORCE_IP_DONT_FRAGMENT + #define ipconfigFORCE_IP_DONT_FRAGMENT 0 +#endif + +/* Configuration to control whether UDP packets with + * checksum value of zero should be passed up the software + * stack OR should be dropped. + * When enabled, the stack will accept UDP packets that have their checksum + * value set to 0. + * When disabled, the stack will drop UDP packets that have their checksum + * value set to 0, and issue some logging. + */ +#ifndef ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS + #define ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS 0 +#endif + +/* Define the value of the TTL field in outgoing UDP packets. */ +#ifndef ipconfigUDP_TIME_TO_LIVE + #define ipconfigUDP_TIME_TO_LIVE 128 +#endif + +/* Define the value of the TTL field in outgoing TCP packets. */ +#ifndef ipconfigTCP_TIME_TO_LIVE + #define ipconfigTCP_TIME_TO_LIVE 128 +#endif + +/* Define the value of the TTL field in outgoing TCP packets. */ +/* The default of 64 is recommend in RFC 1700. */ +#ifndef ipconfigICMP_TIME_TO_LIVE + #define ipconfigICMP_TIME_TO_LIVE 64 +#endif + +/* TCP only: when measuring the Smoothed Round Trip Time (SRTT), + * the result will be rounded up to a minimum value. + * The default has always been 50, but a value of 1000 + * is recommended ( see RFC6298 ) because hosts often delay the + * sending of ACK packets with 200 ms. */ +#ifndef ipconfigTCP_SRTT_MINIMUM_VALUE_MS + #define ipconfigTCP_SRTT_MINIMUM_VALUE_MS 50 +#endif + +/* Make positive to define the maximum number of packets which will be buffered + * in a UDP socket. + * Can be overridden with the socket option 'FREERTOS_SO_UDP_MAX_RX_PACKETS'. + */ +#ifndef ipconfigUDP_MAX_RX_PACKETS + #define ipconfigUDP_MAX_RX_PACKETS 0U +#endif + +/* Define the priority of the IP-task. It is recommended to use this + * order of priorities: + * Highest : network interface, handling transmission and reception. + * Medium : the IP-task handling API calls from the application. + * Lower : the tasks that make use of the IP-stack. + * For other tasks any priority can be chosen. + */ +#ifndef ipconfigIP_TASK_PRIORITY + #define ipconfigIP_TASK_PRIORITY ( configMAX_PRIORITIES - 2 ) +#endif + +/* The size, in words (not bytes), of the stack allocated to the FreeRTOS+TCP + * task. This setting is less important when the FreeRTOS Win32 simulator is used + * as the Win32 simulator only stores a fixed amount of information on the task + * stack. FreeRTOS includes optional stack overflow detection, see: + * http://www.freertos.org/Stacks-and-stack-overflow-checking.html. */ +#ifndef ipconfigIP_TASK_STACK_SIZE_WORDS + #define ipconfigIP_TASK_STACK_SIZE_WORDS ( configMINIMAL_STACK_SIZE * 5 ) +#endif + +/* When non-zero, the module FreeRTOS_DHCP.c will be included and called. + * Note that the application can override decide to ignore the outcome + * of the DHCP negotiation and use a static IP-address. */ +#ifndef ipconfigUSE_DHCP + #define ipconfigUSE_DHCP 1 +#endif + +/* During the DHCP process, the driver will call an application hook + * if 'ipconfigUSE_DHCP_HOOK' is non-zero. It lets the application decide + * if the DHCP offer shall be accepted. + */ +#ifndef ipconfigUSE_DHCP_HOOK + #define ipconfigUSE_DHCP_HOOK 0 +#endif + +/* DHCP servers have a table with information about each clients. One + * of the fields in this table contains the host name of the DHCP clients. + * When 'ipconfigDHCP_REGISTER_HOSTNAME' is defined as non-zero, the DHCP + * driver will call 'pcApplicationHostnameHook()' to obtain the name of + * the embedded device. + */ +#ifndef ipconfigDHCP_REGISTER_HOSTNAME + #define ipconfigDHCP_REGISTER_HOSTNAME 0 +#endif + +/* + * Only applicable when DHCP is in use: + * If no DHCP server responds, use "Auto-IP" : the + * device will allocate a random LinkLayer IP address. + */ +#ifndef ipconfigDHCP_FALL_BACK_AUTO_IP + #define ipconfigDHCP_FALL_BACK_AUTO_IP ( 0 ) +#endif + +/* When a link-layer address is assigned, the driver will test + * if it is already taken by a different device by sending ARP + * requests. Therefore, 'ipconfigARP_USE_CLASH_DETECTION' must + * be defined as non-zero. + */ +#if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + #ifndef ipconfigARP_USE_CLASH_DETECTION + #define ipconfigARP_USE_CLASH_DETECTION 1 + #else + #if ( ipconfigARP_USE_CLASH_DETECTION != 1 ) + #error ipconfigARP_USE_CLASH_DETECTION should be defined as 1 when AUTO_IP is used. + #endif + #endif +#endif + +/* If ipconfigDHCP_FALL_BACK_AUTO_IP is not used, the code for + * clash detection is not needed. + */ +#ifndef ipconfigARP_USE_CLASH_DETECTION + #define ipconfigARP_USE_CLASH_DETECTION 0 +#endif + +/* An important macro: 'ipconfigNETWORK_MTU' determines the Maximum + * transmission unit, which is a network packet minus the size of the + * 14-byte Ethernet header. + */ +#ifndef ipconfigNETWORK_MTU + #define ipconfigNETWORK_MTU 1500 +#else + /* A sanity check to avoid a possible overflow of size_t. */ + #if ipconfigNETWORK_MTU > ( SIZE_MAX >> 1 ) + /* MISRA Ref 20.5.1 [Use of undef] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-2051 */ + /* coverity[misra_c_2012_rule_20_5_violation] */ + #undef ipconfigNETWORK_MTU + #define ipconfigNETWORK_MTU ( SIZE_MAX >> 1 ) + #endif +#endif + +#if ( ipconfigNETWORK_MTU < 46 ) + #error ipconfigNETWORK_MTU must be at least 46. +#endif + +/* The maximum segment size used by TCP, it is the maximum size of + * the TCP payload per packet. + * For IPv4: when MTU equals 1500, the MSS equals 1460. + * It is recommended to use the default value defined here. + * + * In FreeRTOS_TCP_IP.c, there is a local macro called 'tcpREDUCED_MSS_THROUGH_INTERNET'. + * When a TCP connection is made outside the local network, the MSS + * will be reduced to 'tcpREDUCED_MSS_THROUGH_INTERNET' before the connection + * is made. + */ +#ifndef ipconfigTCP_MSS + #define ipconfigTCP_MSS ( ipconfigNETWORK_MTU - ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) ) +#endif + +/* This macro defines the minimum size of an outgoing Ethernet packet. + * When zero, there is no minimum. + * When non-zero, the packet will be extended to the minimum size. + * The extra bytes will be cleared. + * In many projects a value of 60 is used. + * The 32-bit CRC added in the physical layer is not included. + */ +#ifndef ipconfigETHERNET_MINIMUM_PACKET_BYTES + #define ipconfigETHERNET_MINIMUM_PACKET_BYTES 0 +#endif + +/* Each TCP socket has circular stream buffers for reception and transmission, + * which have a fixed maximum size. + * The defaults for these sizes are defined here, although + * they can be overridden at runtime by calling FreeRTOS_setsockopt(), + * and use 'FREERTOS_SO_RCVBUF', 'FREERTOS_SO_SNDBUF' or + * 'FREERTOS_SO_WIN_PROPERTIES'. + * A stream buffer will only be created when needed. A TCP server socket + * will never create buffers. + */ +#ifndef ipconfigTCP_RX_BUFFER_LENGTH + /* When MTU equals 1500, the buffer length defaults to 5840 bytes */ + #define ipconfigTCP_RX_BUFFER_LENGTH ( 4U * ipconfigTCP_MSS ) +#endif + +/* Define the size of Tx stream buffer for TCP sockets. + */ +#ifndef ipconfigTCP_TX_BUFFER_LENGTH + /* When MTU equals 1500, the buffer length defaults to 5840 bytes */ + #define ipconfigTCP_TX_BUFFER_LENGTH ( 4U * ipconfigTCP_MSS ) +#endif + +/* 'ipconfigMAXIMUM_DISCOVER_TX_PERIOD' is about DHCP. + * It determines the maximum time (in clock-ticks) that the DHCP client + * will wait for an offer from a DHCP server. */ +#ifndef ipconfigMAXIMUM_DISCOVER_TX_PERIOD + #ifdef _WINDOWS_ + #define ipconfigMAXIMUM_DISCOVER_TX_PERIOD ( pdMS_TO_TICKS( 999U ) ) + #else + #define ipconfigMAXIMUM_DISCOVER_TX_PERIOD ( pdMS_TO_TICKS( 30000U ) ) + #endif /* _WINDOWS_ */ +#endif /* ipconfigMAXIMUM_DISCOVER_TX_PERIOD */ + +#if ( ipconfigUSE_DNS == 0 ) + /* The DNS module will not be included. */ + #if ( ( ipconfigUSE_LLMNR != 0 ) || ( ipconfigUSE_NBNS != 0 ) ) + /* LLMNR and NBNS depend on DNS because those protocols share a lot of code. */ + #error When either LLMNR or NBNS is used, ipconfigUSE_DNS must be defined + #endif +#endif + +/* By default, the DNS client is included. Note that LLMNR and + * NBNS also need the code from FreeRTOS_DNS.c + */ +#ifndef ipconfigUSE_DNS + #define ipconfigUSE_DNS 1 +#endif + +/* When looking up a host with DNS, this macro determines how long the + * call to FreeRTOS_recvfrom() will wait for a reply. + * When there is no reply, the request will be repeated up to + * 'ipconfigDNS_REQUEST_ATTEMPTS' attempts. */ +#ifndef ipconfigDNS_RECEIVE_BLOCK_TIME_TICKS + #define ipconfigDNS_RECEIVE_BLOCK_TIME_TICKS pdMS_TO_TICKS( 5000U ) +#endif + +/* When looking up a host with DNS, this macro determines how long the + * call to FreeRTOS_sendto() will block to wait for a free buffer. */ +#ifndef ipconfigDNS_SEND_BLOCK_TIME_TICKS + #define ipconfigDNS_SEND_BLOCK_TIME_TICKS pdMS_TO_TICKS( 500U ) +#endif + +/* The number of times a DNS request will be send before giving up. */ +#ifndef ipconfigDNS_REQUEST_ATTEMPTS + #define ipconfigDNS_REQUEST_ATTEMPTS 5 +#endif + +/* The results of DNS lookup's can be stored in a cache table. */ +#ifndef ipconfigUSE_DNS_CACHE + #define ipconfigUSE_DNS_CACHE 0 +#endif + +#if ( ipconfigUSE_DNS_CACHE != 0 ) + +/* Per https://tools.ietf.org/html/rfc1035, 253 is the maximum string length + * of a DNS name. The following default accounts for a null terminator. */ + #ifndef ipconfigDNS_CACHE_NAME_LENGTH + #define ipconfigDNS_CACHE_NAME_LENGTH 254U + #endif + +/* The number of entries in the DNS cache table. + * The default of 1 is maybe too economic. */ + #ifndef ipconfigDNS_CACHE_ENTRIES + #define ipconfigDNS_CACHE_ENTRIES 1U + #endif + +#endif /* ipconfigUSE_DNS_CACHE != 0 */ + +/* When accessing services which have multiple IP addresses, setting this + * greater than 1 can improve reliability by returning different IP address + * answers on successive calls to FreeRTOS_gethostbyname(). */ +#ifndef ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY + #define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY 1 +#endif + +/* When 'ipconfigDNS_USE_CALLBACKS' is defined, a function 'FreeRTOS_gethostbyname_a()' + * will become available. + * It is used for asynchronous DNS lookups. + * This function will start a DNS-lookup and set an application hook. + * This hook will be called when either the URL has been found, or when + * a time-out has been reached. + * Note that the function 'FreeRTOS_gethostbyname_a()' will not make use of the + * macros 'ipconfigDNS_SEND_BLOCK_TIME_TICKS', 'ipconfigDNS_RECEIVE_BLOCK_TIME_TICKS' + * or 'ipconfigDNS_REQUEST_ATTEMPTS'. + */ +#ifndef ipconfigDNS_USE_CALLBACKS + #define ipconfigDNS_USE_CALLBACKS 0 +#endif + +/* Include support for LLMNR: Link-local Multicast Name Resolution. */ +#ifndef ipconfigUSE_LLMNR + #define ipconfigUSE_LLMNR ( 0 ) +#endif + +/* Include support for NBNS: NetBIOS Name Server. */ +#ifndef ipconfigUSE_NBNS + #define ipconfigUSE_NBNS 0 +#endif + +/* It is recommended to let the application respond to incoming ping + * requests. */ +#ifndef ipconfigREPLY_TO_INCOMING_PINGS + #define ipconfigREPLY_TO_INCOMING_PINGS 1 +#endif + +/* Add code for outgoing pings as well. */ +#ifndef ipconfigSUPPORT_OUTGOING_PINGS + #define ipconfigSUPPORT_OUTGOING_PINGS 0 +#endif + +/* A MISRA note: The macros 'ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES' + * and 'ipconfigETHERNET_DRIVER_FILTERS_PACKETS' are too long: + * the first 32 bytes are equal, which might cause problems + * for some compilers. */ + +/* Beside that, there is some overlap between the following 3 macros. Here is + * a summary: + * + * 1) ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES + * When disabled, the IP-task will call 'eConsiderFrameForProcessing()' + * to check incoming packets. + * 2) ipconfigETHERNET_DRIVER_FILTERS_PACKETS + * When disabled, the IP-task will perform sanity checks on the IP-header, + * also checking the target IP address. + * Also when disabled, xPortHasUDPSocket() won't be included. That means + * that the IP-task can access the 'xBoundUDPSocketsList' without locking. + * 3) ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES + * When enabled, the function 'eConsiderFrameForProcessing()' will also + * check if the Ethernet frame type is acceptable. + */ + +/* The following macro determines if the network interface will + * do checks on the incoming packets. When false, the IP-task will + * perform these checks in the function eConsiderFrameForProcessing(). + * + * It should be noted that it is most efficient to drop unwanted packets + * as early as possible. + */ + +#ifndef ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES + #define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES 1 +#endif + +/* When ipconfigETHERNET_DRIVER_FILTERS_PACKETS is enabled, the network + * interface will inspect the incoming packets to see if they can be + * accepted. + * When enabled, the function 'xPortHasUDPSocket()' will become available. + * That function checks if there is a UDP socket listening to a + * given port number. + */ +#ifndef ipconfigETHERNET_DRIVER_FILTERS_PACKETS + #define ipconfigETHERNET_DRIVER_FILTERS_PACKETS ( 0 ) +#endif + +/* When defined as 1, the driver will drop all packets with an unknown + * frame type. + * This macro is only looked at when 'ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES' + * is disabled. + */ +#ifndef ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES + #define ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES 1 +#endif + + +/* The macro 'ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS' was + * introduced to enable a tracing system. + * Currently it is only used in BufferAllocation_2.c. + */ +#ifndef configINCLUDE_TRACE_RELATED_CLI_COMMANDS + #define ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS 0 +#else + #define ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS configINCLUDE_TRACE_RELATED_CLI_COMMANDS +#endif + +/* This macro will be called in every loop the IP-task makes. It may be + * replaced by user-code that triggers a watchdog */ +#ifndef ipconfigWATCHDOG_TIMER + #define ipconfigWATCHDOG_TIMER() +#endif + +/* The option 'ipconfigUSE_CALLBACKS' allows to assign specific application + * hooks to a socket. Each hook will be set with a specific socket option: + * + * FREERTOS_SO_TCP_CONN_HANDLER * Callback for (dis) connection events. + * * Supply pointer to 'F_TCP_UDP_Handler_t' + * FREERTOS_SO_TCP_RECV_HANDLER * Callback for receiving TCP data. + * * Supply pointer to 'F_TCP_UDP_Handler_t' + * FREERTOS_SO_TCP_SENT_HANDLER * Callback for sending TCP data. + * * Supply pointer to 'F_TCP_UDP_Handler_t' + * FREERTOS_SO_UDP_RECV_HANDLER * Callback for receiving UDP data. + * * Supply pointer to 'F_TCP_UDP_Handler_t' + * FREERTOS_SO_UDP_SENT_HANDLER * Callback for sending UDP data. + * * Supply pointer to 'F_TCP_UDP_Handler_t' + * + * Note that the call-back functions will run in the IP-task, so very little + * things can be done. Better not to call any networking API, because that + * could easily lead to a deadlock situation. + */ +#ifndef ipconfigUSE_CALLBACKS + #define ipconfigUSE_CALLBACKS ( 0 ) +#endif + +/* Replace this macro with a test returning non-zero if the memory pointer to by + * pxAddress is valid memory which can contain executable code. + * In fact this is an extra safety measure: if a handler points to invalid memory, + * it will not be called. + * The parameter 'pxAddress' is in fact a pointer to a function. + */ +#if ( ipconfigUSE_CALLBACKS != 0 ) + #ifndef ipconfigIS_VALID_PROG_ADDRESS + #define ipconfigIS_VALID_PROG_ADDRESS( pxAddress ) ( ( pxAddress ) != NULL ) + #endif +#endif + +/* The macro 'ipconfigHAS_INLINE_FUNCTIONS' is now deprecated. */ +#ifndef ipconfigHAS_INLINE_FUNCTIONS + #define ipconfigHAS_INLINE_FUNCTIONS ( 1 ) +#endif + +/* Since all code is made compatible with the MISRA rules, the inline functions + * disappear. 'portINLINE' should normally be defined in FreeRTOSCOnfig.h + */ +#ifndef portINLINE + #define portINLINE inline +#endif + +/* When non-zero, the buffers passed to xNetworkInterfaceOutput() will be passed + * directly to DMA. As soon as sending is ready, the buffers must be released by + * calling vReleaseNetworkBufferAndDescriptor(). */ +#ifndef ipconfigZERO_COPY_TX_DRIVER + #define ipconfigZERO_COPY_TX_DRIVER ( 0 ) +#endif + +/* When enabled, the network interface will pass the network buffers directly to + * the DMA descriptors. When a packet has been received, the function + * pxPacketBuffer_to_NetworkBuffer() will translate a buffer address to a + * network packet, so it can be passed to the IP-task. */ +#ifndef ipconfigZERO_COPY_RX_DRIVER + #define ipconfigZERO_COPY_RX_DRIVER ( 0 ) +#endif + +/* When ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM is defined as non-zero, + * the network interface is responsible for setting the checksums + * of the outgoing packets. + * This can be either done in hardware, or by calling the checksum + * routines from 'xNetworkInterfaceOutput()'. + */ +#ifndef ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM + #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 0 +#endif + +/* When ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM is enabled, + * the network interface is responsible for checking the checksums + * of the incoming packets. + * This can be either done in hardware, or by calling the checksum + * functions. + */ +#ifndef ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM + #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 0 +#endif + +/* The macro 'ipconfigSOCKET_HAS_USER_SEMAPHORE' is rarely used, yet it + * can be very useful. IT applies to both TCP and UDP sockets. + * + * The application can register a semaphore ( of type 'SemaphoreHandle_t' ) + * in a socket with the option 'FREERTOS_SO_SET_SEMAPHORE'. + * Once set, the semaphore will be signalled after every important socket event: + * READ, WRITE, EXCEPTION. + * Note that a READ event is also generated for a TCP socket in listen mode, + * and a WRITE event is generated when a call to connect() has succeeded. + * Beside that, READ and WRITE are the normal events that occur when + * data has been received or delivered. + */ +#ifndef ipconfigSOCKET_HAS_USER_SEMAPHORE + #define ipconfigSOCKET_HAS_USER_SEMAPHORE 0 +#endif + +/* The macro 'ipconfigSOCKET_HAS_USER_WAKE_CALLBACK' allows to use a call-back + * function that will be called at the moment one of the above events occurs. + * Use the socket option 'FREERTOS_SO_WAKEUP_CALLBACK' to install a function + * of the type 'void callback( Socket_t pxSocket )'. + * Note that the call-back function runs in the IP-task, so very little things + * can be done. Better not to call any networking API, because that could + * easily lead to a deadlock situation. + */ +#ifndef ipconfigSOCKET_HAS_USER_WAKE_CALLBACK + #define ipconfigSOCKET_HAS_USER_WAKE_CALLBACK 0 +#endif + +/* Yet another possibility that makes it easy to handle multiple socket in + * a single task FreeRTOS_select(). The code for this functionality will + * be included when 'ipconfigSUPPORT_SELECT_FUNCTION' is defined as non-zero. + */ +#ifndef ipconfigSUPPORT_SELECT_FUNCTION + #define ipconfigSUPPORT_SELECT_FUNCTION 0 +#endif + +/* If the select function will be called simultaneously from more that one task + * e.g. one for 'eSELECT_READ', and the other for the other flags, it is strongly + * recommended to define 'ipconfigSELECT_USES_NOTIFY'. + * The problem is 'eSELECT_CALL_IP': 2 tasks would be waiting for the same bit + * in the event group, and both will try to reset this bit. + * When ipconfigSELECT_USES_NOTIFY is used, the IP-task will wakeup the calling + * task by notifying it. + */ +#ifndef ipconfigSELECT_USES_NOTIFY + #define ipconfigSELECT_USES_NOTIFY 0 +#endif + +/* TCP only: if the 'ipconfigTCP_KEEP_ALIVE' macro is defined as 1, + * sockets in state "ESTABLISHED" can be protected using keep-alive packets. + * These packets will be sent as soon as there hasn't been any activity + * for a while. + * The macro 'ipconfigTCP_KEEP_ALIVE_INTERVAL' determines the interval at + * which keep-alive packets are sent. + */ +#ifndef ipconfigTCP_KEEP_ALIVE + #define ipconfigTCP_KEEP_ALIVE 0 +#endif + +/* The period of non-activity ( in seconds ) after which the driver will + * start sending a keep-alive packet to the TCP peer. The default is 20 seconds. + */ +#ifndef ipconfigTCP_KEEP_ALIVE_INTERVAL + #define ipconfigTCP_KEEP_ALIVE_INTERVAL 20U +#endif + +/* Another less used option: signals. This macro makes it possible to interrupt + * a blocking call to one of the API's by calling either FreeRTOS_SignalSocket() or + * FreeRTOS_SignalSocketFromISR() for that socket. + * When an API got interrupted, it will return the error value -pdFREERTOS_ERRNO_EINTR. + */ +#ifndef ipconfigSUPPORT_SIGNALS + #define ipconfigSUPPORT_SIGNALS 0 +#endif + +/* Hang protection can help reduce the impact of SYN floods. + * When a SYN packet comes in, it will first be checked if there is a listening + * socket for the port number. If not, it will be replied to with a RESET packet. + * If there is a listing socket for that port number, a new socket will be created. + * This socket will be owned temporarily by the IP-task. Only when the SYN/ACK + * handshake is finished, the new socket will be passed to the application, + * resulting in a successful call to FreeRTOS_accept(). + * The option 'ipconfigTCP_HANG_PROTECTION' will make sure that the socket will be + * deleted in case the SYN-handshake doesn't come to a good end. + * See also ipconfigTCP_HANG_PROTECTION_TIME time. + */ +#ifndef ipconfigTCP_HANG_PROTECTION + #define ipconfigTCP_HANG_PROTECTION 1 +#endif + +/* ipconfigTCP_HANG_PROTECTION_TIME defines the maximum time that a socket stays + * in one of these "in-between" states: + * + * eCONNECT_SYN, eSYN_FIRST, eSYN_RECEIVED, eFIN_WAIT_1, eFIN_WAIT_2, eCLOSING, + * eLAST_ACK, or eTIME_WAIT. + */ +#ifndef ipconfigTCP_HANG_PROTECTION_TIME + #define ipconfigTCP_HANG_PROTECTION_TIME 30U +#endif + +/* Initially, 'ipconfigTCP_IP_SANITY' was introduced to include all code that checks + * the correctness of the algorithms. However, it is only used in BufferAllocation_1.c + * When defined as non-zero, some extra code will check the validity of network buffers. + */ +#ifndef ipconfigTCP_IP_SANITY + #define ipconfigTCP_IP_SANITY 0 +#endif + +/* Expert option: define a value for 'ipBUFFER_PADDING'. + * When 'ipconfigBUFFER_PADDING' equals 0, + * 'ipBUFFER_PADDING' will get a default value of 8 + 2 bytes. */ +#ifndef ipconfigBUFFER_PADDING + #define ipconfigBUFFER_PADDING 0U +#endif + +/* This library treats the network packets as structs, containing 16- and 32-bit + * variables. However, due to the size of the Ethernet header, the 32-byte + * variables are badly aligned. + * This is corrected with the macro 'ipconfigPACKET_FILLER_SIZE' which has a default + * of two. Thanks to this offset, ( almost ) all 32-bit numbers can be read and + * written in a single assembler instruction. + */ +#ifndef ipconfigPACKET_FILLER_SIZE + #define ipconfigPACKET_FILLER_SIZE 2U +#endif + +/* Set to 1 if you plan on processing custom Ethernet protocols or protocols + * that are not yet supported by the FreeRTOS+TCP stack. If set to 1, + * the user must define eFrameProcessingResult_t eApplicationProcessCustomFrameHook( NetworkBufferDescriptor_t * const pxNetworkBuffer ) + * which will be called by the stack for any frame with an unsupported EtherType. */ +#ifndef ipconfigPROCESS_CUSTOM_ETHERNET_FRAMES + #define ipconfigPROCESS_CUSTOM_ETHERNET_FRAMES 0 +#endif + +#endif /* FREERTOS_DEFAULT_IP_CONFIG_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_ARP.h b/FreeRTOS/source/include/FreeRTOS_ARP.h new file mode 100644 index 0000000..4785e86 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_ARP.h @@ -0,0 +1,159 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_ARP_H +#define FREERTOS_ARP_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* Application level configuration options. */ +#include "FreeRTOSIPConfig.h" +#include "FreeRTOSIPConfigDefaults.h" +#include "IPTraceMacroDefaults.h" + +/*-----------------------------------------------------------*/ +/* Miscellaneous structure and definitions. */ +/*-----------------------------------------------------------*/ + +/** + * Structure for one row in the ARP cache table. + */ +typedef struct xARP_CACHE_TABLE_ROW +{ + uint32_t ulIPAddress; /**< The IP address of an ARP cache entry. */ + MACAddress_t xMACAddress; /**< The MAC address of an ARP cache entry. */ + uint8_t ucAge; /**< A value that is periodically decremented but can also be refreshed by active communication. The ARP cache entry is removed if the value reaches zero. */ + uint8_t ucValid; /**< pdTRUE: xMACAddress is valid, pdFALSE: waiting for ARP reply */ +} ARPCacheRow_t; + +typedef enum +{ + eARPCacheMiss = 0, /* 0 An ARP table lookup did not find a valid entry. */ + eARPCacheHit, /* 1 An ARP table lookup found a valid entry. */ + eCantSendPacket /* 2 There is no IP address, or an ARP is still in progress, so the packet cannot be sent. */ +} eARPLookupResult_t; + +/* + * If ulIPAddress is already in the ARP cache table then reset the age of the + * entry back to its maximum value. If ulIPAddress is not already in the ARP + * cache table then add it - replacing the oldest current entry if there is not + * a free space available. + */ +void vARPRefreshCacheEntry( const MACAddress_t * pxMACAddress, + const uint32_t ulIPAddress ); + +#if ( ipconfigARP_USE_CLASH_DETECTION != 0 ) + /* Becomes non-zero if another device responded to a gratuitous ARP message. */ + extern BaseType_t xARPHadIPClash; + /* MAC-address of the other device containing the same IP-address. */ + extern MACAddress_t xARPClashMacAddress; +#endif /* ipconfigARP_USE_CLASH_DETECTION */ + +#if ( ipconfigUSE_ARP_REMOVE_ENTRY != 0 ) + +/* + * In some rare cases, it might be useful to remove a ARP cache entry of a + * known MAC address to make sure it gets refreshed. + */ + uint32_t ulARPRemoveCacheEntryByMac( const MACAddress_t * pxMACAddress ); + +#endif /* ipconfigUSE_ARP_REMOVE_ENTRY != 0 */ + + +BaseType_t xIsIPInARPCache( uint32_t ulAddressToLookup ); + +BaseType_t xCheckRequiresARPResolution( const NetworkBufferDescriptor_t * pxNetworkBuffer ); + +/* + * Look for ulIPAddress in the ARP cache. If the IP address exists, copy the + * associated MAC address into pxMACAddress, refresh the ARP cache entry's + * age, and return eARPCacheHit. If the IP address does not exist in the ARP + * cache return eARPCacheMiss. If the packet cannot be sent for any reason + * (maybe DHCP is still in process, or the addressing needs a gateway but there + * isn't a gateway defined) then return eCantSendPacket. + */ +eARPLookupResult_t eARPGetCacheEntry( uint32_t * pulIPAddress, + MACAddress_t * const pxMACAddress ); + +#if ( ipconfigUSE_ARP_REVERSED_LOOKUP != 0 ) + +/* Lookup an IP-address if only the MAC-address is known */ + eARPLookupResult_t eARPGetCacheEntryByMac( const MACAddress_t * const pxMACAddress, + uint32_t * pulIPAddress ); + +#endif + +/* + * Reduce the age count in each entry within the ARP cache. An entry is no + * longer considered valid and is deleted if its age reaches zero. + */ +void vARPAgeCache( void ); + +/* + * Send out an ARP request for the IP address contained in pxNetworkBuffer, and + * add an entry into the ARP table that indicates that an ARP reply is + * outstanding so re-transmissions can be generated. + */ +void vARPGenerateRequestPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ); + +/* + * After DHCP is ready and when changing IP address, force a quick send of our new IP + * address + */ +void vARPSendGratuitous( void ); + +/* This function will check if the target IP-address belongs to this device. + * If so, the packet will be passed to the IP-stack, who will answer it. + * The function is to be called within the function xNetworkInterfaceOutput() + * in NetworkInterface.c as follows: + * + * if( xCheckLoopback( pxDescriptor, bReleaseAfterSend ) != 0 ) + * { + * / * The packet has been sent back to the IP-task. + * * The IP-task will further handle it. + * * Do not release the descriptor. + * * / + * return pdTRUE; + * } + * / * Send the packet as usual. * / + */ +BaseType_t xCheckLoopback( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t bReleaseAfterSend ); + +void FreeRTOS_OutputARPRequest( uint32_t ulIPAddress ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_ARP_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_DHCP.h b/FreeRTOS/source/include/FreeRTOS_DHCP.h new file mode 100644 index 0000000..a5f378a --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_DHCP.h @@ -0,0 +1,250 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_DHCP_H +#define FREERTOS_DHCP_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* Application level configuration options. */ +#include "FreeRTOSIPConfig.h" +#include "IPTraceMacroDefaults.h" + +#include "FreeRTOS_Sockets.h" + + +#if ( ipconfigUSE_DHCP != 0 ) && ( ipconfigNETWORK_MTU < 586U ) + +/* DHCP must be able to receive an options field of 312 bytes, the fixed + * part of the DHCP packet is 240 bytes, and the IP/UDP headers take 28 bytes. */ + #error ipconfigNETWORK_MTU needs to be at least 586 to use DHCP +#endif + +/* Parameter widths in the DHCP packet. */ +#define dhcpCLIENT_HARDWARE_ADDRESS_LENGTH 16 /**< Client hardware address length.*/ +#define dhcpSERVER_HOST_NAME_LENGTH 64 /**< Server host name length. */ +#define dhcpBOOT_FILE_NAME_LENGTH 128 /**< Boot file name length. */ + +/* Timer parameters */ +#ifndef dhcpINITIAL_TIMER_PERIOD + /** @brief The interval at which the DHCP state handler is called. */ + #define dhcpINITIAL_TIMER_PERIOD ( pdMS_TO_TICKS( 250U ) ) +#endif + +#ifndef dhcpINITIAL_DHCP_TX_PERIOD + +/** @brief The initial amount of time to wait for a DHCP reply. When repeating an + * unanswered request, this time-out shall be multiplied by 2. */ + #define dhcpINITIAL_DHCP_TX_PERIOD ( pdMS_TO_TICKS( 5000U ) ) +#endif + +/* Codes of interest found in the DHCP options field. */ +#define dhcpIPv4_ZERO_PAD_OPTION_CODE ( 0U ) /**< Used to pad other options to make them aligned. See RFC 2132. */ +#define dhcpIPv4_SUBNET_MASK_OPTION_CODE ( 1U ) /**< Subnet mask. See RFC 2132. */ +#define dhcpIPv4_GATEWAY_OPTION_CODE ( 3U ) /**< Available routers. See RFC 2132. */ +#define dhcpIPv4_DNS_SERVER_OPTIONS_CODE ( 6U ) /**< Domain name server. See RFC 2132. */ +#define dhcpIPv4_DNS_HOSTNAME_OPTIONS_CODE ( 12U ) /**< Host name. See RFC 2132. */ +#define dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE ( 50U ) /**< Requested IP-address. See RFC 2132. */ +#define dhcpIPv4_LEASE_TIME_OPTION_CODE ( 51U ) /**< IP-address lease time. See RFC 2132. */ +#define dhcpIPv4_MESSAGE_TYPE_OPTION_CODE ( 53U ) /**< DHCP message type. See RFC 2132. */ +#define dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE ( 54U ) /**< Server Identifier. See RFC 2132. */ +#define dhcpIPv4_PARAMETER_REQUEST_OPTION_CODE ( 55U ) /**< Parameter Request list. See RFC 2132. */ +#define dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE ( 61U ) /**< Client Identifier. See RFC 2132. */ + +/* The four DHCP message types of interest. */ +#define dhcpMESSAGE_TYPE_DISCOVER ( 1 ) /**< DHCP discover message. */ +#define dhcpMESSAGE_TYPE_OFFER ( 2 ) /**< DHCP offer message. */ +#define dhcpMESSAGE_TYPE_REQUEST ( 3 ) /**< DHCP request message. */ +#define dhcpMESSAGE_TYPE_ACK ( 5 ) /**< DHCP acknowledgement. */ +#define dhcpMESSAGE_TYPE_NACK ( 6 ) /**< DHCP NACK. (Negative acknowledgement) */ + +/* Offsets into the transmitted DHCP options fields at which various parameters + * are located. */ +#define dhcpCLIENT_IDENTIFIER_OFFSET ( 6U ) /**< Offset for the client ID option. */ +#define dhcpREQUESTED_IP_ADDRESS_OFFSET ( 14U ) /**< Offset for the requested IP-address option. */ +#define dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ( 20U ) /**< Offset for the server IP-address option. */ +#define dhcpOPTION_50_OFFSET ( 12U ) /**< Offset of option-50. */ +#define dhcpOPTION_50_SIZE ( 6U ) /**< Number of bytes included in option-50. */ + + +/* Values used in the DHCP packets. */ +#define dhcpREQUEST_OPCODE ( 1U ) /**< DHCP request opcode. */ +#define dhcpREPLY_OPCODE ( 2U ) /**< DHCP reply opcode. */ +#define dhcpADDRESS_TYPE_ETHERNET ( 1U ) /**< Address type: ethernet opcode. */ +#define dhcpETHERNET_ADDRESS_LENGTH ( 6U ) /**< Ethernet address length opcode. */ + +/* The following define is temporary and serves to make the /single source + * code more similar to the /multi version. */ + +#define EP_DHCPData xDHCPData /**< Temporary define to make /single source similar to /multi version. */ +#define EP_IPv4_SETTINGS xNetworkAddressing /**< Temporary define to make /single source similar to /multi version. */ + +/** @brief If a lease time is not received, use the default of two days (48 hours in ticks). + * Can not use pdMS_TO_TICKS() as integer overflow can occur. */ +#define dhcpDEFAULT_LEASE_TIME ( ( 48UL * 60UL * 60UL ) * configTICK_RATE_HZ ) + +/** @brief Don't allow the lease time to be too short. */ +#define dhcpMINIMUM_LEASE_TIME ( pdMS_TO_TICKS( 60000UL ) ) /* 60 seconds in ticks. */ + +/** @brief Marks the end of the variable length options field in the DHCP packet. */ +#define dhcpOPTION_END_BYTE 0xffu + +/** @brief Offset into a DHCP message at which the first byte of the options is + * located. */ +#define dhcpFIRST_OPTION_BYTE_OFFSET ( 0xf0U ) + +/* Standard DHCP port numbers and magic cookie value. + * DHCPv4 uses UDP port number 68 for clients and port number 67 for servers. + */ +#if ( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) + #define dhcpCLIENT_PORT_IPv4 0x4400U /**< Little endian representation of port 68. */ + #define dhcpSERVER_PORT_IPv4 0x4300U /**< Little endian representation of port 67. */ + #define dhcpCOOKIE 0x63538263UL /**< Little endian representation of magic cookie. */ + #define dhcpBROADCAST 0x0080U /**< Little endian representation of broadcast flag. */ +#else + #define dhcpCLIENT_PORT_IPv4 0x0044U /**< Big endian representation of port 68. */ + #define dhcpSERVER_PORT_IPv4 0x0043U /**< Big endian representation of port 68. */ + #define dhcpCOOKIE 0x63825363UL /**< Big endian representation of magic cookie. */ + #define dhcpBROADCAST 0x8000U /**< Big endian representation of broadcast flag. */ +#endif /* ( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) */ + +#include "pack_struct_start.h" +struct xDHCPMessage_IPv4 +{ + uint8_t ucOpcode; /**< Operation Code: Specifies the general type of message. */ + uint8_t ucAddressType; /**< Hardware type used on the local network. */ + uint8_t ucAddressLength; /**< Hardware Address Length: Specifies how long hardware + * addresses are in this message. */ + uint8_t ucHops; /**< Hops. */ + uint32_t ulTransactionID; /**< A 32-bit identification field generated by the client, + * to allow it to match up the request with replies received + * from DHCP servers. */ + uint16_t usElapsedTime; /**< Number of seconds elapsed since a client began an attempt to acquire or renew a lease. */ + uint16_t usFlags; /**< Just one bit used to indicate broadcast. */ + uint32_t ulClientIPAddress_ciaddr; /**< Client's IP address if it has one or 0 is put in this field. */ + uint32_t ulYourIPAddress_yiaddr; /**< The IP address that the server is assigning to the client. */ + uint32_t ulServerIPAddress_siaddr; /**< The DHCP server address that the client should use. */ + uint32_t ulRelayAgentIPAddress_giaddr; /**< Gateway IP address in case the server client are on different subnets. */ + uint8_t ucClientHardwareAddress[ dhcpCLIENT_HARDWARE_ADDRESS_LENGTH ]; /**< The client hardware address. */ + uint8_t ucServerHostName[ dhcpSERVER_HOST_NAME_LENGTH ]; /**< Server's hostname. */ + uint8_t ucBootFileName[ dhcpBOOT_FILE_NAME_LENGTH ]; /**< Boot file full directory path. */ + uint32_t ulDHCPCookie; /**< Magic cookie option. */ + /* Option bytes from here on. */ +} +#include "pack_struct_end.h" +typedef struct xDHCPMessage_IPv4 DHCPMessage_IPv4_t; + + +#if ( ipconfigUSE_DHCP_HOOK != 0 ) + /* Used in the DHCP callback if ipconfigUSE_DHCP_HOOK is set to 1. */ + typedef enum eDHCP_PHASE + { + eDHCPPhasePreDiscover, /**< Driver is about to send a DHCP discovery. */ + eDHCPPhasePreRequest /**< Driver is about to request DHCP an IP address. */ + } eDHCPCallbackPhase_t; + +/** @brief Used in the DHCP callback if ipconfigUSE_DHCP_HOOK is set to 1. */ + typedef enum eDHCP_ANSWERS + { + eDHCPContinue, /**< Continue the DHCP process */ + eDHCPUseDefaults, /**< Stop DHCP and use the static defaults. */ + eDHCPStopNoChanges, /**< Stop DHCP and continue with current settings. */ + } eDHCPCallbackAnswer_t; +#endif /* #if( ipconfigUSE_DHCP_HOOK != 0 ) */ + +/** @brief DHCP state machine states. */ +typedef enum +{ + eInitialWait = 0, /**< Initial state: open a socket and wait a short time. */ + eWaitingSendFirstDiscover, /**< Send a discover the first time it is called, and reset all timers. */ + eWaitingOffer, /**< Either resend the discover, or, if the offer is forthcoming, send a request. */ + eWaitingAcknowledge, /**< Either resend the request. */ + eSendDHCPRequest, /**< Sendto failed earlier, resend the request to lease the IP-address offered. */ + #if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + eGetLinkLayerAddress, /**< When DHCP didn't respond, try to obtain a LinkLayer address 168.254.x.x. */ + #endif + eLeasedAddress, /**< Resend the request at the appropriate time to renew the lease. */ + eNotUsingLeasedAddress /**< DHCP failed, and a default IP address is being used. */ +} eDHCPState_t; + +/** @brief Hold information in between steps in the DHCP state machine. */ +struct xDHCP_DATA +{ + uint32_t ulTransactionId; /**< The ID of the DHCP transaction */ + uint32_t ulOfferedIPAddress; /**< The IP address offered by the DHCP server */ + uint32_t ulPreferredIPAddress; /**< A preferred IP address */ + uint32_t ulDHCPServerAddress; /**< The IP address of the DHCP server */ + uint32_t ulLeaseTime; /**< The time for which the current IP address is leased */ + TickType_t xDHCPTxTime; /**< The time at which a DHCP request was sent. */ + TickType_t xDHCPTxPeriod; /**< The maximum time that the client will wait for a reply. */ + BaseType_t xUseBroadcast; /**< Try both without and with the broadcast flag */ + eDHCPState_t eDHCPState; /**< Maintains the DHCP state machine state. */ +}; + +typedef struct xDHCP_DATA DHCPData_t; + +/* Returns the current state of a DHCP process. */ +eDHCPState_t eGetDHCPState( void ); + +/* + * NOT A PUBLIC API FUNCTION. + * It will be called when the DHCP timer expires, or when + * data has been received on the DHCP socket. + */ +void vDHCPProcess( BaseType_t xReset, + eDHCPState_t eExpectedState ); + +/* Internal call: returns true if socket is the current DHCP socket */ +BaseType_t xIsDHCPSocket( const ConstSocket_t xSocket ); + + +/* The application can indicate a preferred IP address by calling this function + * before FreeRTOS_IPInit() is called. */ +uint32_t vDHCPSetPreferredIPAddress( uint32_t ulIPAddress ); + +#if ( ipconfigUSE_DHCP_HOOK != 0 ) + +/* Prototype of the hook (or callback) function that must be provided by the + * application if ipconfigUSE_DHCP_HOOK is set to 1. See the following URL for + * usage information: + * http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/TCP_IP_Configuration.html#ipconfigUSE_DHCP_HOOK + */ + eDHCPCallbackAnswer_t xApplicationDHCPHook( eDHCPCallbackPhase_t eDHCPPhase, + uint32_t ulIPAddress ); +#endif /* ( ipconfigUSE_DHCP_HOOK != 0 ) */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_DHCP_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_DNS.h b/FreeRTOS/source/include/FreeRTOS_DNS.h new file mode 100644 index 0000000..b2c9076 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_DNS.h @@ -0,0 +1,102 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://github.com/FreeRTOS + * https://www.FreeRTOS.org + */ + +#ifndef FREERTOS_DNS_H +#define FREERTOS_DNS_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* Application level configuration options. */ +#include "FreeRTOS_DNS_Globals.h" +#include "FreeRTOS_DNS_Callback.h" +#include "FreeRTOS_DNS_Cache.h" + +/* + * LLMNR is very similar to DNS, so is handled by the DNS routines. + */ +uint32_t ulDNSHandlePacket( const NetworkBufferDescriptor_t * pxNetworkBuffer ); + +#if ( ipconfigUSE_LLMNR == 1 ) + /* The LLMNR MAC address is 01:00:5e:00:00:fc */ + extern const MACAddress_t xLLMNR_MacAdress; +#endif /* ipconfigUSE_LLMNR */ + +#if ( ipconfigUSE_NBNS != 0 ) + +/* + * Inspect a NetBIOS Names-Service message. If the name matches with ours + * (xApplicationDNSQueryHook returns true) an answer will be sent back. + * Note that LLMNR is a better protocol for name services on a LAN as it is + * less polluted + */ + uint32_t ulNBNSHandlePacket( NetworkBufferDescriptor_t * pxNetworkBuffer ); + +#endif /* ipconfigUSE_NBNS */ + + +#if ( ipconfigDNS_USE_CALLBACKS != 0 ) + +/* + * Asynchronous version of gethostbyname() + * xTimeout is in units of ms. + */ + uint32_t FreeRTOS_gethostbyname_a( const char * pcHostName, + FOnDNSEvent pCallback, + void * pvSearchID, + TickType_t uxTimeout ); + void FreeRTOS_gethostbyname_cancel( void * pvSearchID ); + + +#endif /* if ( ipconfigDNS_USE_CALLBACKS != 0 ) */ + +/* + * Lookup a IPv4 node in a blocking-way. + * It returns a 32-bit IP-address, 0 when not found. + * gethostbyname() is already deprecated. + */ +uint32_t FreeRTOS_gethostbyname( const char * pcHostName ); + +#if ( ipconfigDNS_USE_CALLBACKS == 1 ) + +/* + * The function vDNSInitialise() initialises the DNS module. + * It will be called "internally", by the IP-task. + */ + void vDNSInitialise( void ); +#endif /* ( ipconfigDNS_USE_CALLBACKS == 1 ) */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_DNS_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_DNS_Cache.h b/FreeRTOS/source/include/FreeRTOS_DNS_Cache.h new file mode 100644 index 0000000..f67bebd --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_DNS_Cache.h @@ -0,0 +1,56 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://github.com/FreeRTOS + * https://www.FreeRTOS.org + */ + +#ifndef FREERTOS_DNS_CACHE_H +#define FREERTOS_DNS_CACHE_H + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" + +/* Standard includes. */ +#include + +#if ( ( ipconfigUSE_DNS_CACHE == 1 ) && ( ipconfigUSE_DNS != 0 ) ) + + uint32_t FreeRTOS_dnslookup( const char * pcHostName ); + + void FreeRTOS_dnsclear( void ); + + BaseType_t FreeRTOS_dns_update( const char * pcName, + uint32_t * pulIP, + uint32_t ulTTL ); + + BaseType_t FreeRTOS_ProcessDNSCache( const char * pcName, + uint32_t * pulIP, + uint32_t ulTTL, + BaseType_t xLookUp ); +#endif /* if ( ipconfigUSE_DNS_CACHE == 1 ) */ + +#endif /* ifndef FREERTOS_DNS_CACHE_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_DNS_Callback.h b/FreeRTOS/source/include/FreeRTOS_DNS_Callback.h new file mode 100644 index 0000000..8a4b0e5 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_DNS_Callback.h @@ -0,0 +1,75 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://github.com/FreeRTOS + * https://www.FreeRTOS.org + */ + + +#ifndef FREERTOS_DNS_CALLBACK_H +#define FREERTOS_DNS_CALLBACK_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" + +#include "FreeRTOS_DNS_Globals.h" + +/* Standard includes. */ +#include +/* Application level configuration options. */ + +#if ( ( ipconfigDNS_USE_CALLBACKS == 1 ) && ( ipconfigUSE_DNS != 0 ) ) + + BaseType_t xDNSDoCallback( TickType_t uxIdentifier, + const char * pcName, + uint32_t ulIPAddress ); + + void vDNSSetCallBack( const char * pcHostName, + void * pvSearchID, + FOnDNSEvent pCallbackFunction, + TickType_t uxTimeout, + TickType_t uxIdentifier ); + + void vDNSCheckCallBack( void * pvSearchID ); + + + void vDNSCallbackInitialise(); + +#endif /* ipconfigDNS_USE_CALLBACKS && ipconfigUSE_DNS */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* ifndef FREERTOS_DNS_CALLBACK_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_DNS_Globals.h b/FreeRTOS/source/include/FreeRTOS_DNS_Globals.h new file mode 100644 index 0000000..3d5b64e --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_DNS_Globals.h @@ -0,0 +1,251 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_DNS_GLOBALS_H +#define FREERTOS_DNS_GLOBALS_H + +#include "FreeRTOS.h" + +#include "FreeRTOSIPConfig.h" +#include "FreeRTOSIPConfigDefaults.h" +#include "IPTraceMacroDefaults.h" + +/* Standard includes. */ + +#define dnsPARSE_ERROR 0UL + +#if ( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) + #define dnsDNS_PORT 0x3500U /**< Little endian: Port used for DNS. */ + #define dnsONE_QUESTION 0x0100U /**< Little endian representation of a DNS question.*/ + #define dnsOUTGOING_FLAGS 0x0001U /**< Little endian representation of standard query. */ + #define dnsRX_FLAGS_MASK 0x0f80U /**< Little endian: The bits of interest in the flags field of incoming DNS messages. */ + #define dnsEXPECTED_RX_FLAGS 0x0080U /**< Little Endian: Should be a response, without any errors. */ +#else + #define dnsDNS_PORT 0x0035U /**< Big endian: Port used for DNS. */ + #define dnsONE_QUESTION 0x0001U /**< Big endian representation of a DNS question.*/ + #define dnsOUTGOING_FLAGS 0x0100U /**< Big endian representation of standard query. */ + #define dnsRX_FLAGS_MASK 0x800fU /**< Big endian: The bits of interest in the flags field of incoming DNS messages. */ + #define dnsEXPECTED_RX_FLAGS 0x8000U /**< Big endian: Should be a response, without any errors. */ + +#endif /* ipconfigBYTE_ORDER */ +#if ( ipconfigUSE_DNS != 0 ) + +/** @brief If the top two bits in the first character of a name field are set then the + * name field is an offset to the string, rather than the string itself. */ + #define dnsNAME_IS_OFFSET ( ( uint8_t ) 0xc0 ) + +/** @brief The maximum number of times a DNS request should be sent out if a response + * is not received, before giving up. */ + #ifndef ipconfigDNS_REQUEST_ATTEMPTS + #define ipconfigDNS_REQUEST_ATTEMPTS 5 + #endif + + +/* NBNS flags. */ + #if ( ipconfigUSE_NBNS == 1 ) + #define dnsNBNS_FLAGS_RESPONSE 0x8000U /**< NBNS response flag. */ + #define dnsNBNS_FLAGS_OPCODE_MASK 0x7800U /**< NBNS opcode bitmask. */ + #define dnsNBNS_FLAGS_OPCODE_QUERY 0x0000U /**< NBNS opcode query. */ + #endif /* ( ipconfigUSE_NBNS == 1 ) */ + +/* Host types. */ + #define dnsTYPE_A_HOST 0x01U /**< DNS type A host. */ + #define dnsCLASS_IN 0x01U /**< DNS class IN (Internet). */ + +/* Maximum hostname length as defined in RFC 1035 section 3.1. */ + #define dnsMAX_HOSTNAME_LENGTH 0xFFU + + #ifndef _lint + /* LLMNR constants. */ + #define dnsLLMNR_TTL_VALUE 300U /**< LLMNR time to live value of 5 minutes. */ + #define dnsLLMNR_FLAGS_IS_REPONSE 0x8000U /**< LLMNR flag value for response. */ + #endif /* _lint */ + +/* NBNS constants. */ + #if ( ipconfigUSE_NBNS != 0 ) + #define dnsNBNS_TTL_VALUE 3600UL /**< NBNS TTL: 1 hour valid. */ + #define dnsNBNS_TYPE_NET_BIOS 0x0020U /**< NBNS Type: NetBIOS. */ + #define dnsNBNS_CLASS_IN 0x01U /**< NBNS Class: IN (Internet). */ + #define dnsNBNS_NAME_FLAGS 0x6000U /**< NBNS name flags. */ + #define dnsNBNS_ENCODED_NAME_LENGTH 32 /**< NBNS encoded name length. */ + +/** @brief If the queried NBNS name matches with the device's name, + * the query will be responded to with these flags: */ + #define dnsNBNS_QUERY_RESPONSE_FLAGS ( 0x8500U ) + #endif /* ( ipconfigUSE_NBNS != 0 ) */ + + + #ifndef _lint + #if ( ipconfigUSE_DNS_CACHE == 0 ) + #if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY != 1 ) + #error When DNS caching is disabled, please make ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY equal to 1. + #endif + #endif + #endif + +/** @brief Define the ASCII value of '.' (Period/Full-stop). */ + #define ASCII_BASELINE_DOT 46U + +/* The Link-local Multicast Name Resolution (LLMNR) + * is included. + * Note that a special MAC address is required in addition to the NIC's actual + * MAC address: 01:00:5E:00:00:FC + * + * The target IP address will be 224.0.0.252 + */ + #if ( ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN ) + #define ipLLMNR_IP_ADDR 0xE00000FCUL + #else + #define ipLLMNR_IP_ADDR 0xFC0000E0UL + #endif /* ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN */ + + #define ipLLMNR_PORT 5355 /* Standard LLMNR port. */ + #define ipDNS_PORT 53 /* Standard DNS port. */ + #define ipDHCP_CLIENT 67 + #define ipDHCP_SERVER 68 + #define ipNBNS_PORT 137 /* NetBIOS Name Service. */ + #define ipNBDGM_PORT 138 /* Datagram Service, not included. */ + +/* DNS answer record header. */ + #include "pack_struct_start.h" + struct xDNSAnswerRecord + { + uint16_t usType; /**< Type of DNS answer record. */ + uint16_t usClass; /**< Class of DNS answer record. */ + uint32_t ulTTL; /**< Number of seconds the result can be cached. */ + uint16_t usDataLength; /**< Length of the data field. */ + } + #include "pack_struct_end.h" + typedef struct xDNSAnswerRecord DNSAnswerRecord_t; + +/* Below #include just tells the compiler to pack the structure. + * It is included in to make the code more readable */ + #include "pack_struct_start.h" + struct xDNSMessage + { + uint16_t usIdentifier; /**< Query identifier. Used to match up replies to outstanding queries. */ + uint16_t usFlags; /**< Flags. */ + uint16_t usQuestions; /**< Number of questions asked in this query. */ + uint16_t usAnswers; /**< Number of answers being provided in this query. */ + uint16_t usAuthorityRRs; /**< Authoritative name server resource records. */ + uint16_t usAdditionalRRs; /**< Additional resource records.*/ + } + #include "pack_struct_end.h" + typedef struct xDNSMessage DNSMessage_t; + + #if ( ipconfigUSE_LLMNR == 1 ) + + #include "pack_struct_start.h" + struct xLLMNRAnswer + { + uint8_t ucNameCode; /**< Name type. */ + uint8_t ucNameOffset; /**< The name is not repeated in the answer, only the offset is given with "0xc0 " */ + uint16_t usType; /**< Type of the Resource record. */ + uint16_t usClass; /**< Class of the Resource record. */ + uint32_t ulTTL; /**< Seconds till this entry can be cached. */ + uint16_t usDataLength; /**< Length of the address in this record. */ + uint32_t ulIPAddress; /**< The IP-address. */ + } + #include "pack_struct_end.h" + typedef struct xLLMNRAnswer LLMNRAnswer_t; + #endif /* if ( ipconfigUSE_LLMNR == 1 ) */ + + #if ( ipconfigUSE_NBNS == 1 ) + + #include "pack_struct_start.h" + struct xNBNSRequest + { + uint16_t usRequestId; /**< NBNS request ID. */ + uint16_t usFlags; /**< Flags of the DNS message. */ + uint16_t ulRequestCount; /**< The number of requests/questions in this query. */ + uint16_t usAnswerRSS; /**< The number of answers in this query. */ + uint16_t usAuthRSS; /**< Number of authoritative resource records. */ + uint16_t usAdditionalRSS; /**< Number of additional resource records. */ + uint8_t ucNameSpace; /**< Length of name. */ + uint8_t ucName[ dnsNBNS_ENCODED_NAME_LENGTH ]; /**< The domain name. */ + uint8_t ucNameZero; /**< Terminator of the name. */ + uint16_t usType; /**< Type of NBNS record. */ + uint16_t usClass; /**< Class of NBNS request. */ + } + #include "pack_struct_end.h" + typedef struct xNBNSRequest NBNSRequest_t; + + #include "pack_struct_start.h" + struct xNBNSAnswer + { + uint16_t usType; /**< Type of NBNS answer. */ + uint16_t usClass; /**< Class of NBNS answer. */ + uint32_t ulTTL; /**< Time in seconds for which the answer can be cached. */ + uint16_t usDataLength; /**< Data length. */ + uint16_t usNbFlags; /**< NetBIOS flags 0x6000 : IP-address, big-endian. */ + uint32_t ulIPAddress; /**< The IPv4 address. */ + } + #include "pack_struct_end.h" + typedef struct xNBNSAnswer NBNSAnswer_t; + #endif /* if ( ipconfigUSE_NBNS == 1 ) */ + + #if ( ipconfigDNS_USE_CALLBACKS != 0 ) + +/* + * Users may define this type of function as a callback. + * It will be called when a DNS reply is received or when a timeout has been reached. + */ + typedef void (* FOnDNSEvent ) ( const char * /* pcName */, + void * /* pvSearchID */, + uint32_t /* ulIPAddress */ ); + /** @brief The structure to hold information for a DNS callback. */ + typedef struct xDNS_Callback + { + TickType_t uxRemainingTime; /**< Timeout in ms */ + FOnDNSEvent pCallbackFunction; /**< Function to be called when the address has been found or when a timeout has been reached */ + TimeOut_t uxTimeoutState; /**< Timeout state. */ + void * pvSearchID; /**< Search ID of the callback function. */ + struct xLIST_ITEM xListItem; /**< List struct. */ + char pcName[ 1 ]; /**< 1 character name. */ + } DNSCallback_t; + #endif /* if ( ipconfigDNS_USE_CALLBACKS != 0 ) */ + +/** + * @brief structure to hold the buffer, its size and the data length + */ + typedef struct xDNSBuffer + { + uint8_t * pucPayloadBuffer; /**< Buffer pointer */ + size_t uxPayloadLength; /**< Payload size */ + size_t uxPayloadSize; /**< Total buffer size */ + } DNSBuffer_t; + + #if ( ipconfigUSE_LLMNR == 1 ) || ( ipconfigUSE_NBNS == 1 ) + +/* + * The following function should be provided by the user and return true if it + * matches the domain name. + */ + extern BaseType_t xApplicationDNSQueryHook( const char * pcName ); + #endif /* ( ipconfigUSE_LLMNR == 1 ) || ( ipconfigUSE_NBNS == 1 ) */ +#endif /* ipconfigUSE_DNS */ +#endif /* ifndef FREERTOS_DNS_GLOBALS_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_DNS_Networking.h b/FreeRTOS/source/include/FreeRTOS_DNS_Networking.h new file mode 100644 index 0000000..9ca2669 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_DNS_Networking.h @@ -0,0 +1,51 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://github.com/FreeRTOS + * https://www.FreeRTOS.org + */ +#ifndef FREERTOS_DNS_NETWORKING_H +#define FREERTOS_DNS_NETWORKING_H + +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_DNS_Globals.h" + +#if ( ipconfigUSE_DNS != 0 ) + +/* + * Create a socket and bind it to the standard DNS port number. Return the + * the created socket - or NULL if the socket could not be created or bound. + */ + Socket_t DNS_CreateSocket( TickType_t uxReadTimeOut_ticks ); + + BaseType_t DNS_SendRequest( Socket_t xDNSSocket, + const struct freertos_sockaddr * xAddress, + const struct xDNSBuffer * pxDNSBuf ); + + void DNS_ReadReply( const ConstSocket_t xDNSSocket, + struct freertos_sockaddr * xAddress, + struct xDNSBuffer * pxReceiveBuffer ); + + void DNS_CloseSocket( Socket_t xDNSSocket ); +#endif /* if ( ipconfigUSE_DNS != 0 ) */ +#endif /* ifndef FREERTOS_DNS_NETWORKING_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_DNS_Parser.h b/FreeRTOS/source/include/FreeRTOS_DNS_Parser.h new file mode 100644 index 0000000..ac85a3a --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_DNS_Parser.h @@ -0,0 +1,93 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://github.com/FreeRTOS + * https://www.FreeRTOS.org + */ + +#ifndef FREERTOS_DNS_PARSER_H +#define FREERTOS_DNS_PARSER_H + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" + +#include "FreeRTOS_DNS_Globals.h" + +/* Standard includes. */ +#include +#if ( ipconfigUSE_DNS != 0 ) + +/** @brief Flag DNS parsing errors in situations where an IPv4 address is the return + * type. */ + + #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) + size_t DNS_ReadNameField( const uint8_t * pucByte, + size_t uxRemainingBytes, + char * pcName, + size_t uxDestLen ); + #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */ + +/* + * Simple routine that jumps over the NAME field of a resource record. + * It returns the number of bytes read. + */ + size_t DNS_SkipNameField( const uint8_t * pucByte, + size_t uxLength ); + +/* + * Process a response packet from a DNS server. + * The parameter 'xExpected' indicates whether the identifier in the reply + * was expected, and thus if the DNS cache may be updated with the reply. + */ + uint32_t DNS_ParseDNSReply( uint8_t * pucUDPPayloadBuffer, + size_t uxBufferLength, + BaseType_t xExpected ); + +/* + * The NBNS and the LLMNR protocol share this reply function. + */ + #if ( ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_LLMNR == 1 ) ) + void prepareReplyDNSMessage( NetworkBufferDescriptor_t * pxNetworkBuffer, + BaseType_t lNetLength ); + #endif + #if ( ipconfigUSE_NBNS == 1 ) + void DNS_TreatNBNS( uint8_t * pucPayload, + size_t uxBufferLength, + uint32_t ulIPAddress ); + #endif + + uint32_t parseDNSAnswer( const DNSMessage_t * pxDNSMessageHeader, + uint8_t * pucByte, + size_t uxSourceBytesRemaining, + size_t * uxBytesRead + #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) + , + const char * pcName, + BaseType_t xDoStore + #endif + ); +#endif /* if ( ipconfigUSE_DNS != 0 ) */ +#endif /* ifndef FREERTOS_DNS_PARSER_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_ICMP.h b/FreeRTOS/source/include/FreeRTOS_ICMP.h new file mode 100644 index 0000000..4315d1a --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_ICMP.h @@ -0,0 +1,82 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_ICMP.h + * @brief Header file for Internet Control Message Protocol for the FreeRTOS+TCP network stack. + */ + +#ifndef FREERTOS_ICMP_H +#define FREERTOS_ICMP_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_DNS.h" + +/* ICMP protocol definitions. */ +#define ipICMP_ECHO_REQUEST ( ( uint8_t ) 8 ) /**< ICMP echo request. */ +#define ipICMP_ECHO_REPLY ( ( uint8_t ) 0 ) /**< ICMP echo reply. */ + +#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) + +/* + * Process incoming ICMP packets. + */ + eFrameProcessingResult_t ProcessICMPPacket( const NetworkBufferDescriptor_t * const pxNetworkBuffer ); +#endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_ICMP_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_IP.h b/FreeRTOS/source/include/FreeRTOS_IP.h new file mode 100644 index 0000000..2e68d0d --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_IP.h @@ -0,0 +1,456 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_IP_H +#define FREERTOS_IP_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +#include "FreeRTOS.h" +#include "task.h" + +/* Application level configuration options. */ +#include "FreeRTOSIPConfig.h" +#include "FreeRTOSIPConfigDefaults.h" +#include "IPTraceMacroDefaults.h" + +/* Constants defining the current version of the FreeRTOS+TCP + * network stack. */ +#define ipFR_TCP_VERSION_NUMBER "V3.1.0" +#define ipFR_TCP_VERSION_MAJOR 3 +#define ipFR_TCP_VERSION_MINOR 1 +/* Development builds are always version 999. */ +#define ipFR_TCP_VERSION_BUILD 0 + +/* Some constants defining the sizes of several parts of a packet. + * These defines come before including the configuration header files. */ + +/* The size of the Ethernet header is 14, meaning that 802.1Q VLAN tags + * are not ( yet ) supported. */ +#define ipSIZE_OF_ETH_HEADER 14U +#define ipSIZE_OF_IPv4_HEADER 20U +#define ipSIZE_OF_IGMP_HEADER 8U +#define ipSIZE_OF_ICMP_HEADER 8U +#define ipSIZE_OF_UDP_HEADER 8U +#define ipSIZE_OF_TCP_HEADER 20U + +#define ipSIZE_OF_IPv4_ADDRESS 4U + +/* + * Generate a randomized TCP Initial Sequence Number per RFC. + * This function must be provided by the application builder. + */ +/* This function is defined generally by the application. */ +extern uint32_t ulApplicationGetNextSequenceNumber( uint32_t ulSourceAddress, + uint16_t usSourcePort, + uint32_t ulDestinationAddress, + uint16_t usDestinationPort ); + +/* The number of octets in the MAC and IP addresses respectively. */ +#define ipMAC_ADDRESS_LENGTH_BYTES ( 6U ) +#define ipIP_ADDRESS_LENGTH_BYTES ( 4U ) + +/* IP protocol definitions. */ +#define ipPROTOCOL_ICMP ( 1U ) +#define ipPROTOCOL_IGMP ( 2U ) +#define ipPROTOCOL_TCP ( 6U ) +#define ipPROTOCOL_UDP ( 17U ) + +/* The character used to fill ICMP echo requests, and therefore also the + * character expected to fill ICMP echo replies. */ +#define ipECHO_DATA_FILL_BYTE 'x' + +/* Dimensions the buffers that are filled by received Ethernet frames. */ +#define ipSIZE_OF_ETH_CRC_BYTES ( 4UL ) +#define ipSIZE_OF_ETH_OPTIONAL_802_1Q_TAG_BYTES ( 4UL ) +#define ipTOTAL_ETHERNET_FRAME_SIZE ( ( ( uint32_t ) ipconfigNETWORK_MTU ) + ( ( uint32_t ) ipSIZE_OF_ETH_HEADER ) + ipSIZE_OF_ETH_CRC_BYTES + ipSIZE_OF_ETH_OPTIONAL_802_1Q_TAG_BYTES ) + + +/* Space left at the beginning of a network buffer storage area to store a + * pointer back to the network buffer. Should be a multiple of 8 to ensure 8 byte + * alignment is maintained on architectures that require it. + * + * In order to get a 32-bit alignment of network packets, an offset of 2 bytes + * would be desirable, as defined by ipconfigPACKET_FILLER_SIZE. So the malloc'd + * buffer will have the following contents: + * uint32_t pointer; // word-aligned + * uchar_8 filler[6]; + * << ETH-header >> // half-word-aligned + * uchar_8 dest[6]; // start of pucEthernetBuffer + * uchar_8 dest[6]; + * uchar16_t type; + * << IP-header >> // word-aligned + * uint8_t ucVersionHeaderLength; + * etc + */ + +#if ( ipconfigBUFFER_PADDING != 0 ) + #define ipBUFFER_PADDING ipconfigBUFFER_PADDING +#else + #define ipBUFFER_PADDING ( 8U + ipconfigPACKET_FILLER_SIZE ) +#endif + +/* The offset of ucTCPFlags within the TCP header. */ +#define ipTCP_FLAGS_OFFSET 13U + +#define ipFIRST_LOOPBACK_IPv4 0x7F000000UL /**< Lowest IPv4 loopback address (including). */ +#define ipLAST_LOOPBACK_IPv4 0x80000000UL /**< Highest IPv4 loopback address (excluding). */ + +/** @brief Returned to indicate a valid checksum. */ +#define ipCORRECT_CRC 0xffffU + +/** @brief Returned to indicate incorrect checksum. */ +#define ipWRONG_CRC 0x0000U + +/** @brief Returned as the (invalid) checksum when the length of the data being checked + * had an invalid length. */ +#define ipINVALID_LENGTH 0x1234U + +/** @brief Returned as the (invalid) checksum when the protocol being checked is not + * handled. The value is chosen simply to be easy to spot when debugging. */ +#define ipUNHANDLED_PROTOCOL 0x4321U + +/** @brief The maximum time the IP task is allowed to remain in the Blocked state if no + * events are posted to the network event queue. */ +#ifndef ipconfigMAX_IP_TASK_SLEEP_TIME + #define ipconfigMAX_IP_TASK_SLEEP_TIME ( pdMS_TO_TICKS( 10000UL ) ) +#endif + +/* Trace macros to aid in debugging, disabled if ipconfigHAS_PRINTF != 1 */ +#if ( ipconfigHAS_PRINTF == 1 ) + #define DEBUG_DECLARE_TRACE_VARIABLE( type, var, init ) type var = ( init ) /**< Trace macro to set "type var = init". */ + #define DEBUG_SET_TRACE_VARIABLE( var, value ) var = ( value ) /**< Trace macro to set var = value. */ +#else + #define DEBUG_DECLARE_TRACE_VARIABLE( type, var, init ) /**< Empty definition since ipconfigHAS_PRINTF != 1. */ + #define DEBUG_SET_TRACE_VARIABLE( var, value ) /**< Empty definition since ipconfigHAS_PRINTF != 1. */ +#endif + + +/** + * The structure used to store buffers and pass them around the network stack. + * Buffers can be in use by the stack, in use by the network interface hardware + * driver, or free (not in use). + */ +typedef struct xNETWORK_BUFFER +{ + ListItem_t xBufferListItem; /**< Used to reference the buffer form the free buffer list or a socket. */ + uint32_t ulIPAddress; /**< Source or destination IP address, depending on usage scenario. */ + uint8_t * pucEthernetBuffer; /**< Pointer to the start of the Ethernet frame. */ + size_t xDataLength; /**< Starts by holding the total Ethernet frame length, then the UDP/TCP payload length. */ + uint16_t usPort; /**< Source or destination port, depending on usage scenario. */ + uint16_t usBoundPort; /**< The port to which a transmitting socket is bound. */ + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + struct xNETWORK_BUFFER * pxNextBuffer; /**< Possible optimisation for expert users - requires network driver support. */ + #endif +} NetworkBufferDescriptor_t; + +#include "pack_struct_start.h" + +/** + * MAC address structure. + */ +struct xMAC_ADDRESS +{ + uint8_t ucBytes[ ipMAC_ADDRESS_LENGTH_BYTES ]; /**< Byte array of the MAC address */ +} +#include "pack_struct_end.h" + +typedef struct xMAC_ADDRESS MACAddress_t; + +typedef enum eNETWORK_EVENTS +{ + eNetworkUp, /* The network is configured. */ + eNetworkDown /* The network connection has been lost. */ +} eIPCallbackEvent_t; + +/* MISRA check: some modules refer to this typedef even though + * ipconfigSUPPORT_OUTGOING_PINGS is not enabled. */ +typedef enum ePING_REPLY_STATUS +{ + eSuccess = 0, /**< A correct reply has been received for an outgoing ping. */ + eInvalidChecksum, /**< A reply was received for an outgoing ping but the checksum of the reply was incorrect. */ + eInvalidData /**< A reply was received to an outgoing ping but the payload of the reply was not correct. */ +} ePingReplyStatus_t; + +/** + * The software timer struct for various IP functions + */ +typedef struct xIP_TIMER +{ + uint32_t + bActive : 1, /**< This timer is running and must be processed. */ + bExpired : 1; /**< Timer has expired and a task must be processed. */ + TimeOut_t xTimeOut; /**< The timeout value. */ + TickType_t ulRemainingTime; /**< The amount of time remaining. */ + TickType_t ulReloadTime; /**< The value of reload time. */ +} IPTimer_t; + + +/* Endian related definitions. */ +#if ( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) + +/* FreeRTOS_htons / FreeRTOS_htonl: some platforms might have built-in versions + * using a single instruction so allow these versions to be overridden. */ + #ifndef FreeRTOS_htons + #define FreeRTOS_htons( usIn ) ( ( uint16_t ) ( ( ( usIn ) << 8U ) | ( ( usIn ) >> 8U ) ) ) + #endif + + #ifndef FreeRTOS_htonl + #define FreeRTOS_htonl( ulIn ) \ + ( \ + ( uint32_t ) \ + ( \ + ( ( ( ( uint32_t ) ( ulIn ) ) ) << 24 ) | \ + ( ( ( ( uint32_t ) ( ulIn ) ) & 0x0000ff00U ) << 8 ) | \ + ( ( ( ( uint32_t ) ( ulIn ) ) & 0x00ff0000U ) >> 8 ) | \ + ( ( ( ( uint32_t ) ( ulIn ) ) ) >> 24 ) \ + ) \ + ) + #endif /* ifndef FreeRTOS_htonl */ + +#else /* ipconfigBYTE_ORDER */ + + #define FreeRTOS_htons( x ) ( ( uint16_t ) ( x ) ) + #define FreeRTOS_htonl( x ) ( ( uint32_t ) ( x ) ) + +#endif /* ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN */ + +#define FreeRTOS_ntohs( x ) FreeRTOS_htons( x ) +#define FreeRTOS_ntohl( x ) FreeRTOS_htonl( x ) + +/* Some simple helper functions. */ +int32_t FreeRTOS_max_int32( int32_t a, + int32_t b ); + +uint32_t FreeRTOS_max_uint32( uint32_t a, + uint32_t b ); + +size_t FreeRTOS_max_size_t( size_t a, + size_t b ); + +int32_t FreeRTOS_min_int32( int32_t a, + int32_t b ); + +uint32_t FreeRTOS_min_uint32( uint32_t a, + uint32_t b ); + +size_t FreeRTOS_min_size_t( size_t a, + size_t b ); + +uint32_t FreeRTOS_round_up( uint32_t a, + uint32_t d ); +uint32_t FreeRTOS_round_down( uint32_t a, + uint32_t d ); + +#define ipMS_TO_MIN_TICKS( xTimeInMs ) ( ( pdMS_TO_TICKS( ( xTimeInMs ) ) < ( ( TickType_t ) 1U ) ) ? ( ( TickType_t ) 1U ) : pdMS_TO_TICKS( ( xTimeInMs ) ) ) + +/* For backward compatibility. */ +#define pdMS_TO_MIN_TICKS( xTimeInMs ) ipMS_TO_MIN_TICKS( xTimeInMs ) + +#ifndef pdTRUE_SIGNED + /* Temporary solution: eventually the defines below will appear in 'Source\include\projdefs.h' */ + #define pdTRUE_SIGNED pdTRUE + #define pdFALSE_SIGNED pdFALSE + #define pdTRUE_UNSIGNED ( 1U ) + #define pdFALSE_UNSIGNED ( 0U ) + #define ipTRUE_BOOL ( 1 == 1 ) + #define ipFALSE_BOOL ( 1 == 2 ) +#endif + +/* + * FULL, UP-TO-DATE AND MAINTAINED REFERENCE DOCUMENTATION FOR ALL THESE + * FUNCTIONS IS AVAILABLE ON THE FOLLOWING URL: + * http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/FreeRTOS_TCP_API_Functions.html + */ +BaseType_t FreeRTOS_IPInit( const uint8_t ucIPAddress[ ipIP_ADDRESS_LENGTH_BYTES ], + const uint8_t ucNetMask[ ipIP_ADDRESS_LENGTH_BYTES ], + const uint8_t ucGatewayAddress[ ipIP_ADDRESS_LENGTH_BYTES ], + const uint8_t ucDNSServerAddress[ ipIP_ADDRESS_LENGTH_BYTES ], + const uint8_t ucMACAddress[ ipMAC_ADDRESS_LENGTH_BYTES ] ); + +TaskHandle_t FreeRTOS_GetIPTaskHandle( void ); + +void * FreeRTOS_GetUDPPayloadBuffer( size_t uxRequestedSizeBytes, + TickType_t uxBlockTimeTicks ); +void FreeRTOS_GetAddressConfiguration( uint32_t * pulIPAddress, + uint32_t * pulNetMask, + uint32_t * pulGatewayAddress, + uint32_t * pulDNSServerAddress ); + +void FreeRTOS_SetAddressConfiguration( const uint32_t * pulIPAddress, + const uint32_t * pulNetMask, + const uint32_t * pulGatewayAddress, + const uint32_t * pulDNSServerAddress ); + +/* MISRA defining 'FreeRTOS_SendPingRequest' should be dependent on 'ipconfigSUPPORT_OUTGOING_PINGS'. + * In order not to break some existing project, define it unconditionally. */ +BaseType_t FreeRTOS_SendPingRequest( uint32_t ulIPAddress, + size_t uxNumberOfBytesToSend, + TickType_t uxBlockTimeTicks ); + +void FreeRTOS_ReleaseUDPPayloadBuffer( void const * pvBuffer ); +const uint8_t * FreeRTOS_GetMACAddress( void ); +void FreeRTOS_UpdateMACAddress( const uint8_t ucMACAddress[ ipMAC_ADDRESS_LENGTH_BYTES ] ); +#if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 ) + /* This function shall be defined by the application. */ + void vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent ); +#endif +#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) + void vApplicationPingReplyHook( ePingReplyStatus_t eStatus, + uint16_t usIdentifier ); +#endif +uint32_t FreeRTOS_GetIPAddress( void ); +void FreeRTOS_SetIPAddress( uint32_t ulIPAddress ); +void FreeRTOS_SetNetmask( uint32_t ulNetmask ); +void FreeRTOS_SetGatewayAddress( uint32_t ulGatewayAddress ); +uint32_t FreeRTOS_GetGatewayAddress( void ); +uint32_t FreeRTOS_GetDNSServerAddress( void ); +uint32_t FreeRTOS_GetNetmask( void ); +BaseType_t xARPWaitResolution( uint32_t ulIPAddress, + TickType_t uxTicksToWait ); + +BaseType_t FreeRTOS_IsNetworkUp( void ); + +#if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + UBaseType_t uxGetMinimumIPQueueSpace( void ); +#endif + +BaseType_t xIsNetworkDownEventPending( void ); + +/* + * Defined in FreeRTOS_Sockets.c + * //_RB_ Don't think this comment is correct. If this is for internal use only it should appear after all the public API functions and not start with FreeRTOS_. + * Socket has had activity, reset the timer so it will not be closed + * because of inactivity + */ +#if ( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) + const char * FreeRTOS_GetTCPStateName( UBaseType_t ulState ); +#endif + +/* _HT_ Temporary: show all valid ARP entries + */ +#if ( ipconfigHAS_PRINTF != 0 ) || ( ipconfigHAS_DEBUG_PRINTF != 0 ) + void FreeRTOS_PrintARPCache( void ); +#endif + +void FreeRTOS_ClearARP( void ); + +/* Return pdTRUE if the IPv4 address is a multicast address. */ +BaseType_t xIsIPv4Multicast( uint32_t ulIPAddress ); + +/* Set the MAC-address that belongs to a given IPv4 multi-cast address. */ +void vSetMultiCastIPv4MacAddress( uint32_t ulIPAddress, + MACAddress_t * pxMACAddress ); + +#if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) + +/* DHCP has an option for clients to register their hostname. It doesn't + * have much use, except that a device can be found in a router along with its + * name. If this option is used the callback below must be provided by the + * application writer to return a const string, denoting the device's name. */ +/* Typically this function is defined in a user module. */ + const char * pcApplicationHostnameHook( void ); + +#endif /* ipconfigDHCP_REGISTER_HOSTNAME */ + + +/* This xApplicationGetRandomNumber() will set *pulNumber to a random number, + * and return pdTRUE. When the random number generator is broken, it shall return + * pdFALSE. + * The function is defined in 'iot_secure_sockets.c'. + * If that module is not included in the project, the application must provide an + * implementation of it. + * The macro's ipconfigRAND32() and configRAND32() are not in use anymore. */ + +/* "xApplicationGetRandomNumber" is declared but never defined, because it may + * be defined in a user module. */ +BaseType_t xApplicationGetRandomNumber( uint32_t * pulNumber ); + +/** @brief The pointer to buffer with packet waiting for ARP resolution. This variable + * is defined in FreeRTOS_IP.c. + * This pointer is for internal use only. */ +extern NetworkBufferDescriptor_t * pxARPWaitingNetworkBuffer; + +/* For backward compatibility define old structure names to the newer equivalent + * structure name. */ +#ifndef ipconfigENABLE_BACKWARD_COMPATIBILITY + #define ipconfigENABLE_BACKWARD_COMPATIBILITY 1 +#endif + +#if ( ipconfigENABLE_BACKWARD_COMPATIBILITY == 1 ) + #define xIPStackEvent_t IPStackEvent_t + #define xNetworkBufferDescriptor_t NetworkBufferDescriptor_t + #define xMACAddress_t MACAddress_t + #define xWinProperties_t WinProperties_t + #define xSocket_t Socket_t + #define xSocketSet_t SocketSet_t + #define ipSIZE_OF_IP_HEADER ipSIZE_OF_IPv4_HEADER + +/* Since August 2016, the public types and fields below have changed name: + * abbreviations TCP/UDP are now written in capitals, and type names now end with "_t". */ + #define FOnConnected FOnConnected_t + #define FOnTcpReceive FOnTCPReceive_t + #define FOnTcpSent FOnTCPSent_t + #define FOnUdpReceive FOnUDPReceive_t + #define FOnUdpSent FOnUDPSent_t + + #define pOnTcpConnected pxOnTCPConnected + #define pOnTcpReceive pxOnTCPReceive + #define pOnTcpSent pxOnTCPSent + #define pOnUdpReceive pxOnUDPReceive + #define pOnUdpSent pxOnUDPSent + + #define FOnUdpSent FOnUDPSent_t + #define FOnTcpSent FOnTCPSent_t +#endif /* ipconfigENABLE_BACKWARD_COMPATIBILITY */ + +#if ( ipconfigHAS_PRINTF != 0 ) + extern void vPrintResourceStats( void ); +#else + #define vPrintResourceStats() do {} while( ipFALSE_BOOL ) /**< ipconfigHAS_PRINTF is not defined. Define vPrintResourceStats to a do-while( 0 ). */ +#endif + +#if ( ipconfigUSE_TCP != 0 ) + +/** @brief Set to a non-zero value if one or more TCP message have been processed + * within the last round. */ + extern BaseType_t xProcessedTCPMessage; +#endif + +#include "FreeRTOS_IP_Utils.h" + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_IP_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_IP_Private.h b/FreeRTOS/source/include/FreeRTOS_IP_Private.h new file mode 100644 index 0000000..347e673 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_IP_Private.h @@ -0,0 +1,857 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + */ + +#ifndef FREERTOS_IP_PRIVATE_H +#define FREERTOS_IP_PRIVATE_H +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* Application level configuration options. */ +#include "FreeRTOSIPConfig.h" +#include "FreeRTOSIPConfigDefaults.h" +#include "FreeRTOS_Sockets.h" +#include "IPTraceMacroDefaults.h" +#include "FreeRTOS_Stream_Buffer.h" + +#if ( ipconfigUSE_TCP == 1 ) + #include "FreeRTOS_TCP_WIN.h" + #include "FreeRTOS_TCP_IP.h" +#endif + +#include "semphr.h" + +#include "event_groups.h" + +#ifdef TEST + int ipFOREVER( void ); +#else + #define ipFOREVER() 1 +#endif + +/** + * Structure to hold the information about the Network parameters. + */ +typedef struct xNetworkAddressingParameters +{ + uint32_t ulDefaultIPAddress; /**< The default IP address */ + uint32_t ulNetMask; /**< The netmask */ + uint32_t ulGatewayAddress; /**< The gateway address */ + uint32_t ulDNSServerAddress; /**< The DNS server address */ + uint32_t ulBroadcastAddress; /**< The Broadcast address */ +} NetworkAddressingParameters_t; + +extern BaseType_t xTCPWindowLoggingLevel; +extern QueueHandle_t xNetworkEventQueue; + +/*-----------------------------------------------------------*/ +/* Protocol headers. */ +/*-----------------------------------------------------------*/ + +#include "pack_struct_start.h" +struct xETH_HEADER +{ + MACAddress_t xDestinationAddress; /**< Destination address 0 + 6 = 6 */ + MACAddress_t xSourceAddress; /**< Source address 6 + 6 = 12 */ + uint16_t usFrameType; /**< The EtherType field 12 + 2 = 14 */ +} +#include "pack_struct_end.h" +typedef struct xETH_HEADER EthernetHeader_t; + +#include "pack_struct_start.h" +struct xARP_HEADER +{ + uint16_t usHardwareType; /**< Network Link Protocol type 0 + 2 = 2 */ + uint16_t usProtocolType; /**< The internetwork protocol 2 + 2 = 4 */ + uint8_t ucHardwareAddressLength; /**< Length in octets of a hardware address 4 + 1 = 5 */ + uint8_t ucProtocolAddressLength; /**< Length in octets of the internetwork protocol 5 + 1 = 6 */ + uint16_t usOperation; /**< Operation that the sender is performing 6 + 2 = 8 */ + MACAddress_t xSenderHardwareAddress; /**< Media address of the sender 8 + 6 = 14 */ + uint8_t ucSenderProtocolAddress[ 4 ]; /**< Internetwork address of sender 14 + 4 = 18 */ + MACAddress_t xTargetHardwareAddress; /**< Media address of the intended receiver 18 + 6 = 24 */ + uint32_t ulTargetProtocolAddress; /**< Internetwork address of the intended receiver 24 + 4 = 28 */ +} +#include "pack_struct_end.h" +typedef struct xARP_HEADER ARPHeader_t; + +#include "pack_struct_start.h" +struct xIP_HEADER +{ + uint8_t ucVersionHeaderLength; /**< The version field + internet header length 0 + 1 = 1 */ + uint8_t ucDifferentiatedServicesCode; /**< Differentiated services code point + ECN 1 + 1 = 2 */ + uint16_t usLength; /**< Entire Packet size 2 + 2 = 4 */ + uint16_t usIdentification; /**< Identification field 4 + 2 = 6 */ + uint16_t usFragmentOffset; /**< Fragment flags and fragment offset 6 + 2 = 8 */ + uint8_t ucTimeToLive; /**< Time to live field 8 + 1 = 9 */ + uint8_t ucProtocol; /**< Protocol used in the IP-datagram 9 + 1 = 10 */ + uint16_t usHeaderChecksum; /**< Checksum of the IP-header 10 + 2 = 12 */ + uint32_t ulSourceIPAddress; /**< IP address of the source 12 + 4 = 16 */ + uint32_t ulDestinationIPAddress; /**< IP address of the destination 16 + 4 = 20 */ +} +#include "pack_struct_end.h" +typedef struct xIP_HEADER IPHeader_t; + +#include "pack_struct_start.h" +struct xICMP_HEADER +{ + uint8_t ucTypeOfMessage; /**< The ICMP type 0 + 1 = 1 */ + uint8_t ucTypeOfService; /**< The ICMP subtype 1 + 1 = 2 */ + uint16_t usChecksum; /**< The checksum of whole ICMP packet 2 + 2 = 4 */ + uint16_t usIdentifier; /**< Used in some types of ICMP 4 + 2 = 6 */ + uint16_t usSequenceNumber; /**< Used in some types of ICMP 6 + 2 = 8 */ +} +#include "pack_struct_end.h" +typedef struct xICMP_HEADER ICMPHeader_t; + +#include "pack_struct_start.h" +struct xUDP_HEADER +{ + uint16_t usSourcePort; /**< The source port 0 + 2 = 2 */ + uint16_t usDestinationPort; /**< The destination port 2 + 2 = 4 */ + uint16_t usLength; /**< The size of the whole UDP packet 4 + 2 = 6 */ + uint16_t usChecksum; /**< The checksum of the whole UDP Packet 6 + 2 = 8 */ +} +#include "pack_struct_end.h" +typedef struct xUDP_HEADER UDPHeader_t; + +#include "pack_struct_start.h" +struct xTCP_HEADER +{ + uint16_t usSourcePort; /**< The Source port + 2 = 2 */ + uint16_t usDestinationPort; /**< The destination port + 2 = 4 */ + uint32_t ulSequenceNumber; /**< The Sequence number + 4 = 8 */ + uint32_t ulAckNr; /**< The acknowledgement number + 4 = 12 */ + uint8_t ucTCPOffset; /**< The value of TCP offset + 1 = 13 */ + uint8_t ucTCPFlags; /**< The TCP-flags field + 1 = 14 */ + uint16_t usWindow; /**< The size of the receive window + 2 = 15 */ + uint16_t usChecksum; /**< The checksum of the header + 2 = 18 */ + uint16_t usUrgent; /**< Pointer to the last urgent data byte + 2 = 20 */ + #if ipconfigUSE_TCP == 1 + uint8_t ucOptdata[ ipSIZE_TCP_OPTIONS ]; /**< The options + 12 = 32 */ + #endif +} +#include "pack_struct_end.h" +typedef struct xTCP_HEADER TCPHeader_t; + + +/*-----------------------------------------------------------*/ +/* Nested protocol packets. */ +/*-----------------------------------------------------------*/ + +#include "pack_struct_start.h" +struct xARP_PACKET +{ + EthernetHeader_t xEthernetHeader; /**< The ethernet header of an ARP Packet 0 + 14 = 14 */ + ARPHeader_t xARPHeader; /**< The ARP header of an ARP Packet 14 + 28 = 42 */ +} +#include "pack_struct_end.h" +typedef struct xARP_PACKET ARPPacket_t; + +#include "pack_struct_start.h" +struct xIP_PACKET +{ + EthernetHeader_t xEthernetHeader; + IPHeader_t xIPHeader; +} +#include "pack_struct_end.h" +typedef struct xIP_PACKET IPPacket_t; + +#include "pack_struct_start.h" +struct xICMP_PACKET +{ + EthernetHeader_t xEthernetHeader; /**< The Ethernet header of an ICMP packet. */ + IPHeader_t xIPHeader; /**< The IP header of an ICMP packet. */ + ICMPHeader_t xICMPHeader; /**< The ICMP header of an ICMP packet. */ +} +#include "pack_struct_end.h" +typedef struct xICMP_PACKET ICMPPacket_t; + +#include "pack_struct_start.h" +struct xUDP_PACKET +{ + EthernetHeader_t xEthernetHeader; /**< UDP-Packet ethernet header 0 + 14 = 14 */ + IPHeader_t xIPHeader; /**< UDP-Packet IP header 14 + 20 = 34 */ + UDPHeader_t xUDPHeader; /**< UDP-Packet UDP header 34 + 8 = 42 */ +} +#include "pack_struct_end.h" +typedef struct xUDP_PACKET UDPPacket_t; + +#include "pack_struct_start.h" +struct xTCP_PACKET +{ + EthernetHeader_t xEthernetHeader; /**< The ethernet header 0 + 14 = 14 */ + IPHeader_t xIPHeader; /**< The IP header 14 + 20 = 34 */ + TCPHeader_t xTCPHeader; /**< The TCP header 34 + 32 = 66 */ +} +#include "pack_struct_end.h" +typedef struct xTCP_PACKET TCPPacket_t; + +/** + * Union for the protocol packet to save space. Any packet cannot have more than one + * of the below protocol packets. + */ +typedef union XPROT_PACKET +{ + ARPPacket_t xARPPacket; /**< Union member: ARP packet struct */ + TCPPacket_t xTCPPacket; /**< Union member: TCP packet struct */ + UDPPacket_t xUDPPacket; /**< Union member: UDP packet struct */ + ICMPPacket_t xICMPPacket; /**< Union member: ICMP packet struct */ +} ProtocolPacket_t; + +/** + * Union for protocol headers to save space (RAM). Any packet cannot have more than one of + * the below protocols. + */ +typedef union xPROT_HEADERS +{ + ICMPHeader_t xICMPHeader; /**< Union member: ICMP header */ + UDPHeader_t xUDPHeader; /**< Union member: UDP header */ + TCPHeader_t xTCPHeader; /**< Union member: TCP header */ +} ProtocolHeaders_t; + +/* The maximum UDP payload length. */ +#define ipMAX_UDP_PAYLOAD_LENGTH ( ( ipconfigNETWORK_MTU - ipSIZE_OF_IPv4_HEADER ) - ipSIZE_OF_UDP_HEADER ) + +typedef enum +{ + eReleaseBuffer = 0, /* Processing the frame did not find anything to do - just release the buffer. */ + eProcessBuffer, /* An Ethernet frame has a valid address - continue process its contents. */ + eReturnEthernetFrame, /* The Ethernet frame contains an ARP or ICMP packet that can be returned to its source. */ + eFrameConsumed, /* Processing the Ethernet packet contents resulted in the payload being sent to the stack. */ + eWaitingARPResolution /* Frame is awaiting ARP resolution. */ +} eFrameProcessingResult_t; + +typedef enum +{ + eNoEvent = -1, + eNetworkDownEvent, /* 0: The network interface has been lost and/or needs [re]connecting. */ + eNetworkRxEvent, /* 1: The network interface has queued a received Ethernet frame. */ + eNetworkTxEvent, /* 2: Let the IP-task send a network packet. */ + eARPTimerEvent, /* 3: The ARP timer expired. */ + eStackTxEvent, /* 4: The software stack has queued a packet to transmit. */ + eDHCPEvent, /* 5: Process the DHCP state machine. */ + eTCPTimerEvent, /* 6: See if any TCP socket needs attention. */ + eTCPAcceptEvent, /* 7: Client API FreeRTOS_accept() waiting for client connections. */ + eTCPNetStat, /* 8: IP-task is asked to produce a netstat listing. */ + eSocketBindEvent, /* 9: Send a message to the IP-task to bind a socket to a port. */ + eSocketCloseEvent, /*10: Send a message to the IP-task to close a socket. */ + eSocketSelectEvent, /*11: Send a message to the IP-task for select(). */ + eSocketSignalEvent, /*12: A socket must be signalled. */ + eSocketSetDeleteEvent, /*13: A socket set must be deleted. */ +} eIPEvent_t; + +/** + * Structure for the information of the commands issued to the IP task. + */ +typedef struct IP_TASK_COMMANDS +{ + eIPEvent_t eEventType; /**< The event-type enum */ + void * pvData; /**< The data in the event */ +} IPStackEvent_t; + +#define ipBROADCAST_IP_ADDRESS 0xffffffffU + +/* Offset into the Ethernet frame that is used to temporarily store information + * on the fragmentation status of the packet being sent. The value is important, + * as it is past the location into which the destination address will get placed. */ +#define ipFRAGMENTATION_PARAMETERS_OFFSET ( 6 ) +#define ipSOCKET_OPTIONS_OFFSET ( 6 ) + + +/* The offset into a UDP packet at which the UDP data (payload) starts. */ +#define ipUDP_PAYLOAD_OFFSET_IPv4 ( sizeof( UDPPacket_t ) ) + +/* The offset into an IP packet into which the IP data (payload) starts. */ +#define ipIP_PAYLOAD_OFFSET ( sizeof( IPPacket_t ) ) + +#if ( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) + +/* Ethernet frame types. */ + #define ipARP_FRAME_TYPE ( 0x0608U ) + #define ipIPv4_FRAME_TYPE ( 0x0008U ) + +/* ARP related definitions. */ + #define ipARP_PROTOCOL_TYPE ( 0x0008U ) + #define ipARP_HARDWARE_TYPE_ETHERNET ( 0x0100U ) + #define ipARP_REQUEST ( 0x0100U ) + #define ipARP_REPLY ( 0x0200U ) + +/* The bits in the two byte IP header field that make up the fragment offset value. */ + #define ipFRAGMENT_OFFSET_BIT_MASK ( ( uint16_t ) 0xff1fU ) + +/* The bits in the two byte IP header field that make up the flags value. */ + #define ipFRAGMENT_FLAGS_BIT_MASK ( ( uint16_t ) 0x00e0U ) + +/* Don't Fragment Flag */ + #define ipFRAGMENT_FLAGS_DONT_FRAGMENT ( ( uint16_t ) 0x0040U ) + +/* More Fragments Flag */ + #define ipFRAGMENT_FLAGS_MORE_FRAGMENTS ( ( uint16_t ) 0x0020U ) + +#else /* if ( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) */ + +/* Ethernet frame types. */ + #define ipARP_FRAME_TYPE ( 0x0806U ) + #define ipIPv4_FRAME_TYPE ( 0x0800U ) + +/* ARP related definitions. */ + #define ipARP_PROTOCOL_TYPE ( 0x0800U ) + #define ipARP_HARDWARE_TYPE_ETHERNET ( 0x0001U ) + #define ipARP_REQUEST ( 0x0001 ) + #define ipARP_REPLY ( 0x0002 ) + +/* The bits in the two byte IP header field that make up the fragment offset value. */ + #define ipFRAGMENT_OFFSET_BIT_MASK ( ( uint16_t ) 0x1fffU ) + +/* The bits in the two byte IP header field that make up the flags value. */ + #define ipFRAGMENT_FLAGS_BIT_MASK ( ( uint16_t ) 0xe000U ) + +/* Don't Fragment Flag */ + #define ipFRAGMENT_FLAGS_DONT_FRAGMENT ( ( uint16_t ) 0x4000U ) + +/* More Fragments Flag */ + #define ipFRAGMENT_FLAGS_MORE_FRAGMENTS ( ( uint16_t ) 0x2000U ) + +#endif /* ipconfigBYTE_ORDER */ + +/* For convenience, a MAC address of all zeros and another of all 0xffs are + * defined const for quick reference. */ +extern const MACAddress_t xBroadcastMACAddress; /* all 0xff's */ +extern uint16_t usPacketIdentifier; + +/** @brief The list that contains mappings between sockets and port numbers. + * Accesses to this list must be protected by critical sections of + * some kind. + */ +extern List_t xBoundUDPSocketsList; + +/** + * Define a default UDP packet header (declared in FreeRTOS_UDP_IP.c) + */ +typedef union xUDPPacketHeader +{ + uint8_t ucBytes[ 24 ]; /**< Member: 8-bit array */ + uint32_t ulWords[ 6 ]; /**< Member: 32-bit array */ +} UDPPacketHeader_t; +extern UDPPacketHeader_t xDefaultPartUDPPacketHeader; + + +/* Structure that stores the netmask, gateway address and DNS server addresses. */ +extern NetworkAddressingParameters_t xNetworkAddressing; + +/* Structure that stores the defaults for netmask, gateway address and DNS. + * These values will be copied to 'xNetworkAddressing' in case DHCP is not used, + * and also in case DHCP does not lead to a confirmed request. */ +/*lint -e9003*/ +extern NetworkAddressingParameters_t xDefaultAddressing; /*lint !e9003 could define variable 'xDefaultAddressing' at block scope [MISRA 2012 Rule 8.9, advisory]. */ + +/* True when BufferAllocation_1.c was included, false for BufferAllocation_2.c */ +extern const BaseType_t xBufferAllocFixedSize; + +/* Defined in FreeRTOS_Sockets.c */ +#if ( ipconfigUSE_TCP == 1 ) + extern List_t xBoundTCPSocketsList; +#endif + +/* The local IP address is accessed from within xDefaultPartUDPPacketHeader, + * rather than duplicated in its own variable. */ +#define ipLOCAL_IP_ADDRESS_POINTER ( ( uint32_t * ) &( xDefaultPartUDPPacketHeader.ulWords[ 20U / sizeof( uint32_t ) ] ) ) + +/* The local MAC address is accessed from within xDefaultPartUDPPacketHeader, + * rather than duplicated in its own variable. */ +#define ipLOCAL_MAC_ADDRESS ( xDefaultPartUDPPacketHeader.ucBytes ) + +/* ICMP packets are sent using the same function as UDP packets. The port + * number is used to distinguish between the two, as 0 is an invalid UDP port. */ +#define ipPACKET_CONTAINS_ICMP_DATA ( 0 ) + +/* For now, the lower 8 bits in 'xEventBits' will be reserved for the above + * socket events. */ +#define SOCKET_EVENT_BIT_COUNT 8 + +#define vSetField16( pxBase, xType, xField, usValue ) \ + { \ + ( ( uint8_t * ) ( pxBase ) )[ offsetof( xType, xField ) + 0 ] = ( uint8_t ) ( ( usValue ) >> 8 ); \ + ( ( uint8_t * ) ( pxBase ) )[ offsetof( xType, xField ) + 1 ] = ( uint8_t ) ( ( usValue ) & 0xffU ); \ + } + +#define vSetField32( pxBase, xType, xField, ulValue ) \ + { \ + ( ( uint8_t * ) ( pxBase ) )[ offsetof( xType, xField ) + 0 ] = ( uint8_t ) ( ( ulValue ) >> 24 ); \ + ( ( uint8_t * ) ( pxBase ) )[ offsetof( xType, xField ) + 1 ] = ( uint8_t ) ( ( ( ulValue ) >> 16 ) & 0xffU ); \ + ( ( uint8_t * ) ( pxBase ) )[ offsetof( xType, xField ) + 2 ] = ( uint8_t ) ( ( ( ulValue ) >> 8 ) & 0xffU ); \ + ( ( uint8_t * ) ( pxBase ) )[ offsetof( xType, xField ) + 3 ] = ( uint8_t ) ( ( ulValue ) & 0xffU ); \ + } + +#define vFlip_16( left, right ) \ + do { \ + uint16_t tmp = ( left ); \ + ( left ) = ( right ); \ + ( right ) = tmp; \ + } while( ipFALSE_BOOL ) + +#define vFlip_32( left, right ) \ + do { \ + uint32_t tmp = ( left ); \ + ( left ) = ( right ); \ + ( right ) = tmp; \ + } while( ipFALSE_BOOL ) + +/* WARNING: Do NOT use this macro when the array was received as a parameter. */ +#ifndef ARRAY_SIZE + #define ARRAY_SIZE( x ) ( ( BaseType_t ) ( sizeof( x ) / sizeof( ( x )[ 0 ] ) ) ) +#endif + +#ifndef ARRAY_USIZE + #define ARRAY_USIZE( x ) ( ( UBaseType_t ) ( sizeof( x ) / sizeof( ( x )[ 0 ] ) ) ) +#endif + +/* + * Create a message that contains a command to initialise the network interface. + * This is used during initialisation, and at any time the network interface + * goes down thereafter. The network interface hardware driver is responsible + * for sending the message that contains the network interface down command/ + * event. + * + * Only use the FreeRTOS_NetworkDownFromISR() version if the function is to be + * called from an interrupt service routine. If FreeRTOS_NetworkDownFromISR() + * returns a non-zero value then a context switch should be performed before + * the interrupt is exited. + */ +void FreeRTOS_NetworkDown( void ); +BaseType_t FreeRTOS_NetworkDownFromISR( void ); + +/* + * Processes incoming ARP packets. + */ +eFrameProcessingResult_t eARPProcessPacket( ARPPacket_t * const pxARPFrame ); + +/* + * Inspect an Ethernet frame to see if it contains data that the stack needs to + * process. eProcessBuffer is returned if the frame should be processed by the + * stack. eReleaseBuffer is returned if the frame should be discarded. + */ +eFrameProcessingResult_t eConsiderFrameForProcessing( const uint8_t * const pucEthernetBuffer ); + +/* + * Return the checksum generated over xDataLengthBytes from pucNextData. + */ +uint16_t usGenerateChecksum( uint16_t usSum, + const uint8_t * pucNextData, + size_t uxByteCount ); + +/* Socket related private functions. */ + +/* + * The caller must ensure that pxNetworkBuffer->xDataLength is the UDP packet + * payload size (excluding packet headers) and that the packet in pucEthernetBuffer + * is at least the size of UDPPacket_t. + */ +BaseType_t xProcessReceivedUDPPacket( NetworkBufferDescriptor_t * pxNetworkBuffer, + uint16_t usPort, + BaseType_t * pxIsWaitingForARPResolution ); + +/* + * Initialize the socket list data structures for TCP and UDP. + */ +void vNetworkSocketsInit( void ); + +/* + * Returns pdTRUE if the IP task has been created and is initialised. Otherwise + * returns pdFALSE. + */ +BaseType_t xIPIsNetworkTaskReady( void ); + +#if ( ipconfigSOCKET_HAS_USER_WAKE_CALLBACK == 1 ) + struct xSOCKET; + typedef void (* SocketWakeupCallback_t)( struct xSOCKET * pxSocket ); +#endif + +#if ( ipconfigUSE_TCP == 1 ) + +/* + * Actually a user thing, but because xBoundTCPSocketsList, let it do by the + * IP-task + */ + #if ( ipconfigHAS_PRINTF != 0 ) + void vTCPNetStat( void ); + #endif + +/* + * At least one socket needs to check for timeouts + */ + TickType_t xTCPTimerCheck( BaseType_t xWillSleep ); + +/** + * Every TCP socket has a buffer space just big enough to store + * the last TCP header received. + * As a reference of this field may be passed to DMA, force the + * alignment to 8 bytes. + */ + typedef union + { + struct + { + uint64_t ullAlignmentWord; /**< Increase the alignment of this union by adding a 64-bit variable. */ + } a; /**< A struct to increase alignment. */ + struct + { + /* The next field only serves to give 'ucLastPacket' a correct + * alignment of 8 + 2. See comments in FreeRTOS_IP.h */ + uint8_t ucFillPacket[ ipconfigPACKET_FILLER_SIZE ]; + uint8_t ucLastPacket[ sizeof( TCPPacket_t ) ]; + } u; /**< The structure to give an alignment of 8 + 2 */ + } LastTCPPacket_t; + +/** + * Note that the values of all short and long integers in these structs + * are being stored in the native-endian way + * Translation should take place when accessing any structure which defines + * network packets, such as IPHeader_t and TCPHeader_t + */ + typedef struct TCPSOCKET + { + uint32_t ulRemoteIP; /**< IP address of remote machine */ + uint16_t usRemotePort; /**< Port on remote machine */ + struct + { + /* Most compilers do like bit-flags */ + uint32_t + bMssChange : 1, /**< This socket has seen a change in MSS */ + bPassAccept : 1, /**< when true, this socket may be returned in a call to accept() */ + bPassQueued : 1, /**< when true, this socket is an orphan until it gets connected + * Why an orphan? Because it may not be returned in a accept() call until it + * gets the state eESTABLISHED */ + bReuseSocket : 1, /**< When a listening socket gets a connection, do not create a new instance but keep on using it */ + bCloseAfterSend : 1, /**< As soon as the last byte has been transmitted, finalise the connection + * Useful in e.g. FTP connections, where the last data bytes are sent along with the FIN flag */ + bUserShutdown : 1, /**< User requesting a graceful shutdown */ + bCloseRequested : 1, /**< Request to finalise the connection */ + bLowWater : 1, /**< high-water level has been reached. Cleared as soon as 'rx-count < lo-water' */ + bWinChange : 1, /**< The value of bLowWater has changed, must send a window update */ + bSendKeepAlive : 1, /**< When this flag is true, a TCP keep-alive message must be send */ + bWaitKeepAlive : 1, /**< When this flag is true, a TCP keep-alive reply is expected */ + bConnPrepared : 1, /**< Connecting socket: Message has been prepared */ + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + bConnPassed : 1, /**< Connecting socket: Socket has been passed in a successful select() */ + #endif /* ipconfigSUPPORT_SELECT_FUNCTION */ + bFinAccepted : 1, /**< This socket has received (or sent) a FIN and accepted it */ + bFinSent : 1, /**< We've sent out a FIN */ + bFinRecv : 1, /**< We've received a FIN from our peer */ + bFinAcked : 1, /**< Our FIN packet has been acked */ + bFinLast : 1, /**< The last ACK (after FIN and FIN+ACK) has been sent or will be sent by the peer */ + bRxStopped : 1, /**< Application asked to temporarily stop reception */ + bMallocError : 1, /**< There was an error allocating a stream */ + bWinScaling : 1; /**< A TCP-Window Scaling option was offered and accepted in the SYN phase. */ + } bits; /**< The bits structure */ + uint32_t ulHighestRxAllowed; /**< The highest sequence number that we can receive at any moment */ + uint16_t usTimeout; /**< Time (in ticks) after which this socket needs attention */ + uint16_t usMSS; /**< Current Maximum Segment Size */ + uint16_t usChildCount; /**< In case of a listening socket: number of connections on this port number */ + uint16_t usBacklog; /**< In case of a listening socket: maximum number of concurrent connections on this port number */ + uint8_t ucRepCount; /**< Send repeat count, for retransmissions + * This counter is separate from the xmitCount in the + * TCP win segments */ + eIPTCPState_t eTCPState; /**< TCP state: see eTCP_STATE */ + struct xSOCKET * pxPeerSocket; /**< for server socket: child, for child socket: parent */ + #if ( ipconfigTCP_KEEP_ALIVE == 1 ) + uint8_t ucKeepRepCount; + TickType_t xLastAliveTime; /**< The last value of keepalive time.*/ + #endif /* ipconfigTCP_KEEP_ALIVE */ + #if ( ipconfigTCP_HANG_PROTECTION == 1 ) + TickType_t xLastActTime; /**< The last time when hang-protection was done.*/ + #endif /* ipconfigTCP_HANG_PROTECTION */ + size_t uxLittleSpace; /**< The value deemed as low amount of space. */ + size_t uxEnoughSpace; /**< The value deemed as enough space. */ + size_t uxRxStreamSize; /**< The Receive stream size */ + size_t uxTxStreamSize; /**< The transmit stream size */ + StreamBuffer_t * rxStream; /**< The pointer to the receive stream buffer. */ + StreamBuffer_t * txStream; /**< The pointer to the transmit stream buffer. */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + NetworkBufferDescriptor_t * pxAckMessage; /**< The pointer to the ACK message */ + #endif /* ipconfigUSE_TCP_WIN */ + LastTCPPacket_t xPacket; /**< Buffer space to store the last TCP header received. */ + uint8_t tcpflags; /**< TCP flags */ + #if ( ipconfigUSE_TCP_WIN != 0 ) + uint8_t ucMyWinScaleFactor; /**< Scaling factor of this device. */ + uint8_t ucPeerWinScaleFactor; /**< Scaling factor of the peer. */ + #endif + #if ( ipconfigUSE_CALLBACKS == 1 ) + FOnTCPReceive_t pxHandleReceive; /**< + * In case of a TCP socket: + * typedef void (* FOnTCPReceive_t) (Socket_t xSocket, void *pData, size_t xLength ); + */ + FOnTCPSent_t pxHandleSent; /**< Function pointer to handle a successful send event. */ + FOnConnected_t pxHandleConnected; /**< Actually type: typedef void (* FOnConnected_t) (Socket_t xSocket, BaseType_t ulConnected ); */ + #endif /* ipconfigUSE_CALLBACKS */ + uint32_t ulWindowSize; /**< Current Window size advertised by peer */ + size_t uxRxWinSize; /**< Fixed value: size of the TCP reception window */ + size_t uxTxWinSize; /**< Fixed value: size of the TCP transmit window */ + + TCPWindow_t xTCPWindow; /**< The TCP window struct*/ + } IPTCPSocket_t; + +#endif /* ipconfigUSE_TCP */ + +/** + * Structure to hold the information about a UDP socket. + */ +typedef struct UDPSOCKET +{ + List_t xWaitingPacketsList; /**< Incoming packets */ + #if ( ipconfigUDP_MAX_RX_PACKETS > 0 ) + UBaseType_t uxMaxPackets; /**< Protection: limits the number of packets buffered per socket */ + #endif /* ipconfigUDP_MAX_RX_PACKETS */ + #if ( ipconfigUSE_CALLBACKS == 1 ) + FOnUDPReceive_t pxHandleReceive; /**< + * In case of a UDP socket: + * typedef void (* FOnUDPReceive_t) (Socket_t xSocket, void *pData, size_t xLength, struct freertos_sockaddr *pxAddr ); + */ + FOnUDPSent_t pxHandleSent; /**< Function pointer to handle the events after a successful send. */ + #endif /* ipconfigUSE_CALLBACKS */ +} IPUDPSocket_t; + +/* Formally typedef'd as eSocketEvent_t. */ +enum eSOCKET_EVENT +{ + eSOCKET_RECEIVE = 0x0001, + eSOCKET_SEND = 0x0002, + eSOCKET_ACCEPT = 0x0004, + eSOCKET_CONNECT = 0x0008, + eSOCKET_BOUND = 0x0010, + eSOCKET_CLOSED = 0x0020, + eSOCKET_INTR = 0x0040, + eSOCKET_ALL = 0x007F, +}; + + +/** + * Structure to hold information for a socket. + */ +typedef struct xSOCKET +{ + EventBits_t xEventBits; /**< The eventbits to keep track of events. */ + EventGroupHandle_t xEventGroup; /**< The event group for this socket. */ + + ListItem_t xBoundSocketListItem; /**< Used to reference the socket from a bound sockets list. */ + TickType_t xReceiveBlockTime; /**< if recv[to] is called while no data is available, wait this amount of time. Unit in clock-ticks */ + TickType_t xSendBlockTime; /**< if send[to] is called while there is not enough space to send, wait this amount of time. Unit in clock-ticks */ + + uint16_t usLocalPort; /**< Local port on this machine */ + uint8_t ucSocketOptions; /**< Socket options */ + uint8_t ucProtocol; /**< choice of FREERTOS_IPPROTO_UDP/TCP */ + #if ( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 ) + SemaphoreHandle_t pxUserSemaphore; /**< The user semaphore */ + #endif /* ipconfigSOCKET_HAS_USER_SEMAPHORE */ + #if ( ipconfigSOCKET_HAS_USER_WAKE_CALLBACK == 1 ) + SocketWakeupCallback_t pxUserWakeCallback; /**< Pointer to the callback function. */ + #endif /* ipconfigSOCKET_HAS_USER_WAKE_CALLBACK */ + + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + struct xSOCKET_SET * pxSocketSet; /**< Pointer to the socket set structure */ + EventBits_t xSelectBits; /**< User may indicate which bits are interesting for this socket. */ + + EventBits_t xSocketBits; /**< These bits indicate the events which have actually occurred. + * They are maintained by the IP-task */ + #endif /* ipconfigSUPPORT_SELECT_FUNCTION */ + /* TCP/UDP specific fields: */ + /* Before accessing any member of this structure, it should be confirmed */ + /* that the protocol corresponds with the type of structure */ + + union + { + IPUDPSocket_t xUDP; /**< Union member: UDP socket*/ + #if ( ipconfigUSE_TCP == 1 ) + IPTCPSocket_t xTCP; /**< Union member: TCP socket */ + + uint64_t ullTCPAlignment; /**< Make sure that xTCP is 8-bytes aligned by + * declaring a 64-bit variable in the same union */ + #endif /* ipconfigUSE_TCP */ + } u; /**< Union of TCP/UDP socket */ +} FreeRTOS_Socket_t; + +#if ( ipconfigUSE_TCP == 1 ) + +/* + * Close the socket another time. + */ + void vSocketCloseNextTime( FreeRTOS_Socket_t * pxSocket ); + +/* + * Postpone a call to listen() by the IP-task. + */ + void vSocketListenNextTime( FreeRTOS_Socket_t * pxSocket ); + +/* + * Lookup a TCP socket, using a multiple matching: both port numbers and + * return IP address. + */ + FreeRTOS_Socket_t * pxTCPSocketLookup( uint32_t ulLocalIP, + UBaseType_t uxLocalPort, + uint32_t ulRemoteIP, + UBaseType_t uxRemotePort ); + +#endif /* ipconfigUSE_TCP */ + + +/* + * Look up a local socket by finding a match with the local port. + */ +FreeRTOS_Socket_t * pxUDPSocketLookup( UBaseType_t uxLocalPort ); + +/* + * Calculate the upper-layer checksum + * Works both for UDP, ICMP and TCP packages + * bOut = true: checksum will be set in outgoing packets + * bOut = false: checksum will be calculated for incoming packets + * returning 0xffff means: checksum was correct + */ +uint16_t usGenerateProtocolChecksum( uint8_t * pucEthernetBuffer, + size_t uxBufferLength, + BaseType_t xOutgoingPacket ); + +/* + * An Ethernet frame has been updated (maybe it was an ARP request or a PING + * request?) and is to be sent back to its source. + */ +void vReturnEthernetFrame( NetworkBufferDescriptor_t * pxNetworkBuffer, + BaseType_t xReleaseAfterSend ); + +/* + * The internal version of bind() + * If 'ulInternal' is true, it is called by the driver + * The TCP driver needs to bind a socket at the moment a listening socket + * creates a new connected socket + */ +BaseType_t vSocketBind( FreeRTOS_Socket_t * pxSocket, + struct freertos_sockaddr * pxBindAddress, + size_t uxAddressLength, + BaseType_t xInternal ); + +/* + * Internal function to add streaming data to a TCP socket. If ulIn == true, + * data will be added to the rxStream, otherwise to the tXStream. Normally data + * will be written with ulOffset == 0, meaning: at the end of the FIFO. When + * packet come in out-of-order, an offset will be used to put it in front and + * the head will not change yet. + */ +int32_t lTCPAddRxdata( FreeRTOS_Socket_t * pxSocket, + size_t uxOffset, + const uint8_t * pcData, + uint32_t ulByteCount ); + +/* + * Currently called for any important event. + */ +void vSocketWakeUpUser( FreeRTOS_Socket_t * pxSocket ); + +/* + * Some helping function, their meaning should be clear. + * Going by MISRA rules, these utility functions should not be defined + * if they are not being used anywhere. But their use depends on the + * application and hence these functions are defined unconditionally. + */ +extern uint32_t ulChar2u32( const uint8_t * pucPtr ); + +extern uint16_t usChar2u16( const uint8_t * pucPtr ); + +/* Check a single socket for retransmissions and timeouts */ +BaseType_t xTCPSocketCheck( FreeRTOS_Socket_t * pxSocket ); + +BaseType_t xTCPCheckNewClient( FreeRTOS_Socket_t * pxSocket ); + +/* Defined in FreeRTOS_Sockets.c + * Close a socket + */ +void * vSocketClose( FreeRTOS_Socket_t * pxSocket ); + +/* + * Send the event eEvent to the IP task event queue, using a block time of + * zero. Return pdPASS if the message was sent successfully, otherwise return + * pdFALSE. + */ +BaseType_t xSendEventToIPTask( eIPEvent_t eEvent ); + +/* + * The same as above, but a struct as a parameter, containing: + * eIPEvent_t eEventType; + * void *pvData; + */ +BaseType_t xSendEventStructToIPTask( const IPStackEvent_t * pxEvent, + TickType_t uxTimeout ); + +/* + * Returns a pointer to the original NetworkBuffer from a pointer to a UDP + * payload buffer. + */ +NetworkBufferDescriptor_t * pxUDPPayloadBuffer_to_NetworkBuffer( const void * pvBuffer ); + +/* + * Internal: Sets a new state for a TCP socket and performs the necessary + * actions like calling a OnConnected handler to notify the socket owner. + */ +#if ( ipconfigUSE_TCP == 1 ) + void vTCPStateChange( FreeRTOS_Socket_t * pxSocket, + enum eTCP_STATE eTCPState ); +#endif /* ipconfigUSE_TCP */ + +/* Returns pdTRUE is this function is called from the IP-task */ +BaseType_t xIsCallingFromIPTask( void ); + +#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/** @brief Structure for event groups of the Socket Select functions */ + typedef struct xSOCKET_SET + { + /** @brief Event group for the socket select function. + */ + EventGroupHandle_t xSelectGroup; + } SocketSelect_t; + + extern void vSocketSelect( const SocketSelect_t * pxSocketSet ); + +/** @brief Define the data that must be passed for a 'eSocketSelectEvent'. */ + typedef struct xSocketSelectMessage + { + TaskHandle_t xTaskhandle; /**< Task handle for use in the socket select functionality. */ + SocketSelect_t * pxSocketSet; /**< The event group for the socket select functionality. */ + } SocketSelectMessage_t; + +#endif /* ipconfigSUPPORT_SELECT_FUNCTION */ + +/* Send the network-up event and start the ARP timer. */ +void vIPNetworkUpCalls( void ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_IP_PRIVATE_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_IP_Timers.h b/FreeRTOS/source/include/FreeRTOS_IP_Timers.h new file mode 100644 index 0000000..d4bc2f1 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_IP_Timers.h @@ -0,0 +1,118 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @file FreeRTOS_IP_Timers.h + * @brief Header file for IP Timers on FreeRTOS+TCP network stack. + */ + +#ifndef FREERTOS_IP_TIMERS_H +#define FREERTOS_IP_TIMERS_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_DNS.h" + +/* + * Checks the ARP, DHCP and TCP timers to see if any periodic or timeout + * processing is required. + */ +void vCheckNetworkTimers( void ); + +/* + * Determine how long the IP task can sleep for, which depends on when the next + * periodic or timeout processing must be performed. + */ +TickType_t xCalculateSleepTime( void ); + +void vIPTimerStartARPResolution( TickType_t xTime ); + +void vIPSetTCPTimerExpiredState( BaseType_t xExpiredState ); + +void vIPSetARPTimerEnableState( BaseType_t xEnableState ); + +void vIPSetARPResolutionTimerEnableState( BaseType_t xEnableState ); + +#if ( ipconfigUSE_DHCP != 0 ) + +/** + * @brief Enable/disable the DHCP timer. + * @param[in] xEnableState: pdTRUE - enable timer; pdFALSE - disable timer. + */ + void vIPSetDHCPTimerEnableState( BaseType_t xEnableState ); +#endif + +#if ( ipconfigDNS_USE_CALLBACKS != 0 ) + +/** + * @brief Enable/disable the DNS timer. + * @param[in] xEnableState: pdTRUE - enable timer; pdFALSE - disable timer. + */ + void vIPSetDNSTimerEnableState( BaseType_t xEnableState ); +#endif + +void vARPTimerReload( TickType_t xTime ); +void vTCPTimerReload( TickType_t xTime ); +#if ( ipconfigUSE_DHCP == 1 ) + void vDHCPTimerReload( TickType_t xLeaseTime ); +#endif + +#if ( ipconfigDNS_USE_CALLBACKS != 0 ) + void vDNSTimerReload( uint32_t ulCheckTime ); +#endif + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_IP_TIMERS_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_IP_Utils.h b/FreeRTOS/source/include/FreeRTOS_IP_Utils.h new file mode 100644 index 0000000..98e49a3 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_IP_Utils.h @@ -0,0 +1,105 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_IP_UTILS_H +#define FREERTOS_IP_UTILS_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/** + * @file FreeRTOS_IP_Utils.h + * @brief Implements the utility functions for FreeRTOS_IP.c + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_DNS.h" + +#if ( ipconfigUSE_DHCP != 0 ) + +/** + * @brief Create a DHCP event. + * + * @return pdPASS or pdFAIL, depending on whether xSendEventStructToIPTask() + * succeeded. + */ + BaseType_t xSendDHCPEvent( void ); +#endif + +#if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) || ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + +/** + * @brief Get the network buffer from the packet buffer. + * + * @param[in] pvBuffer: Pointer to the packet buffer. + * + * @return The network buffer if the alignment is correct. Else a NULL is returned. + */ + NetworkBufferDescriptor_t * pxPacketBuffer_to_NetworkBuffer( const void * pvBuffer ); +#endif + +/** + * @brief Check the values of configuration options and assert on it. Also verify that the IP-task + * has not already been initialized. + */ +void vPreCheckConfigs( void ); + +/** + * @brief Called to create a network connection when the stack is first + * started, or when the network connection is lost. + */ +void prvProcessNetworkDownEvent( void ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_IP_UTILS_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_Sockets.h b/FreeRTOS/source/include/FreeRTOS_Sockets.h new file mode 100644 index 0000000..a154986 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_Sockets.h @@ -0,0 +1,551 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_SOCKETS_H + #define FREERTOS_SOCKETS_H + + #ifdef __cplusplus + extern "C" { + #endif + +/* Standard includes. */ + #include + +/* FreeRTOS includes. */ + #include "FreeRTOS.h" + #include "task.h" + +/* Application level configuration options. */ + #include "FreeRTOSIPConfig.h" + #include "FreeRTOSIPConfigDefaults.h" + + #ifndef FREERTOS_IP_CONFIG_H + #error FreeRTOSIPConfig.h has not been included yet + #endif + +/* Event bit definitions are required by the select functions. */ + #include "event_groups.h" + + #ifndef INC_FREERTOS_H + #error FreeRTOS.h must be included before FreeRTOS_Sockets.h. + #endif + + #ifndef INC_TASK_H + #ifndef TASK_H /* For compatibility with older FreeRTOS versions. */ + #error The FreeRTOS header file task.h must be included before FreeRTOS_Sockets.h. + #endif + #endif + +/* Assigned to an Socket_t variable when the socket is not valid, probably + * because it could not be created. */ + #define FREERTOS_INVALID_SOCKET ( ( Socket_t ) ~0U ) + +/* API function error values. As errno is supported, the FreeRTOS sockets + * functions return error codes rather than just a pass or fail indication. + * + * Like in errno.h, the error codes are defined as positive numbers. + * However, in case of an error, API 's will still negative values, e.g. + * return -pdFREERTOS_ERRNO_EWOULDBLOCK; + * in case an operation would block. + * + * The following defines are obsolete, please use -pdFREERTOS_ERRNO_Exxx. */ + #define FREERTOS_SOCKET_ERROR ( -1 ) + #define FREERTOS_EWOULDBLOCK ( -pdFREERTOS_ERRNO_EWOULDBLOCK ) + #define FREERTOS_EINVAL ( -pdFREERTOS_ERRNO_EINVAL ) + #define FREERTOS_EADDRNOTAVAIL ( -pdFREERTOS_ERRNO_EADDRNOTAVAIL ) + #define FREERTOS_EADDRINUSE ( -pdFREERTOS_ERRNO_EADDRINUSE ) + #define FREERTOS_ENOBUFS ( -pdFREERTOS_ERRNO_ENOBUFS ) + #define FREERTOS_ENOPROTOOPT ( -pdFREERTOS_ERRNO_ENOPROTOOPT ) + #define FREERTOS_ECLOSED ( -pdFREERTOS_ERRNO_ENOTCONN ) + +/* Values for the parameters to FreeRTOS_socket(), inline with the Berkeley + * standard. See the documentation of FreeRTOS_socket() for more information. */ + #define FREERTOS_AF_INET ( 2 ) + #define FREERTOS_AF_INET6 ( 10 ) + #define FREERTOS_SOCK_DGRAM ( 2 ) + #define FREERTOS_IPPROTO_UDP ( 17 ) + #define FREERTOS_SOCK_STREAM ( 1 ) + #define FREERTOS_IPPROTO_TCP ( 6 ) + #define FREERTOS_SOCK_DEPENDENT_PROTO ( 0 ) + +/* Values for xFlags parameter of Receive/Send functions. */ + #define FREERTOS_ZERO_COPY ( 1 ) /* Can be used with recvfrom(), sendto() and recv(), + * Indicates that the zero copy interface is being used. + * See the documentation for FreeRTOS_sockets() for more information. */ + #define FREERTOS_MSG_OOB ( 2 ) /* Not used. */ + #define FREERTOS_MSG_PEEK ( 4 ) /* Can be used with recvfrom() and recv(). */ + #define FREERTOS_MSG_DONTROUTE ( 8 ) /* Not used. */ + #define FREERTOS_MSG_DONTWAIT ( 16 ) /* Can be used with recvfrom(), sendto(), recv() and send(). */ + +/* Values that can be passed in the option name parameter of calls to + * FreeRTOS_setsockopt(). */ + #define FREERTOS_SO_RCVTIMEO ( 0 ) /* Used to set the receive time out. */ + #define FREERTOS_SO_SNDTIMEO ( 1 ) /* Used to set the send time out. */ + #define FREERTOS_SO_UDPCKSUM_OUT ( 2 ) /* Used to turn the use of the UDP checksum + * by a socket on or off. This also doubles + * as part of an 8-bit bitwise socket option. */ + #if ( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 ) + #define FREERTOS_SO_SET_SEMAPHORE ( 3 ) /* Used to set a user's semaphore. */ + #endif + + #if ( ipconfigUSE_TCP == 1 ) + #define FREERTOS_SO_SNDBUF ( 4 ) /* Set the size of the send buffer (TCP only). */ + #define FREERTOS_SO_RCVBUF ( 5 ) /* Set the size of the receive buffer (TCP only). */ + #endif + + #if ( ipconfigUSE_CALLBACKS == 1 ) + +/* Supply pointer to 'F_TCP_UDP_Handler_t' for pvOptionValue parameter in + * FreeRTOS_setsockopt() */ + #define FREERTOS_SO_TCP_CONN_HANDLER ( 6 ) /* Install a callback for (dis) connection events. */ + #define FREERTOS_SO_TCP_RECV_HANDLER ( 7 ) /* Install a callback for receiving TCP data. */ + #define FREERTOS_SO_TCP_SENT_HANDLER ( 8 ) /* Install a callback for sending TCP data. */ + #define FREERTOS_SO_UDP_RECV_HANDLER ( 9 ) /* Install a callback for receiving UDP data. */ + #define FREERTOS_SO_UDP_SENT_HANDLER ( 10 ) /* Install a callback for sending UDP data. */ + #endif + + #if ( ipconfigUSE_TCP == 1 ) + #define FREERTOS_SO_REUSE_LISTEN_SOCKET ( 11 ) /* When a listening socket gets connected, do not create a new one but re-use it. */ + #define FREERTOS_SO_CLOSE_AFTER_SEND ( 12 ) /* As soon as the last byte has been transmitted, finalise the connection. */ + #define FREERTOS_SO_WIN_PROPERTIES ( 13 ) /* Set all buffer and window properties in one call, parameter is pointer to WinProperties_t. */ + #define FREERTOS_SO_SET_FULL_SIZE ( 14 ) /* Refuse to send packets smaller than MSS. */ + #define FREERTOS_SO_STOP_RX ( 15 ) /* Temporarily hold up reception, used by streaming client. */ + #endif + + #if ( ipconfigUDP_MAX_RX_PACKETS > 0 ) + #define FREERTOS_SO_UDP_MAX_RX_PACKETS ( 16 ) /* This option helps to limit the maximum number of packets a UDP socket will buffer. */ + #endif + + #if ( ipconfigSOCKET_HAS_USER_WAKE_CALLBACK == 1 ) + #define FREERTOS_SO_WAKEUP_CALLBACK ( 17 ) + #endif + + #if ( ipconfigUSE_TCP == 1 ) + #define FREERTOS_SO_SET_LOW_HIGH_WATER ( 18 ) + #endif + + #if ( 0 ) /* Not Used */ + #define FREERTOS_NOT_LAST_IN_FRAGMENTED_PACKET ( 0x80 ) + #define FREERTOS_FRAGMENTED_PACKET ( 0x40 ) + #endif + + #if ( ipconfigUSE_TCP == 1 ) +/* Values for 'xHow' flag of FreeRTOS_shutdown(), currently ignored. */ + #define FREERTOS_SHUT_RD ( 0 ) + #define FREERTOS_SHUT_WR ( 1 ) + #define FREERTOS_SHUT_RDWR ( 2 ) + #endif + +/* For compatibility with the expected Berkeley sockets naming. */ + #define socklen_t uint32_t + +/** + * For this limited implementation, only two members are required in the + * Berkeley style sockaddr structure. + */ + struct freertos_sockaddr + { +/* _HT_ On 32- and 64-bit architectures, the addition of the two uint8_t + * fields sin_len and sin_family doesn't make the structure bigger, due to alignment. + * These fields are only inserted as a preparation for IPv6 + * and are not used in the IPv4-only release. */ + uint8_t sin_len; /**< length of this structure. */ + uint8_t sin_family; /**< FREERTOS_AF_INET. */ + uint16_t sin_port; /**< The port. */ + uint32_t sin_addr; /**< The IP address. */ + }; + +/* The socket type itself. */ + struct xSOCKET; + typedef struct xSOCKET * Socket_t; + typedef struct xSOCKET const * ConstSocket_t; + + extern BaseType_t xSocketValid( const ConstSocket_t xSocket ); + +/** + * FULL, UP-TO-DATE AND MAINTAINED REFERENCE DOCUMENTATION FOR ALL THESE + * FUNCTIONS IS AVAILABLE ON THE FOLLOWING URL: + * http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/FreeRTOS_TCP_API_Functions.html + */ + +/* Common Socket Attributes. */ + +/* Create a TCP or UDP socket. */ + Socket_t FreeRTOS_socket( BaseType_t xDomain, + BaseType_t xType, + BaseType_t xProtocol ); + +/* Binds a socket to a local port number. */ + BaseType_t FreeRTOS_bind( Socket_t xSocket, + struct freertos_sockaddr const * pxAddress, + socklen_t xAddressLength ); + +/* Sets a socket option. */ + BaseType_t FreeRTOS_setsockopt( Socket_t xSocket, + int32_t lLevel, + int32_t lOptionName, + const void * pvOptionValue, + size_t uxOptionLength ); + +/* Close a socket. */ + BaseType_t FreeRTOS_closesocket( Socket_t xSocket ); + + #if ( ipconfigSUPPORT_SIGNALS != 0 ) +/* Send a signal to the task which is waiting for a given socket. */ + BaseType_t FreeRTOS_SignalSocket( Socket_t xSocket ); + +/* Send a signal to the task which reads from this socket (FromISR version). */ + BaseType_t FreeRTOS_SignalSocketFromISR( Socket_t xSocket, + BaseType_t * pxHigherPriorityTaskWoken ); + #endif + +/* End Common Socket Attributes */ + + +/* UDP Socket Attributes. */ + +/* Send data to a UDP socket. */ + int32_t FreeRTOS_sendto( Socket_t xSocket, + const void * pvBuffer, + size_t uxTotalDataLength, + BaseType_t xFlags, + const struct freertos_sockaddr * pxDestinationAddress, + socklen_t xDestinationAddressLength ); + +/* Receive data from a UDP socket */ + int32_t FreeRTOS_recvfrom( const ConstSocket_t xSocket, + void * pvBuffer, + size_t uxBufferLength, + BaseType_t xFlags, + struct freertos_sockaddr * pxSourceAddress, + const socklen_t * pxSourceAddressLength ); + + +/* Function to get the local address and IP port. */ + size_t FreeRTOS_GetLocalAddress( ConstSocket_t xSocket, + struct freertos_sockaddr * pxAddress ); + + #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) +/* Returns true if an UDP socket exists bound to mentioned port number. */ + BaseType_t xPortHasUDPSocket( uint16_t usPortNr ); + #endif + +/* End UDP Socket Attributes */ + + + #if ( ipconfigUSE_TCP == 1 ) + +/* TCP Socket Attributes. */ + +/** + * Structure to hold the properties of Tx/Rx buffers and windows. + */ + typedef struct xWIN_PROPS + { + /* Properties of the Tx buffer and Tx window. */ + int32_t lTxBufSize; /**< Unit: bytes. */ + int32_t lTxWinSize; /**< Unit: MSS. */ + + /* Properties of the Rx buffer and Rx window. */ + int32_t lRxBufSize; /**< Unit: bytes. */ + int32_t lRxWinSize; /**< Unit: MSS. */ + } WinProperties_t; + +/** + * Structure to pass for the 'FREERTOS_SO_SET_LOW_HIGH_WATER' option. + */ + typedef struct xLOW_HIGH_WATER + { + size_t uxLittleSpace; /**< Send a STOP when buffer space drops below X bytes */ + size_t uxEnoughSpace; /**< Send a GO when buffer space grows above X bytes */ + } LowHighWater_t; + +/* Connect a TCP socket to a remote socket. */ + BaseType_t FreeRTOS_connect( Socket_t xClientSocket, + const struct freertos_sockaddr * pxAddress, + socklen_t xAddressLength ); + +/* Places a TCP socket into a state where it is listening for and can accept + * incoming connection requests from remote sockets. */ + BaseType_t FreeRTOS_listen( Socket_t xSocket, + BaseType_t xBacklog ); + +/* Accept a connection on a TCP socket. */ + Socket_t FreeRTOS_accept( Socket_t xServerSocket, + struct freertos_sockaddr * pxAddress, + socklen_t * pxAddressLength ); + +/* Send data to a TCP socket. */ + BaseType_t FreeRTOS_send( Socket_t xSocket, + const void * pvBuffer, + size_t uxDataLength, + BaseType_t xFlags ); + +/* Receive data from a TCP socket */ + BaseType_t FreeRTOS_recv( Socket_t xSocket, + void * pvBuffer, + size_t uxBufferLength, + BaseType_t xFlags ); + +/* Disable reads and writes on a connected TCP socket. */ + BaseType_t FreeRTOS_shutdown( Socket_t xSocket, + BaseType_t xHow ); + + #if ( ipconfigUSE_TCP == 1 ) + +/* Release a TCP payload buffer that was obtained by + * calling FreeRTOS_recv() with the FREERTOS_ZERO_COPY flag, + * and a pointer to a void pointer. */ + BaseType_t FreeRTOS_ReleaseTCPPayloadBuffer( Socket_t xSocket, + void const * pvBuffer, + BaseType_t xByteCount ); + #endif /* ( ipconfigUSE_TCP == 1 ) */ + +/* Returns the number of bytes available in the Rx buffer. */ + BaseType_t FreeRTOS_rx_size( ConstSocket_t xSocket ); + + #define FreeRTOS_recvcount( xSocket ) FreeRTOS_rx_size( xSocket ) + +/* Returns the free space in the Tx buffer. */ + BaseType_t FreeRTOS_tx_space( ConstSocket_t xSocket ); + + #define FreeRTOS_outstanding( xSocket ) FreeRTOS_tx_size( xSocket ) + +/* Returns the number of bytes stored in the Tx buffer. */ + BaseType_t FreeRTOS_tx_size( ConstSocket_t xSocket ); + +/* Returns pdTRUE if TCP socket is connected. */ + BaseType_t FreeRTOS_issocketconnected( ConstSocket_t xSocket ); + +/* Return the remote address and IP port of a connected TCP Socket. */ + BaseType_t FreeRTOS_GetRemoteAddress( ConstSocket_t xSocket, + struct freertos_sockaddr * pxAddress ); + +/* Returns the number of bytes that may be added to txStream. */ + BaseType_t FreeRTOS_maywrite( ConstSocket_t xSocket ); + +/* Returns the actual size of MSS being used. */ + BaseType_t FreeRTOS_mss( ConstSocket_t xSocket ); + +/* For internal use only: return the connection status. */ + BaseType_t FreeRTOS_connstatus( ConstSocket_t xSocket ); + +/* For advanced applications only: + * Get a direct pointer to the circular transmit buffer. + * '*pxLength' will contain the number of bytes that may be written. */ + uint8_t * FreeRTOS_get_tx_head( ConstSocket_t xSocket, + BaseType_t * pxLength ); + +/* For the web server: borrow the circular Rx buffer for inspection + * HTML driver wants to see if a sequence of 13/10/13/10 is available. */ + const struct xSTREAM_BUFFER * FreeRTOS_get_rx_buf( ConstSocket_t xSocket ); + + void FreeRTOS_netstat( void ); + + +/* End TCP Socket Attributes. */ + + #endif /* ( ipconfigUSE_TCP == 1 ) */ + + #if ( ipconfigUSE_CALLBACKS == 1 ) + +/* + * Callback handlers for a socket + * User-provided functions will be called for each sockopt callback defined + * For example: + * static void xOnTCPConnect( Socket_t xSocket, BaseType_t ulConnected ) {} + * static BaseType_t xOnTCPReceive( Socket_t xSocket, void * pData, size_t uxLength ) + * { + * // handle the message + * return pdFALSE; // Not Used + * } + * static void xOnTCPSent( Socket_t xSocket, size_t xLength ) {} + * static BaseType_t xOnUDPReceive( Socket_t xSocket, void * pData, size_t xLength, const struct freertos_sockaddr * pxFrom, const struct freertos_sockaddr * pxDest ) + * { + * // handle the message + * return pdTRUE; // message processing is finished, don't store + * } + * static void xOnUDPSent( Socket_t xSocket, size_t xLength ) {} + * F_TCP_UDP_Handler_t xHand = { xOnTCPConnect, xOnTCPReceive, xOnTCPSent, xOnUDPReceive, xOnUDPSent }; + * FreeRTOS_setsockopt( sock, 0, FREERTOS_SO_TCP_CONN_HANDLER, ( void * ) &xHand, 0 ); + */ + +/* Connected callback handler for a TCP Socket. */ + typedef void (* FOnConnected_t )( Socket_t xSocket, + BaseType_t ulConnected ); + +/* Received callback handler for a TCP Socket. + * Return value is not currently used. */ + typedef BaseType_t (* FOnTCPReceive_t )( Socket_t xSocket, + void * pData, + size_t xLength ); + +/* Sent callback handler for a TCP Socket. */ + typedef void (* FOnTCPSent_t )( Socket_t xSocket, + size_t xLength ); + +/* Received callback handler for a UDP Socket. + * If a positive number is returned, the messages will not be stored in + * xWaitingPacketsList for later processing by recvfrom(). */ + typedef BaseType_t (* FOnUDPReceive_t ) ( Socket_t xSocket, + void * pData, + size_t xLength, + const struct freertos_sockaddr * pxFrom, + const struct freertos_sockaddr * pxDest ); + +/* Sent callback handler for a UDP Socket */ + typedef void (* FOnUDPSent_t )( Socket_t xSocket, + size_t xLength ); + +/* The following values are used in the lOptionName parameter of setsockopt() + * to set the callback handlers options. */ + typedef struct xTCP_UDP_HANDLER + { + FOnConnected_t pxOnTCPConnected; /* FREERTOS_SO_TCP_CONN_HANDLER */ + FOnTCPReceive_t pxOnTCPReceive; /* FREERTOS_SO_TCP_RECV_HANDLER */ + FOnTCPSent_t pxOnTCPSent; /* FREERTOS_SO_TCP_SENT_HANDLER */ + FOnUDPReceive_t pxOnUDPReceive; /* FREERTOS_SO_UDP_RECV_HANDLER */ + FOnUDPSent_t pxOnUDPSent; /* FREERTOS_SO_UDP_SENT_HANDLER */ + } F_TCP_UDP_Handler_t; + + #endif /* ( ipconfigUSE_CALLBACKS == 1 ) */ + +/* Conversion Functions */ + +/* Converts an IP address expressed as a 32-bit number in network byte order + * to a string in decimal dot notation. */ + extern const char * FreeRTOS_inet_ntoa( uint32_t ulIPAddress, + char * pcBuffer ); + + #if ( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) + +/* Converts an IP address expressed as four separate numeric octets into an + * IP address expressed as a 32-bit number in network byte order */ + #define FreeRTOS_inet_addr_quick( ucOctet0, ucOctet1, ucOctet2, ucOctet3 ) \ + ( ( ( ( uint32_t ) ( ucOctet3 ) ) << 24UL ) | \ + ( ( ( uint32_t ) ( ucOctet2 ) ) << 16UL ) | \ + ( ( ( uint32_t ) ( ucOctet1 ) ) << 8UL ) | \ + ( ( uint32_t ) ( ucOctet0 ) ) ) + + #else /* ( ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN ) */ + + #define FreeRTOS_inet_addr_quick( ucOctet0, ucOctet1, ucOctet2, ucOctet3 ) \ + ( ( ( ( uint32_t ) ( ucOctet0 ) ) << 24UL ) | \ + ( ( ( uint32_t ) ( ucOctet1 ) ) << 16UL ) | \ + ( ( ( uint32_t ) ( ucOctet2 ) ) << 8UL ) | \ + ( ( uint32_t ) ( ucOctet3 ) ) ) + + #endif /* ( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) */ + +/* Convert a null-terminated string in dot-decimal-notation (d.d.d.d) + * to a 32-bit unsigned integer. */ + uint32_t FreeRTOS_inet_addr( const char * pcIPAddress ); + + BaseType_t FreeRTOS_inet_pton( BaseType_t xAddressFamily, + const char * pcSource, + void * pvDestination ); + + const char * FreeRTOS_inet_ntop( BaseType_t xAddressFamily, + const void * pvSource, + char * pcDestination, + socklen_t uxSize ); + + BaseType_t FreeRTOS_inet_pton4( const char * pcSource, + void * pvDestination ); + + const char * FreeRTOS_inet_ntop4( const void * pvSource, + char * pcDestination, + socklen_t uxSize ); + +/** @brief This function converts a human readable string, representing an 48-bit MAC address, + * into a 6-byte address. Valid inputs are e.g. "62:48:5:83:A0:b2" and "0-12-34-fe-dc-ba". */ + BaseType_t FreeRTOS_EUI48_pton( const char * pcSource, + uint8_t * pucTarget ); + +/** @brief This function converts a 48-bit MAC address to a human readable string. */ + void FreeRTOS_EUI48_ntop( const uint8_t * pucSource, + char * pcTarget, + char cTen, + char cSeparator ); + +/* End Conversion Functions */ + + #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + +/* The SocketSet_t type is the equivalent to the fd_set type used by the + * Berkeley API. */ + struct xSOCKET_SET; + typedef struct xSOCKET_SET * SocketSet_t; + typedef struct xSOCKET_SET const * ConstSocketSet_t; + +/* Create a socket set for use with the FreeRTOS_select() function */ + SocketSet_t FreeRTOS_CreateSocketSet( void ); + + void FreeRTOS_DeleteSocketSet( SocketSet_t xSocketSet ); + +/* Block on a "socket set" until an event of interest occurs on a + * socket within the set. */ + BaseType_t FreeRTOS_select( SocketSet_t xSocketSet, + TickType_t xBlockTimeTicks ); + +/* For FD_SET and FD_CLR, a combination of the following bits can be used: */ + typedef enum eSELECT_EVENT + { + eSELECT_READ = 0x0001, + eSELECT_WRITE = 0x0002, + eSELECT_EXCEPT = 0x0004, + eSELECT_INTR = 0x0008, + eSELECT_ALL = 0x000F, + /* Reserved for internal use: */ + eSELECT_CALL_IP = 0x0010, + /* end */ + } eSelectEvent_t; + +/* Add a socket to a socket set, and set the event bits of interest + * for the added socket. */ + void FreeRTOS_FD_SET( Socket_t xSocket, + SocketSet_t xSocketSet, + EventBits_t xBitsToSet ); + +/* Clear a set event bit of interest for a socket of the socket set. + * If all the event bits are clear then the socket will be removed + * from the socket set. */ + void FreeRTOS_FD_CLR( Socket_t xSocket, + SocketSet_t xSocketSet, + EventBits_t xBitsToClear ); + +/* Check if a socket in a socket set has an event bit set. */ + EventBits_t FreeRTOS_FD_ISSET( const ConstSocket_t xSocket, + const ConstSocketSet_t xSocketSet ); + + #endif /* ( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) */ + + #ifdef __cplusplus + } /* extern "C" */ + #endif + +#endif /* FREERTOS_SOCKETS_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_Stream_Buffer.h b/FreeRTOS/source/include/FreeRTOS_Stream_Buffer.h new file mode 100644 index 0000000..4f86678 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_Stream_Buffer.h @@ -0,0 +1,131 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* + * FreeRTOS_Stream_Buffer.h + * + * A circular character buffer + * An implementation of a circular buffer without a length field + * If LENGTH defines the size of the buffer, a maximum of (LENGTH-1) bytes can be stored + * In order to add or read data from the buffer, memcpy() will be called at most 2 times + */ + +#ifndef FREERTOS_STREAM_BUFFER_H +#define FREERTOS_STREAM_BUFFER_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/** + * structure to store all the details of a stream buffer. + */ +typedef struct xSTREAM_BUFFER +{ + volatile size_t uxTail; /**< next item to read */ + volatile size_t uxMid; /**< iterator within the valid items */ + volatile size_t uxHead; /**< next position store a new item */ + volatile size_t uxFront; /**< iterator within the free space */ + size_t LENGTH; /**< const value: number of reserved elements */ + uint8_t ucArray[ sizeof( size_t ) ]; /**< array big enough to store any pointer address */ +} StreamBuffer_t; + +void vStreamBufferClear( StreamBuffer_t * pxBuffer ); +/*-----------------------------------------------------------*/ + +size_t uxStreamBufferSpace( const StreamBuffer_t * pxBuffer, + const size_t uxLower, + const size_t uxUpper ); +/*-----------------------------------------------------------*/ + +size_t uxStreamBufferDistance( const StreamBuffer_t * pxBuffer, + const size_t uxLower, + const size_t uxUpper ); +/*-----------------------------------------------------------*/ + +size_t uxStreamBufferGetSpace( const StreamBuffer_t * pxBuffer ); +/*-----------------------------------------------------------*/ + +size_t uxStreamBufferFrontSpace( const StreamBuffer_t * pxBuffer ); +/*-----------------------------------------------------------*/ + +size_t uxStreamBufferGetSize( const StreamBuffer_t * pxBuffer ); +/*-----------------------------------------------------------*/ + +size_t uxStreamBufferMidSpace( const StreamBuffer_t * pxBuffer ); +/*-----------------------------------------------------------*/ + +void vStreamBufferMoveMid( StreamBuffer_t * pxBuffer, + size_t uxCount ); +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferLessThenEqual( const StreamBuffer_t * pxBuffer, + const size_t uxLeft, + const size_t uxRight ); +/*-----------------------------------------------------------*/ + +size_t uxStreamBufferGetPtr( StreamBuffer_t * pxBuffer, + uint8_t ** ppucData ); + +/* + * Add bytes to a stream buffer. + * + * pxBuffer - The buffer to which the bytes will be added. + * uxOffset - If uxOffset > 0, data will be written at an offset from uxHead + * while uxHead will not be moved yet. + * pucData - A pointer to the data to be added. + * uxCount - The number of bytes to add. + */ +size_t uxStreamBufferAdd( StreamBuffer_t * pxBuffer, + size_t uxOffset, + const uint8_t * pucData, + size_t uxByteCount ); + +/* + * Read bytes from a stream buffer. + * + * pxBuffer - The buffer from which the bytes will be read. + * uxOffset - Can be used to read data located at a certain offset from 'uxTail'. + * pucData - A pointer to the buffer into which data will be read. + * uxMaxCount - The number of bytes to read. + * xPeek - If set to pdTRUE the data will remain in the buffer. + */ +size_t uxStreamBufferGet( StreamBuffer_t * pxBuffer, + size_t uxOffset, + uint8_t * pucData, + size_t uxMaxCount, + BaseType_t xPeek ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* !defined( FREERTOS_STREAM_BUFFER_H ) */ diff --git a/FreeRTOS/source/include/FreeRTOS_TCP_IP.h b/FreeRTOS/source/include/FreeRTOS_TCP_IP.h new file mode 100644 index 0000000..12b6000 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_TCP_IP.h @@ -0,0 +1,175 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_TCP_IP_H +#define FREERTOS_TCP_IP_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +BaseType_t xProcessReceivedTCPPacket( NetworkBufferDescriptor_t * pxDescriptor ); + +typedef enum eTCP_STATE +{ + /* Comments about the TCP states are borrowed from the very useful + * Wiki page: + * http://en.wikipedia.org/wiki/Transmission_Control_Protocol */ + eCLOSED = 0U, /* 0 (server + client) no connection state at all. */ + eTCP_LISTEN, /* 1 (server) waiting for a connection request + * from any remote TCP and port. */ + eCONNECT_SYN, /* 2 (client) internal state: socket wants to send + * a connect */ + eSYN_FIRST, /* 3 (server) Just created, must ACK the SYN request. */ + eSYN_RECEIVED, /* 4 (server) waiting for a confirming connection request + * acknowledgement after having both received and sent a connection request. */ + eESTABLISHED, /* 5 (server + client) an open connection, data received can be + * delivered to the user. The normal state for the data transfer phase of the connection. */ + eFIN_WAIT_1, /* 6 (server + client) waiting for a connection termination request from the remote TCP, + * or an acknowledgement of the connection termination request previously sent. */ + eFIN_WAIT_2, /* 7 (server + client) waiting for a connection termination request from the remote TCP. */ + eCLOSE_WAIT, /* 8 (server + client) waiting for a connection termination request from the local user. */ + eCLOSING, /* 9 (server + client) waiting for a connection termination request acknowledgement from the remote TCP. */ + eLAST_ACK, /*10 (server + client) waiting for an acknowledgement of the connection termination request + * previously sent to the remote TCP + * (which includes an acknowledgement of its connection termination request). */ + eTIME_WAIT, /*11 (either server or client) waiting for enough time to pass to be sure the remote TCP received the + * acknowledgement of its connection termination request. [According to RFC 793 a connection can + * stay in TIME-WAIT for a maximum of four minutes known as a MSL (maximum segment lifetime).] */ +} eIPTCPState_t; + +/* + * The meaning of the TCP flags: + */ +#define tcpTCP_FLAG_FIN ( ( uint8_t ) 0x01U ) /**< No more data from sender. */ +#define tcpTCP_FLAG_SYN ( ( uint8_t ) 0x02U ) /**< Synchronize sequence numbers. */ +#define tcpTCP_FLAG_RST ( ( uint8_t ) 0x04U ) /**< Reset the connection. */ +#define tcpTCP_FLAG_PSH ( ( uint8_t ) 0x08U ) /**< Push function: please push buffered data to the recv application. */ +#define tcpTCP_FLAG_ACK ( ( uint8_t ) 0x10U ) /**< Acknowledgment field is significant. */ +#define tcpTCP_FLAG_URG ( ( uint8_t ) 0x20U ) /**< Urgent pointer field is significant. */ +#define tcpTCP_FLAG_ECN ( ( uint8_t ) 0x40U ) /**< ECN-Echo. */ +#define tcpTCP_FLAG_CWR ( ( uint8_t ) 0x80U ) /**< Congestion Window Reduced. */ + +#define tcpTCP_FLAG_CTRL ( ( uint8_t ) 0x1FU ) /**< A mask to filter all protocol flags. */ + + +/* + * A few values of the TCP options: + */ +#define tcpTCP_OPT_END 0U /**< End of TCP options list. */ +#define tcpTCP_OPT_NOOP 1U /**< "No-operation" TCP option. */ +#define tcpTCP_OPT_MSS 2U /**< Maximum segment size TCP option. */ +#define tcpTCP_OPT_WSOPT 3U /**< TCP Window Scale Option (3-byte long). */ +#define tcpTCP_OPT_SACK_P 4U /**< Advertise that SACK is permitted. */ +#define tcpTCP_OPT_SACK_A 5U /**< SACK option with first/last. */ +#define tcpTCP_OPT_TIMESTAMP 8U /**< Time-stamp option. */ + + +#define tcpTCP_OPT_MSS_LEN 4U /**< Length of TCP MSS option. */ +#define tcpTCP_OPT_WSOPT_LEN 3U /**< Length of TCP WSOPT option. */ + +#define tcpTCP_OPT_TIMESTAMP_LEN 10 /**< fixed length of the time-stamp option. */ + +/** @brief + * Minimum segment length as outlined by RFC 791 section 3.1. + * Minimum segment length ( 536 ) = Minimum MTU ( 576 ) - IP Header ( 20 ) - TCP Header ( 20 ). + */ +#define tcpMINIMUM_SEGMENT_LENGTH 536U + +/** @brief + * The macro tcpNOW_CONNECTED() is use to determine if the connection makes a + * transition from connected to non-connected and vice versa. + * tcpNOW_CONNECTED() returns true when the status has one of these values: + * eESTABLISHED, eFIN_WAIT_1, eFIN_WAIT_2, eCLOSING, eLAST_ACK, eTIME_WAIT + * Technically the connection status is closed earlier, but the library wants + * to prevent that the socket will be deleted before the last ACK has been + * and thus causing a 'RST' packet on either side. + */ +#define tcpNOW_CONNECTED( status ) \ + ( ( ( ( status ) >= ( BaseType_t ) eESTABLISHED ) && ( ( status ) != ( BaseType_t ) eCLOSE_WAIT ) ) ? 1 : 0 ) + +/** @brief + * The highest 4 bits in the TCP offset byte indicate the total length of the + * TCP header, divided by 4. + */ +#define tcpVALID_BITS_IN_TCP_OFFSET_BYTE ( 0xF0U ) + +/* + * Acknowledgements to TCP data packets may be delayed as long as more is being expected. + * A normal delay would be 200ms. Here a much shorter delay of 20 ms is being used to + * gain performance. + */ +#define tcpDELAYED_ACK_SHORT_DELAY_MS ( 2 ) /**< Should not become smaller than 1. */ +#define tcpDELAYED_ACK_LONGER_DELAY_MS ( 20 ) /**< Longer delay for ACK. */ + + +/** @brief + * The MSS (Maximum Segment Size) will be taken as large as possible. However, packets with + * an MSS of 1460 bytes won't be transported through the internet. The MSS will be reduced + * to 1400 bytes. + */ +#define tcpREDUCED_MSS_THROUGH_INTERNET ( 1400 ) + +/** @brief + * When there are no TCP options, the TCP offset equals 20 bytes, which is stored as + * the number 5 (words) in the higher nibble of the TCP-offset byte. + */ +#define tcpTCP_OFFSET_LENGTH_BITS ( 0xf0U ) +#define tcpTCP_OFFSET_STANDARD_LENGTH ( 0x50U ) /**< Standard TCP packet offset. */ + + +/** @brief + * Each TCP socket is checked regularly to see if it can send data packets. + * By default, the maximum number of packets sent during one check is limited to 8. + * This amount may be further limited by setting the socket's TX window size. + */ +#if ( !defined( SEND_REPEATED_COUNT ) ) + #define SEND_REPEATED_COUNT ( 8 ) +#endif /* !defined( SEND_REPEATED_COUNT ) */ + +/** @brief + * Define a maximum period of time (ms) to leave a TCP-socket unattended. + * When a TCP timer expires, retries and keep-alive messages will be checked. + */ +#ifndef tcpMAXIMUM_TCP_WAKEUP_TIME_MS + #define tcpMAXIMUM_TCP_WAKEUP_TIME_MS 20000U +#endif + +/* Two macro's that were introduced to work with both IPv4 and IPv6. */ +#define xIPHeaderSize( pxNetworkBuffer ) ( ipSIZE_OF_IPv4_HEADER ) /**< Size of IP Header. */ +#define uxIPHeaderSizeSocket( pxSocket ) ( ipSIZE_OF_IPv4_HEADER ) /**< Size of IP Header socket. */ + + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_TCP_IP_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_TCP_Reception.h b/FreeRTOS/source/include/FreeRTOS_TCP_Reception.h new file mode 100644 index 0000000..ad8a5d0 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_TCP_Reception.h @@ -0,0 +1,66 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_TCP_RECEPTION_H +#define FREERTOS_TCP_RECEPTION_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* + * Called from xProcessReceivedTCPPacket. Parse the TCP option(s) received, + * if present. This function returns pdFALSE if the options are not well formed. + */ +BaseType_t prvCheckOptions( FreeRTOS_Socket_t * pxSocket, + const NetworkBufferDescriptor_t * pxNetworkBuffer ); + +/* + * Called from prvTCPHandleState(). Find the TCP payload data and check and + * return its length. + */ +BaseType_t prvCheckRxData( const NetworkBufferDescriptor_t * pxNetworkBuffer, + uint8_t ** ppucRecvData ); + +/* + * Called from prvTCPHandleState(). Check if the payload data may be accepted. + * If so, it will be added to the socket's reception queue. + */ +BaseType_t prvStoreRxData( FreeRTOS_Socket_t * pxSocket, + const uint8_t * pucRecvData, + NetworkBufferDescriptor_t * pxNetworkBuffer, + uint32_t ulReceiveLength ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_TCP_RECEPTION_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_TCP_State_Handling.h b/FreeRTOS/source/include/FreeRTOS_TCP_State_Handling.h new file mode 100644 index 0000000..ca567eb --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_TCP_State_Handling.h @@ -0,0 +1,74 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_TCP_STATE_HANDLING_H +#define FREERTOS_TCP_STATE_HANDLING_H + +#include "FreeRTOS_TCP_IP.h" + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* + * Returns true if the socket must be checked. Non-active sockets are waiting + * for user action, either connect() or close(). + */ +BaseType_t prvTCPSocketIsActive( eIPTCPState_t eStatus ); + +/* + * prvTCPStatusAgeCheck() will see if the socket has been in a non-connected + * state for too long. If so, the socket will be closed, and -1 will be + * returned. + */ +#if ( ipconfigTCP_HANG_PROTECTION == 1 ) + BaseType_t prvTCPStatusAgeCheck( FreeRTOS_Socket_t * pxSocket ); +#endif + +/* + * The heart of all: check incoming packet for valid data and acks and do what + * is necessary in each state. + */ +BaseType_t prvTCPHandleState( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer ); + +/* + * Return either a newly created socket, or the current socket in a connected + * state (depends on the 'bReuseSocket' flag). + */ +FreeRTOS_Socket_t * prvHandleListen( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t * pxNetworkBuffer ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_TCP_STATE_HANDLING_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_TCP_Transmission.h b/FreeRTOS/source/include/FreeRTOS_TCP_Transmission.h new file mode 100644 index 0000000..0685a8e --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_TCP_Transmission.h @@ -0,0 +1,124 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_TCP_TRANSMISSION_H +#define FREERTOS_TCP_TRANSMISSION_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* + * Either sends a SYN or calls prvTCPSendRepeated (for regular messages). + */ +int32_t prvTCPSendPacket( FreeRTOS_Socket_t * pxSocket ); + +/* + * Try to send a series of messages. + */ +int32_t prvTCPSendRepeated( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer ); + +/* + * Return or send a packet to the other party. + */ +void prvTCPReturnPacket( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t * pxDescriptor, + uint32_t ulLen, + BaseType_t xReleaseAfterSend ); + +/* + * Initialise the data structures which keep track of the TCP windowing system. + */ +void prvTCPCreateWindow( FreeRTOS_Socket_t * pxSocket ); + +/* + * Set the initial properties in the options fields, like the preferred + * value of MSS and whether SACK allowed. Will be transmitted in the state + * 'eCONNECT_SYN'. + */ +UBaseType_t prvSetSynAckOptions( FreeRTOS_Socket_t * pxSocket, + TCPHeader_t * pxTCPHeader ); + +/* + * Prepare an outgoing message, if anything has to be sent. + */ +int32_t prvTCPPrepareSend( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer, + UBaseType_t uxOptionsLength ); + +/* + * The API FreeRTOS_send() adds data to the TX stream. Add + * this data to the windowing system to it can be transmitted. + */ +void prvTCPAddTxData( FreeRTOS_Socket_t * pxSocket ); + +/* + * Set the TCP options (if any) for the outgoing packet. + */ +UBaseType_t prvSetOptions( FreeRTOS_Socket_t * pxSocket, + const NetworkBufferDescriptor_t * pxNetworkBuffer ); + +/* + * Called from prvTCPHandleState(). There is data to be sent. + * If ipconfigUSE_TCP_WIN is defined, and if only an ACK must be sent, it will + * be checked if it would better be postponed for efficiency. + */ +BaseType_t prvSendData( FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t ** ppxNetworkBuffer, + uint32_t ulReceiveLength, + BaseType_t xByteCount ); + +/* + * A "challenge ACK" is as per https://tools.ietf.org/html/rfc5961#section-3.2, + * case #3. In summary, an RST was received with a sequence number that is + * unexpected but still within the window. + */ +BaseType_t prvTCPSendChallengeAck( NetworkBufferDescriptor_t * pxNetworkBuffer ); + +/* + * Reply to a peer with the RST flag on, in case a packet can not be handled. + */ +BaseType_t prvTCPSendReset( NetworkBufferDescriptor_t * pxNetworkBuffer ); + +/* + * Check if the size of a network buffer is big enough to hold the outgoing message. + * Allocate a new bigger network buffer when necessary. + */ +NetworkBufferDescriptor_t * prvTCPBufferResize( const FreeRTOS_Socket_t * pxSocket, + NetworkBufferDescriptor_t * pxNetworkBuffer, + int32_t lDataLen, + UBaseType_t uxOptionsLength ); +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_TCP_TRANSMISSION_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_TCP_Utils.h b/FreeRTOS/source/include/FreeRTOS_TCP_Utils.h new file mode 100644 index 0000000..0b9cdd2 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_TCP_Utils.h @@ -0,0 +1,56 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_TCP_UTILS_H +#define FREERTOS_TCP_UTILS_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + + +/* + * For logging and debugging: make a string showing the TCP flags. + */ +#if ( ipconfigHAS_DEBUG_PRINTF != 0 ) + const char * prvTCPFlagMeaning( UBaseType_t xFlags ); +#endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ + +/* + * Set the initial value for MSS (Maximum Segment Size) to be used. + */ +void prvSocketSetMSS( FreeRTOS_Socket_t * pxSocket ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_TCP_UTILS_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_TCP_WIN.h b/FreeRTOS/source/include/FreeRTOS_TCP_WIN.h new file mode 100644 index 0000000..264e608 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_TCP_WIN.h @@ -0,0 +1,258 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* + * FreeRTOS_TCP_WIN.c + * Module which handles the TCP windowing schemes for FreeRTOS-PLUS-TCP + */ + +#ifndef FREERTOS_TCP_WIN_H +#define FREERTOS_TCP_WIN_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/** @brief A very simple timer that registers the time that a packet was sent. It is used to trigger re-sending. */ +typedef struct xTCPTimerStruct +{ + TickType_t uxBorn; /**< The time at which a packet was sent ( using xTaskGetTickCount() ). */ +} TCPTimer_t; + +/** @brief This struct collects the properties of a TCP segment. A segment is a chunk of data which + * is sent in a single TCP packet, at most 1460 bytes. */ +typedef struct xTCP_SEGMENT +{ + uint32_t ulSequenceNumber; /**< The sequence number of the first byte in this packet */ + int32_t lMaxLength; /**< Maximum space, number of bytes which can be stored in this segment */ + int32_t lDataLength; /**< Actual number of bytes */ + int32_t lStreamPos; /**< reference to the [t|r]xStream of the socket */ + TCPTimer_t xTransmitTimer; /**< saves a timestamp at the moment this segment gets transmitted (TX only) */ + union + { + struct + { + uint32_t + ucTransmitCount : 8, /**< Number of times the segment has been transmitted, used to calculate the RTT */ + ucDupAckCount : 8, /**< Counts the number of times that a higher segment was ACK'd. After 3 times a Fast Retransmission takes place */ + bOutstanding : 1, /**< It the peer's turn, we're just waiting for an ACK */ + bAcked : 1, /**< This segment has been acknowledged */ + bIsForRx : 1; /**< pdTRUE if segment is used for reception */ + } bits; + uint32_t ulFlags; + } u; /**< A collection of boolean flags. */ + #if ( ipconfigUSE_TCP_WIN != 0 ) + struct xLIST_ITEM xQueueItem; /**< TX only: segments can be linked in one of three queues: xPriorityQueue, xTxQueue, and xWaitQueue */ + struct xLIST_ITEM xSegmentItem; /**< With this item the segment can be connected to a list, depending on who is owning it */ + #endif +} TCPSegment_t; + +/** @brief This struct describes the windows sizes, both for incoming and outgoing. */ +typedef struct xTCP_WINSIZE +{ + uint32_t ulRxWindowLength; /**< The TCP window size of the incoming stream. */ + uint32_t ulTxWindowLength; /**< The TCP window size of the outgoing stream. */ +} TCPWinSize_t; + +/** @brief If TCP time-stamps are being used, they will occupy 12 bytes in + * each packet, and thus the message space will become smaller. + * Keep this as a multiple of 4 */ +#if ( ipconfigUSE_TCP_WIN == 1 ) + #define ipSIZE_TCP_OPTIONS 16U +#else + #define ipSIZE_TCP_OPTIONS 12U +#endif + +/** @brief Every TCP connection owns a TCP window for the administration of all packets + * It owns two sets of segment descriptors, incoming and outgoing + */ +typedef struct xTCP_WINDOW +{ + union + { + struct + { + uint32_t + bHasInit : 1, /**< The window structure has been initialised */ + bSendFullSize : 1, /**< May only send packets with a size equal to MSS (for optimisation) */ + bTimeStamps : 1; /**< Socket is supposed to use TCP time-stamps. This depends on the */ + } bits; /**< party which opens the connection */ + uint32_t ulFlags; + } u; /**< A collection of boolean flags. */ + TCPWinSize_t xSize; /**< The TCP window sizes of the incoming and outgoing streams. */ + struct + { + uint32_t ulFirstSequenceNumber; /**< Logging & debug: the first segment received/sent in this connection + * for Tx: initial send sequence number (ISS) + * for Rx: initial receive sequence number (IRS) */ + uint32_t ulCurrentSequenceNumber; /**< Tx/Rx: the oldest sequence number not yet confirmed, also SND.UNA / RCV.NXT + * In other words: the sequence number of the left side of the sliding window */ + uint32_t ulFINSequenceNumber; /**< The sequence number which carried the FIN flag */ + uint32_t ulHighestSequenceNumber; /**< Sequence number of the right-most byte + 1 */ + } rx, /**< Sequence number of the incoming data stream. */ + tx; /**< Sequence number of the outgoing data stream. */ + uint32_t ulOurSequenceNumber; /**< The SEQ number we're sending out */ + uint32_t ulUserDataLength; /**< Number of bytes in Rx buffer which may be passed to the user, after having received a 'missing packet' */ + uint32_t ulNextTxSequenceNumber; /**< The sequence number given to the next byte to be added for transmission */ + int32_t lSRTT; /**< Smoothed Round Trip Time, it may increment quickly and it decrements slower */ + uint8_t ucOptionLength; /**< Number of valid bytes in ulOptionsData[] */ + #if ( ipconfigUSE_TCP_WIN == 1 ) + List_t xPriorityQueue; /**< Priority queue: segments which must be sent immediately */ + List_t xTxQueue; /**< Transmit queue: segments queued for transmission */ + List_t xWaitQueue; /**< Waiting queue: outstanding segments */ + TCPSegment_t * pxHeadSegment; /**< points to a segment which has not been transmitted and it's size is still growing (user data being added) */ + uint32_t ulOptionsData[ ipSIZE_TCP_OPTIONS / sizeof( uint32_t ) ]; /**< Contains the options we send out */ + List_t xTxSegments; /**< A linked list of all transmission segments, sorted on sequence number */ + List_t xRxSegments; /**< A linked list of reception segments, order depends on sequence of arrival */ + #else + /* For tiny TCP, there is only 1 outstanding TX segment */ + TCPSegment_t xTxSegment; /**< Priority queue */ + #endif + uint16_t usOurPortNumber; /**< Mostly for debugging/logging: our TCP port number */ + uint16_t usPeerPortNumber; /**< debugging/logging: the peer's TCP port number */ + uint16_t usMSS; /**< Current accepted MSS */ + uint16_t usMSSInit; /**< MSS as configured by the socket owner */ +} TCPWindow_t; + + +/*============================================================================= + * + * Creation and destruction + * + *=============================================================================*/ + +/* Create and initialize a window */ +void vTCPWindowCreate( TCPWindow_t * pxWindow, + uint32_t ulRxWindowLength, + uint32_t ulTxWindowLength, + uint32_t ulAckNumber, + uint32_t ulSequenceNumber, + uint32_t ulMSS ); + +/* Destroy a window (always returns NULL) + * It will free some resources: a collection of segments */ +void vTCPWindowDestroy( TCPWindow_t const * pxWindow ); + +/* Initialize a window */ +void vTCPWindowInit( TCPWindow_t * pxWindow, + uint32_t ulAckNumber, + uint32_t ulSequenceNumber, + uint32_t ulMSS ); + +/* Clean up allocated segments. Should only be called when FreeRTOS+TCP will no longer be used. */ +void vTCPSegmentCleanup( void ); + +/*============================================================================= + * + * Rx functions + * + *=============================================================================*/ + +/* if true may be passed directly to user (segment expected and window is empty) + * But pxWindow->ackno should always be used to set "BUF->ackno" */ +int32_t lTCPWindowRxCheck( TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber, + uint32_t ulLength, + uint32_t ulSpace, + uint32_t * pulSkipCount ); + +/* This function will be called as soon as a FIN is received. It will return true + * if there are no 'open' reception segments */ +BaseType_t xTCPWindowRxEmpty( const TCPWindow_t * pxWindow ); + +/*============================================================================= + * + * Tx functions + * + *=============================================================================*/ + +/* Adds data to the Tx-window */ +int32_t lTCPWindowTxAdd( TCPWindow_t * pxWindow, + uint32_t ulLength, + int32_t lPosition, + int32_t lMax ); + +/* Check data to be sent and calculate the time period we may sleep */ +BaseType_t xTCPWindowTxHasData( TCPWindow_t const * pxWindow, + uint32_t ulWindowSize, + TickType_t * pulDelay ); + +/* See if anything is left to be sent + * Function will be called when a FIN has been received. Only when the TX window is clean, + * it will return pdTRUE */ +BaseType_t xTCPWindowTxDone( const TCPWindow_t * pxWindow ); + +/* Fetches data to be sent. + * 'plPosition' will point to a location with the circular data buffer: txStream */ +uint32_t ulTCPWindowTxGet( TCPWindow_t * pxWindow, + uint32_t ulWindowSize, + int32_t * plPosition ); + +/* Receive a normal ACK */ +uint32_t ulTCPWindowTxAck( TCPWindow_t * pxWindow, + uint32_t ulSequenceNumber ); + +/* Receive a SACK option */ +uint32_t ulTCPWindowTxSack( TCPWindow_t * pxWindow, + uint32_t ulFirst, + uint32_t ulLast ); + +/** + * @brief Check if a > b, where a and b are rolling counters. + * + * @param[in] a: The value on the left-hand side. + * @param[in] b: The value on the right-hand side. + * + * @return pdTRUE if a > b, otherwise pdFALSE. + * + * @note GreaterThan is calculated as "( a - ( b + 1U ) ) < 0x80000000". + */ +BaseType_t xSequenceGreaterThan( uint32_t a, + uint32_t b ); + +/** + * @brief Check if a < b, where a and b are rolling counters. + * + * @param[in] a: The value on the left-hand side. + * @param[in] b: The value on the right-hand side. + * + * @return pdTRUE if a < b, otherwise pdFALSE. + * + * @note LessThan is implemented as "( b - ( a + 1 ) ) < 0x80000000". + */ +BaseType_t xSequenceLessThan( uint32_t a, + uint32_t b ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_TCP_WIN_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_UDP_IP.h b/FreeRTOS/source/include/FreeRTOS_UDP_IP.h new file mode 100644 index 0000000..d7546e2 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_UDP_IP.h @@ -0,0 +1,55 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_UDP_IP_H +#define FREERTOS_UDP_IP_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* Application level configuration options. */ +#include "FreeRTOSIPConfig.h" +#include "FreeRTOSIPConfigDefaults.h" +#include "IPTraceMacroDefaults.h" +#include "FreeRTOS_IP.h" + +/* + * Called when the application has generated a UDP packet to send. + */ +void vProcessGeneratedUDPPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ); + + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* FREERTOS_UDP_IP_H */ diff --git a/FreeRTOS/source/include/FreeRTOS_errno_TCP.h b/FreeRTOS/source/include/FreeRTOS_errno_TCP.h new file mode 100644 index 0000000..ce08d51 --- /dev/null +++ b/FreeRTOS/source/include/FreeRTOS_errno_TCP.h @@ -0,0 +1,98 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef FREERTOS_ERRNO_TCP +#define FREERTOS_ERRNO_TCP + +/* The following definitions will be included in the core FreeRTOS code in + * future versions of FreeRTOS - hence the 'pd' (ProjDefs) prefix - at which time + * this file will be removed. */ + +/* The following errno values are used by FreeRTOS+ components, not FreeRTOS + * itself. */ + +/* For future compatibility (see comment above), check the definitions have not + * already been made. */ +#ifndef pdFREERTOS_ERRNO_NONE + #define pdFREERTOS_ERRNO_NONE 0 /* No errors */ + #define pdFREERTOS_ERRNO_ENOENT 2 /* No such file or directory */ + #define pdFREERTOS_ERRNO_EINTR 4 /* Interrupted system call */ + #define pdFREERTOS_ERRNO_EIO 5 /* I/O error */ + #define pdFREERTOS_ERRNO_ENXIO 6 /* No such device or address */ + #define pdFREERTOS_ERRNO_EBADF 9 /* Bad file number */ + #define pdFREERTOS_ERRNO_EAGAIN 11 /* No more processes */ + #define pdFREERTOS_ERRNO_EWOULDBLOCK 11 /* Operation would block */ + #define pdFREERTOS_ERRNO_ENOMEM 12 /* Not enough memory */ + #define pdFREERTOS_ERRNO_EACCES 13 /* Permission denied */ + #define pdFREERTOS_ERRNO_EFAULT 14 /* Bad address */ + #define pdFREERTOS_ERRNO_EBUSY 16 /* Mount device busy */ + #define pdFREERTOS_ERRNO_EEXIST 17 /* File exists */ + #define pdFREERTOS_ERRNO_EXDEV 18 /* Cross-device link */ + #define pdFREERTOS_ERRNO_ENODEV 19 /* No such device */ + #define pdFREERTOS_ERRNO_ENOTDIR 20 /* Not a directory */ + #define pdFREERTOS_ERRNO_EISDIR 21 /* Is a directory */ + #define pdFREERTOS_ERRNO_EINVAL 22 /* Invalid argument */ + #define pdFREERTOS_ERRNO_ENOSPC 28 /* No space left on device */ + #define pdFREERTOS_ERRNO_ESPIPE 29 /* Illegal seek */ + #define pdFREERTOS_ERRNO_EROFS 30 /* Read only file system */ + #define pdFREERTOS_ERRNO_EUNATCH 42 /* Protocol driver not attached */ + #define pdFREERTOS_ERRNO_EBADE 50 /* Invalid exchange */ + #define pdFREERTOS_ERRNO_EFTYPE 79 /* Inappropriate file type or format */ + #define pdFREERTOS_ERRNO_ENMFILE 89 /* No more files */ + #define pdFREERTOS_ERRNO_ENOTEMPTY 90 /* Directory not empty */ + #define pdFREERTOS_ERRNO_ENAMETOOLONG 91 /* File or path name too long */ + #define pdFREERTOS_ERRNO_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ + #define pdFREERTOS_ERRNO_EAFNOSUPPORT 97 /* Address family not supported by protocol */ + #define pdFREERTOS_ERRNO_ENOBUFS 105 /* No buffer space available */ + #define pdFREERTOS_ERRNO_ENOPROTOOPT 109 /* Protocol not available */ + #define pdFREERTOS_ERRNO_EADDRINUSE 112 /* Address already in use */ + #define pdFREERTOS_ERRNO_ETIMEDOUT 116 /* Connection timed out */ + #define pdFREERTOS_ERRNO_EINPROGRESS 119 /* Connection already in progress */ + #define pdFREERTOS_ERRNO_EALREADY 120 /* Socket already connected */ + #define pdFREERTOS_ERRNO_EADDRNOTAVAIL 125 /* Address not available */ + #define pdFREERTOS_ERRNO_EISCONN 127 /* Socket is already connected */ + #define pdFREERTOS_ERRNO_ENOTCONN 128 /* Socket is not connected */ + #define pdFREERTOS_ERRNO_ENOMEDIUM 135 /* No medium inserted */ + #define pdFREERTOS_ERRNO_EILSEQ 138 /* An invalid UTF-16 sequence was encountered. */ + #define pdFREERTOS_ERRNO_ECANCELED 140 /* Operation canceled. */ + +/* The following endian values are used by FreeRTOS+ components, not FreeRTOS + * itself. */ + #define pdFREERTOS_LITTLE_ENDIAN 0 + #define pdFREERTOS_BIG_ENDIAN 1 +#else /* ifndef pdFREERTOS_ERRNO_NONE */ + #ifndef pdFREERTOS_ERRNO_EAFNOSUPPORT + #define pdFREERTOS_ERRNO_EAFNOSUPPORT 97 /* Address family not supported by protocol */ + #endif /* pdFREERTOS_ERRNO_EAFNOSUPPORT */ +#endif /* pdFREERTOS_ERRNO_NONE */ + +/* Translate a pdFREERTOS_ERRNO code to a human readable string. */ +const char * FreeRTOS_strerror_r( BaseType_t xErrnum, + char * pcBuffer, + size_t uxLength ); + +#endif /* FREERTOS_ERRNO_TCP */ diff --git a/FreeRTOS/source/include/IPTraceMacroDefaults.h b/FreeRTOS/source/include/IPTraceMacroDefaults.h new file mode 100644 index 0000000..40589d9 --- /dev/null +++ b/FreeRTOS/source/include/IPTraceMacroDefaults.h @@ -0,0 +1,277 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* This file provides default (empty) implementations for any IP trace macros + * that are not defined by the user. See + * http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/TCP_IP_Trace.html */ + +#ifndef UDP_TRACE_MACRO_DEFAULTS_H +#define UDP_TRACE_MACRO_DEFAULTS_H + +#ifndef iptraceNETWORK_DOWN + #define iptraceNETWORK_DOWN() +#endif + +#ifndef iptraceNETWORK_BUFFER_RELEASED + #define iptraceNETWORK_BUFFER_RELEASED( pxBufferAddress ) +#endif + +#ifndef iptraceNETWORK_BUFFER_OBTAINED + #define iptraceNETWORK_BUFFER_OBTAINED( pxBufferAddress ) +#endif + +#ifndef iptraceNETWORK_BUFFER_OBTAINED_FROM_ISR + #define iptraceNETWORK_BUFFER_OBTAINED_FROM_ISR( pxBufferAddress ) +#endif + +#ifndef iptraceNETWORK_INTERFACE_INPUT + /* An Ethernet packet has been received. */ + #define iptraceNETWORK_INTERFACE_INPUT( uxDataLength, pucEthernetBuffer ) +#endif + +#ifndef iptraceNETWORK_INTERFACE_OUTPUT + /* An Ethernet packet will be sent. */ + #define iptraceNETWORK_INTERFACE_OUTPUT( uxDataLength, pucEthernetBuffer ) +#endif + +#ifndef iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER + #define iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER() +#endif + +#ifndef iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER_FROM_ISR + #define iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER_FROM_ISR() +#endif + +#ifndef iptraceDROPPED_INVALID_ARP_PACKET + #define iptraceDROPPED_INVALID_ARP_PACKET( pxARPHeader ) +#endif + +#ifndef iptraceCREATING_ARP_REQUEST + #define iptraceCREATING_ARP_REQUEST( ulIPAddress ) +#endif + +/* A packet came in from an unknown IPv4 address. + * An ARP request has been sent and the network + * buffer is stored for processing later.*/ +#ifndef iptraceDELAYED_ARP_REQUEST_STARTED + #define iptraceDELAYED_ARP_REQUEST_STARTED() +#endif + +/* A packet has come in from an unknown IPv4 address. + * An ARP request has been sent, but the queue is + * still filled with a different packet. */ +#ifndef iptraceDELAYED_ARP_BUFFER_FULL + #define iptraceDELAYED_ARP_BUFFER_FULL() +#endif + +/* An ARP request has been sent, and a matching + * reply is received. Now the original + * packet will be processed by the IP-task. */ +#ifndef iptrace_DELAYED_ARP_REQUEST_REPLIED + #define iptrace_DELAYED_ARP_REQUEST_REPLIED() +#endif + +/* A packet was stored for delayed processing, but + * there is no ARP reply. The network buffer will + * be released without being processed. */ +#ifndef iptraceDELAYED_ARP_TIMER_EXPIRED + #define iptraceDELAYED_ARP_TIMER_EXPIRED() +#endif + +#ifndef iptraceARP_TABLE_ENTRY_WILL_EXPIRE + #define iptraceARP_TABLE_ENTRY_WILL_EXPIRE( ulIPAddress ) +#endif + +#ifndef iptraceARP_TABLE_ENTRY_EXPIRED + #define iptraceARP_TABLE_ENTRY_EXPIRED( ulIPAddress ) +#endif + +#ifndef iptraceARP_TABLE_ENTRY_CREATED + #define iptraceARP_TABLE_ENTRY_CREATED( ulIPAddress, ucMACAddress ) +#endif + +#ifndef iptraceSENDING_UDP_PACKET + #define iptraceSENDING_UDP_PACKET( ulIPAddress ) +#endif + +#ifndef iptracePACKET_DROPPED_TO_GENERATE_ARP + #define iptracePACKET_DROPPED_TO_GENERATE_ARP( ulIPAddress ) +#endif + +#ifndef iptraceICMP_PACKET_RECEIVED + #define iptraceICMP_PACKET_RECEIVED() +#endif + +#ifndef iptraceSENDING_PING_REPLY + #define iptraceSENDING_PING_REPLY( ulIPAddress ) +#endif + +#ifndef traceARP_PACKET_RECEIVED + #define traceARP_PACKET_RECEIVED() +#endif + +#ifndef iptracePROCESSING_RECEIVED_ARP_REPLY + #define iptracePROCESSING_RECEIVED_ARP_REPLY( ulIPAddress ) +#endif + +#ifndef iptraceSENDING_ARP_REPLY + #define iptraceSENDING_ARP_REPLY( ulIPAddress ) +#endif + +#ifndef iptraceFAILED_TO_CREATE_SOCKET + #define iptraceFAILED_TO_CREATE_SOCKET() +#endif + +#ifndef iptraceFAILED_TO_CREATE_EVENT_GROUP + #define iptraceFAILED_TO_CREATE_EVENT_GROUP() +#endif + +#ifndef iptraceRECVFROM_DISCARDING_BYTES + #define iptraceRECVFROM_DISCARDING_BYTES( xNumberOfBytesDiscarded ) +#endif + +#ifndef iptraceETHERNET_RX_EVENT_LOST + #define iptraceETHERNET_RX_EVENT_LOST() +#endif + +#ifndef iptraceSTACK_TX_EVENT_LOST + #define iptraceSTACK_TX_EVENT_LOST( xEvent ) +#endif + +#ifndef iptraceNETWORK_EVENT_RECEIVED + #define iptraceNETWORK_EVENT_RECEIVED( eEvent ) +#endif + +#ifndef iptraceBIND_FAILED + #define iptraceBIND_FAILED( xSocket, usPort ) +#endif + +#ifndef iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS + #define iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( ulIPAddress ) +#endif + +#ifndef iptraceSENDING_DHCP_DISCOVER + #define iptraceSENDING_DHCP_DISCOVER() +#endif + +#ifndef iptraceSENDING_DHCP_REQUEST + #define iptraceSENDING_DHCP_REQUEST() +#endif + +#ifndef iptraceDHCP_SUCCEDEED + #define iptraceDHCP_SUCCEDEED( address ) +#endif + +#ifndef iptraceNETWORK_INTERFACE_TRANSMIT + #define iptraceNETWORK_INTERFACE_TRANSMIT() +#endif + +#ifndef iptraceNETWORK_INTERFACE_RECEIVE + #define iptraceNETWORK_INTERFACE_RECEIVE() +#endif + +#ifndef iptraceSENDING_DNS_REQUEST + #define iptraceSENDING_DNS_REQUEST() +#endif + +#ifndef iptraceWAITING_FOR_TX_DMA_DESCRIPTOR + #define iptraceWAITING_FOR_TX_DMA_DESCRIPTOR() +#endif + +#ifndef ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS + #define ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS 0 +#endif + +#ifndef iptraceFAILED_TO_NOTIFY_SELECT_GROUP + #define iptraceFAILED_TO_NOTIFY_SELECT_GROUP( xSocket ) +#endif + +#ifndef pvPortMallocSocket + #define pvPortMallocSocket( xSize ) pvPortMalloc( ( xSize ) ) +#endif + +#ifndef iptraceRECVFROM_TIMEOUT + #define iptraceRECVFROM_TIMEOUT() +#endif + +#ifndef iptraceRECVFROM_INTERRUPTED + #define iptraceRECVFROM_INTERRUPTED() +#endif + +#ifndef iptraceNO_BUFFER_FOR_SENDTO + #define iptraceNO_BUFFER_FOR_SENDTO() +#endif + +#ifndef iptraceSENDTO_SOCKET_NOT_BOUND + #define iptraceSENDTO_SOCKET_NOT_BOUND() +#endif + +#ifndef iptraceSENDTO_DATA_TOO_LONG + #define iptraceSENDTO_DATA_TOO_LONG() +#endif + +#ifndef ipconfigUSE_TCP_MEM_STATS + #define ipconfigUSE_TCP_MEM_STATS 0 +#endif + +#if ( ipconfigUSE_TCP_MEM_STATS == 0 ) + +/* See tools/tcp_mem_stat.c */ + + #ifndef iptraceMEM_STATS_CREATE + #define iptraceMEM_STATS_CREATE( xMemType, pxObject, uxSize ) + #endif + + #ifndef iptraceMEM_STATS_DELETE + #define iptraceMEM_STATS_DELETE( pxObject ) + #endif + + #ifndef iptraceMEM_STATS_CLOSE + #define iptraceMEM_STATS_CLOSE() + #endif + +#endif /* ( ipconfigUSE_TCP_MEM_STATS != 0 ) */ + +#ifndef ipconfigUSE_DUMP_PACKETS + #define ipconfigUSE_DUMP_PACKETS 0 +#endif + +#if ( ipconfigUSE_DUMP_PACKETS == 0 ) + +/* See tools/tcp_dump_packets.c */ + + #ifndef iptraceDUMP_INIT + #define iptraceDUMP_INIT( pcFileName, pxEntries ) + #endif + + #ifndef iptraceDUMP_PACKET + #define iptraceDUMP_PACKET( pucBuffer, uxLength, xIncoming ) + #endif + +#endif /* ( ipconfigUSE_DUMP_PACKETS != 0 ) */ + +#endif /* UDP_TRACE_MACRO_DEFAULTS_H */ diff --git a/FreeRTOS/source/include/NetworkBufferManagement.h b/FreeRTOS/source/include/NetworkBufferManagement.h new file mode 100644 index 0000000..5be2b0b --- /dev/null +++ b/FreeRTOS/source/include/NetworkBufferManagement.h @@ -0,0 +1,83 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef NETWORK_BUFFER_MANAGEMENT_H +#define NETWORK_BUFFER_MANAGEMENT_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* NOTE PUBLIC API FUNCTIONS. */ +BaseType_t xNetworkBuffersInitialise( void ); +NetworkBufferDescriptor_t * pxGetNetworkBufferWithDescriptor( size_t xRequestedSizeBytes, + TickType_t xBlockTimeTicks ); + +/* The definition of the below function is only available if BufferAllocation_2.c has been linked into the source. */ +NetworkBufferDescriptor_t * pxNetworkBufferGetFromISR( size_t xRequestedSizeBytes ); +void vReleaseNetworkBufferAndDescriptor( NetworkBufferDescriptor_t * const pxNetworkBuffer ); + +/* The definition of the below function is only available if BufferAllocation_2.c has been linked into the source. */ +BaseType_t vNetworkBufferReleaseFromISR( NetworkBufferDescriptor_t * const pxNetworkBuffer ); +uint8_t * pucGetNetworkBuffer( size_t * pxRequestedSizeBytes ); +void vReleaseNetworkBuffer( uint8_t * pucEthernetBuffer ); + +/* Get the current number of free network buffers. */ +UBaseType_t uxGetNumberOfFreeNetworkBuffers( void ); + +/* Get the lowest number of free network buffers. */ +UBaseType_t uxGetMinimumFreeNetworkBuffers( void ); + +/* Copy a network buffer into a bigger buffer. */ +NetworkBufferDescriptor_t * pxDuplicateNetworkBufferWithDescriptor( const NetworkBufferDescriptor_t * const pxNetworkBuffer, + size_t uxNewLength ); + +/* Increase the size of a Network Buffer. + * In case BufferAllocation_2.c is used, the new space must be allocated. */ +NetworkBufferDescriptor_t * pxResizeNetworkBufferWithDescriptor( NetworkBufferDescriptor_t * pxNetworkBuffer, + size_t xNewSizeBytes ); + +#if ipconfigTCP_IP_SANITY + +/* + * Check if an address is a valid pointer to a network descriptor + * by looking it up in the array of network descriptors + */ + UBaseType_t bIsValidNetworkDescriptor( const NetworkBufferDescriptor_t * pxDesc ); + BaseType_t prvIsFreeBuffer( const NetworkBufferDescriptor_t * pxDescr ); +#endif + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + + +#endif /* NETWORK_BUFFER_MANAGEMENT_H */ diff --git a/FreeRTOS/source/include/NetworkInterface.h b/FreeRTOS/source/include/NetworkInterface.h new file mode 100644 index 0000000..97494f8 --- /dev/null +++ b/FreeRTOS/source/include/NetworkInterface.h @@ -0,0 +1,54 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#ifndef NETWORK_INTERFACE_H +#define NETWORK_INTERFACE_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* INTERNAL API FUNCTIONS. */ +BaseType_t xNetworkInterfaceInitialise( void ); +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t xReleaseAfterSend ); + +/* The following function is defined only when BufferAllocation_1.c is linked in the project. */ +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ); + +/* The following function is defined only when BufferAllocation_1.c is linked in the project. */ +BaseType_t xGetPhyLinkStatus( void ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } /* extern "C" */ +#endif +/* *INDENT-ON* */ + +#endif /* NETWORK_INTERFACE_H */ diff --git a/FreeRTOS/source/portable/BufferManagement/BufferAllocation_1.c b/FreeRTOS/source/portable/BufferManagement/BufferAllocation_1.c new file mode 100644 index 0000000..8565fb7 --- /dev/null +++ b/FreeRTOS/source/portable/BufferManagement/BufferAllocation_1.c @@ -0,0 +1,440 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/****************************************************************************** +* +* See the following web page for essential buffer allocation scheme usage and +* configuration details: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html +* +******************************************************************************/ + +/* Standard includes. */ +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" + +/* For an Ethernet interrupt to be able to obtain a network buffer there must + * be at least this number of buffers available. */ +#define baINTERRUPT_BUFFER_GET_THRESHOLD ( 3 ) + +/* A list of free (available) NetworkBufferDescriptor_t structures. */ +static List_t xFreeBuffersList; + +/* Some statistics about the use of buffers. */ +static UBaseType_t uxMinimumFreeNetworkBuffers = 0U; + +/* Declares the pool of NetworkBufferDescriptor_t structures that are available + * to the system. All the network buffers referenced from xFreeBuffersList exist + * in this array. The array is not accessed directly except during initialisation, + * when the xFreeBuffersList is filled (as all the buffers are free when the system + * is booted). */ +static NetworkBufferDescriptor_t xNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ]; + +/* This constant is defined as true to let FreeRTOS_TCP_IP.c know that the + * network buffers have constant size, large enough to hold the biggest Ethernet + * packet. No resizing will be done. */ +const BaseType_t xBufferAllocFixedSize = pdTRUE; + +/* The semaphore used to obtain network buffers. */ +static SemaphoreHandle_t xNetworkBufferSemaphore = NULL; + +#if ( ipconfigTCP_IP_SANITY != 0 ) + static char cIsLow = pdFALSE; + UBaseType_t bIsValidNetworkDescriptor( const NetworkBufferDescriptor_t * pxDesc ); +#else + static UBaseType_t bIsValidNetworkDescriptor( const NetworkBufferDescriptor_t * pxDesc ); +#endif /* ipconfigTCP_IP_SANITY */ + +static void prvShowWarnings( void ); + +/* The user can define their own ipconfigBUFFER_ALLOC_LOCK() and + * ipconfigBUFFER_ALLOC_UNLOCK() macros, especially for use form an ISR. If these + * are not defined then default them to call the normal enter/exit critical + * section macros. */ +#if !defined( ipconfigBUFFER_ALLOC_LOCK ) + + #define ipconfigBUFFER_ALLOC_INIT() do {} while( ipFALSE_BOOL ) + #define ipconfigBUFFER_ALLOC_LOCK_FROM_ISR() \ + UBaseType_t uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); \ + { + #define ipconfigBUFFER_ALLOC_UNLOCK_FROM_ISR() \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \ + } + + #define ipconfigBUFFER_ALLOC_LOCK() taskENTER_CRITICAL() + #define ipconfigBUFFER_ALLOC_UNLOCK() taskEXIT_CRITICAL() + +#endif /* ipconfigBUFFER_ALLOC_LOCK */ + +/*-----------------------------------------------------------*/ + +#if ( ipconfigTCP_IP_SANITY != 0 ) + +/* HT: SANITY code will be removed as soon as the library is stable + * and and ready to become public + * Function below gives information about the use of buffers */ + #define WARN_LOW ( 2 ) + #define WARN_HIGH ( ( 5 * ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ) / 10 ) + +#endif /* ipconfigTCP_IP_SANITY */ + +/*-----------------------------------------------------------*/ + +#if ( ipconfigTCP_IP_SANITY != 0 ) + + BaseType_t prvIsFreeBuffer( const NetworkBufferDescriptor_t * pxDescr ) + { + return ( bIsValidNetworkDescriptor( pxDescr ) != 0 ) && + ( listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxDescr->xBufferListItem ) ) != 0 ); + } + /*-----------------------------------------------------------*/ + + static void prvShowWarnings( void ) + { + UBaseType_t uxCount = uxGetNumberOfFreeNetworkBuffers(); + + if( ( ( cIsLow == 0 ) && ( uxCount <= WARN_LOW ) ) || ( ( cIsLow != 0 ) && ( uxCount >= WARN_HIGH ) ) ) + { + cIsLow = !cIsLow; + FreeRTOS_debug_printf( ( "*** Warning *** %s %lu buffers left\n", cIsLow ? "only" : "now", uxCount ) ); + } + } + /*-----------------------------------------------------------*/ + + UBaseType_t bIsValidNetworkDescriptor( const NetworkBufferDescriptor_t * pxDesc ) + { + uint32_t offset = ( uint32_t ) ( ( ( const char * ) pxDesc ) - ( ( const char * ) xNetworkBuffers ) ); + + if( ( offset >= sizeof( xNetworkBuffers ) ) || + ( ( offset % sizeof( xNetworkBuffers[ 0 ] ) ) != 0 ) ) + { + return pdFALSE; + } + + return ( UBaseType_t ) ( pxDesc - xNetworkBuffers ) + 1; + } + /*-----------------------------------------------------------*/ + +#else /* if ( ipconfigTCP_IP_SANITY != 0 ) */ + static UBaseType_t bIsValidNetworkDescriptor( const NetworkBufferDescriptor_t * pxDesc ) + { + ( void ) pxDesc; + return ( UBaseType_t ) pdTRUE; + } + /*-----------------------------------------------------------*/ + + static void prvShowWarnings( void ) + { + } + /*-----------------------------------------------------------*/ + +#endif /* ipconfigTCP_IP_SANITY */ + +BaseType_t xNetworkBuffersInitialise( void ) +{ + BaseType_t xReturn; + uint32_t x; + + /* Only initialise the buffers and their associated kernel objects if they + * have not been initialised before. */ + if( xNetworkBufferSemaphore == NULL ) + { + /* In case alternative locking is used, the mutexes can be initialised + * here */ + ipconfigBUFFER_ALLOC_INIT(); + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + static StaticSemaphore_t xNetworkBufferSemaphoreBuffer; + xNetworkBufferSemaphore = xSemaphoreCreateCountingStatic( + ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, + ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, + &xNetworkBufferSemaphoreBuffer ); + } + #else + { + xNetworkBufferSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ); + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + configASSERT( xNetworkBufferSemaphore != NULL ); + + if( xNetworkBufferSemaphore != NULL ) + { + vListInitialise( &xFreeBuffersList ); + + /* Initialise all the network buffers. The buffer storage comes + * from the network interface, and different hardware has different + * requirements. */ + vNetworkInterfaceAllocateRAMToBuffers( xNetworkBuffers ); + + for( x = 0U; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ ) + { + /* Initialise and set the owner of the buffer list items. */ + vListInitialiseItem( &( xNetworkBuffers[ x ].xBufferListItem ) ); + listSET_LIST_ITEM_OWNER( &( xNetworkBuffers[ x ].xBufferListItem ), &xNetworkBuffers[ x ] ); + + /* Currently, all buffers are available for use. */ + vListInsert( &xFreeBuffersList, &( xNetworkBuffers[ x ].xBufferListItem ) ); + } + + uxMinimumFreeNetworkBuffers = ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; + } + } + + if( xNetworkBufferSemaphore == NULL ) + { + xReturn = pdFAIL; + } + else + { + xReturn = pdPASS; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +NetworkBufferDescriptor_t * pxGetNetworkBufferWithDescriptor( size_t xRequestedSizeBytes, + TickType_t xBlockTimeTicks ) +{ + NetworkBufferDescriptor_t * pxReturn = NULL; + BaseType_t xInvalid = pdFALSE; + UBaseType_t uxCount; + + /* The current implementation only has a single size memory block, so + * the requested size parameter is not used (yet). */ + ( void ) xRequestedSizeBytes; + + if( xNetworkBufferSemaphore != NULL ) + { + /* If there is a semaphore available, there is a network buffer + * available. */ + if( xSemaphoreTake( xNetworkBufferSemaphore, xBlockTimeTicks ) == pdPASS ) + { + /* Protect the structure as it is accessed from tasks and + * interrupts. */ + ipconfigBUFFER_ALLOC_LOCK(); + { + pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList ); + + if( ( bIsValidNetworkDescriptor( pxReturn ) != pdFALSE_UNSIGNED ) && + listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxReturn->xBufferListItem ) ) ) + { + ( void ) uxListRemove( &( pxReturn->xBufferListItem ) ); + } + else + { + xInvalid = pdTRUE; + } + } + ipconfigBUFFER_ALLOC_UNLOCK(); + + if( xInvalid == pdTRUE ) + { + /* _RB_ Can printf() be called from an interrupt? (comment + * above says this can be called from an interrupt too) */ + + /* _HT_ The function shall not be called from an ISR. Comment + * was indeed misleading. Hopefully clear now? + * So the printf()is OK here. */ + FreeRTOS_debug_printf( ( "pxGetNetworkBufferWithDescriptor: INVALID BUFFER: %p (valid %lu)\n", + pxReturn, bIsValidNetworkDescriptor( pxReturn ) ) ); + pxReturn = NULL; + } + else + { + /* Reading UBaseType_t, no critical section needed. */ + uxCount = listCURRENT_LIST_LENGTH( &xFreeBuffersList ); + + /* For stats, latch the lowest number of network buffers since + * booting. */ + if( uxMinimumFreeNetworkBuffers > uxCount ) + { + uxMinimumFreeNetworkBuffers = uxCount; + } + + pxReturn->xDataLength = xRequestedSizeBytes; + + #if ( ipconfigTCP_IP_SANITY != 0 ) + { + prvShowWarnings(); + } + #endif /* ipconfigTCP_IP_SANITY */ + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + /* make sure the buffer is not linked */ + pxReturn->pxNextBuffer = NULL; + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + } + + iptraceNETWORK_BUFFER_OBTAINED( pxReturn ); + } + else + { + /* lint wants to see at least a comment. */ + iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER(); + } + } + + return pxReturn; +} +/*-----------------------------------------------------------*/ + +NetworkBufferDescriptor_t * pxNetworkBufferGetFromISR( size_t xRequestedSizeBytes ) +{ + NetworkBufferDescriptor_t * pxReturn = NULL; + + /* The current implementation only has a single size memory block, so + * the requested size parameter is not used (yet). */ + ( void ) xRequestedSizeBytes; + + /* If there is a semaphore available then there is a buffer available, but, + * as this is called from an interrupt, only take a buffer if there are at + * least baINTERRUPT_BUFFER_GET_THRESHOLD buffers remaining. This prevents, + * to a certain degree at least, a rapidly executing interrupt exhausting + * buffer and in so doing preventing tasks from continuing. */ + if( uxQueueMessagesWaitingFromISR( ( QueueHandle_t ) xNetworkBufferSemaphore ) > ( UBaseType_t ) baINTERRUPT_BUFFER_GET_THRESHOLD ) + { + if( xSemaphoreTakeFromISR( xNetworkBufferSemaphore, NULL ) == pdPASS ) + { + /* Protect the structure as it is accessed from tasks and interrupts. */ + ipconfigBUFFER_ALLOC_LOCK_FROM_ISR(); + { + pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList ); + uxListRemove( &( pxReturn->xBufferListItem ) ); + } + ipconfigBUFFER_ALLOC_UNLOCK_FROM_ISR(); + + iptraceNETWORK_BUFFER_OBTAINED_FROM_ISR( pxReturn ); + } + } + + if( pxReturn == NULL ) + { + iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER_FROM_ISR(); + } + + return pxReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t vNetworkBufferReleaseFromISR( NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + /* Ensure the buffer is returned to the list of free buffers before the + * counting semaphore is 'given' to say a buffer is available. */ + ipconfigBUFFER_ALLOC_LOCK_FROM_ISR(); + { + vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) ); + } + ipconfigBUFFER_ALLOC_UNLOCK_FROM_ISR(); + + ( void ) xSemaphoreGiveFromISR( xNetworkBufferSemaphore, &xHigherPriorityTaskWoken ); + iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer ); + + return xHigherPriorityTaskWoken; +} +/*-----------------------------------------------------------*/ + +void vReleaseNetworkBufferAndDescriptor( NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ + BaseType_t xListItemAlreadyInFreeList; + + if( bIsValidNetworkDescriptor( pxNetworkBuffer ) == pdFALSE_UNSIGNED ) + { + FreeRTOS_debug_printf( ( "vReleaseNetworkBufferAndDescriptor: Invalid buffer %p\n", pxNetworkBuffer ) ); + } + else + { + /* Ensure the buffer is returned to the list of free buffers before the + * counting semaphore is 'given' to say a buffer is available. */ + ipconfigBUFFER_ALLOC_LOCK(); + { + { + xListItemAlreadyInFreeList = listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) ); + + if( xListItemAlreadyInFreeList == pdFALSE ) + { + vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) ); + } + } + } + ipconfigBUFFER_ALLOC_UNLOCK(); + + if( xListItemAlreadyInFreeList ) + { + FreeRTOS_debug_printf( ( "vReleaseNetworkBufferAndDescriptor: %p ALREADY RELEASED (now %lu)\n", + pxNetworkBuffer, uxGetNumberOfFreeNetworkBuffers() ) ); + } + else + { + ( void ) xSemaphoreGive( xNetworkBufferSemaphore ); + prvShowWarnings(); + } + + iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer ); + } +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxGetMinimumFreeNetworkBuffers( void ) +{ + return uxMinimumFreeNetworkBuffers; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxGetNumberOfFreeNetworkBuffers( void ) +{ + return listCURRENT_LIST_LENGTH( &xFreeBuffersList ); +} + +NetworkBufferDescriptor_t * pxResizeNetworkBufferWithDescriptor( NetworkBufferDescriptor_t * pxNetworkBuffer, + size_t xNewSizeBytes ) +{ + /* In BufferAllocation_1.c all network buffer are allocated with a + * maximum size of 'ipTOTAL_ETHERNET_FRAME_SIZE'.No need to resize the + * network buffer. */ + pxNetworkBuffer->xDataLength = xNewSizeBytes; + return pxNetworkBuffer; +} + +/*#endif */ /* ipconfigINCLUDE_TEST_CODE */ diff --git a/FreeRTOS/source/portable/BufferManagement/BufferAllocation_2.c b/FreeRTOS/source/portable/BufferManagement/BufferAllocation_2.c new file mode 100644 index 0000000..bd8db4c --- /dev/null +++ b/FreeRTOS/source/portable/BufferManagement/BufferAllocation_2.c @@ -0,0 +1,434 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/****************************************************************************** +* +* See the following web page for essential buffer allocation scheme usage and +* configuration details: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html +* +******************************************************************************/ + +/* THIS FILE SHOULD NOT BE USED IF THE PROJECT INCLUDES A MEMORY ALLOCATOR + * THAT WILL FRAGMENT THE HEAP MEMORY. For example, heap_2 must not be used, + * heap_4 can be used. */ + + +/* Standard includes. */ +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" + +/* The obtained network buffer must be large enough to hold a packet that might + * replace the packet that was requested to be sent. */ +#if ipconfigUSE_TCP == 1 + #define baMINIMAL_BUFFER_SIZE sizeof( TCPPacket_t ) +#else + #define baMINIMAL_BUFFER_SIZE sizeof( ARPPacket_t ) +#endif /* ipconfigUSE_TCP == 1 */ + +/* Compile time assertion with zero runtime effects + * it will assert on 'e' not being zero, as it tries to divide by it, + * will also print the line where the error occured in case of failure */ +/* MISRA Ref 20.10.1 [Lack of sizeof operator and compile time error checking] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-2010 */ +/* coverity[misra_c_2012_rule_20_10_violation] */ +#if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES ) + #define ASSERT_CONCAT_( a, b ) a ## b + #define ASSERT_CONCAT( a, b ) ASSERT_CONCAT_( a, b ) + #define STATIC_ASSERT( e ) \ + ; enum { ASSERT_CONCAT( assert_line_, __LINE__ ) = 1 / ( !!( e ) ) } + + STATIC_ASSERT( ipconfigETHERNET_MINIMUM_PACKET_BYTES <= baMINIMAL_BUFFER_SIZE ); +#endif + +/* A list of free (available) NetworkBufferDescriptor_t structures. */ +static List_t xFreeBuffersList; + +/* Some statistics about the use of buffers. */ +static size_t uxMinimumFreeNetworkBuffers; + +/* This constant is defined as false to let FreeRTOS_TCP_IP.c know that the + * network buffers have a variable size: resizing may be necessary */ +const BaseType_t xBufferAllocFixedSize = pdFALSE; + +/* The semaphore used to obtain network buffers. */ +static SemaphoreHandle_t xNetworkBufferSemaphore = NULL; + +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkBuffersInitialise( void ) +{ + /* Declares the pool of NetworkBufferDescriptor_t structures that are available + * to the system. All the network buffers referenced from xFreeBuffersList exist + * in this array. The array is not accessed directly except during initialisation, + * when the xFreeBuffersList is filled (as all the buffers are free when the system + * is booted). */ + static NetworkBufferDescriptor_t xNetworkBufferDescriptors[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ]; + BaseType_t xReturn; + uint32_t x; + + /* Only initialise the buffers and their associated kernel objects if they + * have not been initialised before. */ + if( xNetworkBufferSemaphore == NULL ) + { + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + static StaticSemaphore_t xNetworkBufferSemaphoreBuffer; + xNetworkBufferSemaphore = xSemaphoreCreateCountingStatic( + ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, + ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, + &xNetworkBufferSemaphoreBuffer ); + } + #else + { + xNetworkBufferSemaphore = xSemaphoreCreateCounting( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ); + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + configASSERT( xNetworkBufferSemaphore != NULL ); + + if( xNetworkBufferSemaphore != NULL ) + { + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + vQueueAddToRegistry( xNetworkBufferSemaphore, "NetBufSem" ); + } + #endif /* configQUEUE_REGISTRY_SIZE */ + + /* If the trace recorder code is included name the semaphore for viewing + * in FreeRTOS+Trace. */ + #if ( ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 ) + { + extern QueueHandle_t xNetworkEventQueue; + vTraceSetQueueName( xNetworkEventQueue, "IPStackEvent" ); + vTraceSetQueueName( xNetworkBufferSemaphore, "NetworkBufferCount" ); + } + #endif /* ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 */ + + vListInitialise( &xFreeBuffersList ); + + /* Initialise all the network buffers. No storage is allocated to + * the buffers yet. */ + for( x = 0U; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ ) + { + /* Initialise and set the owner of the buffer list items. */ + xNetworkBufferDescriptors[ x ].pucEthernetBuffer = NULL; + vListInitialiseItem( &( xNetworkBufferDescriptors[ x ].xBufferListItem ) ); + listSET_LIST_ITEM_OWNER( &( xNetworkBufferDescriptors[ x ].xBufferListItem ), &xNetworkBufferDescriptors[ x ] ); + + /* Currently, all buffers are available for use. */ + vListInsert( &xFreeBuffersList, &( xNetworkBufferDescriptors[ x ].xBufferListItem ) ); + } + + uxMinimumFreeNetworkBuffers = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; + } + } + + if( xNetworkBufferSemaphore == NULL ) + { + xReturn = pdFAIL; + } + else + { + xReturn = pdPASS; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +uint8_t * pucGetNetworkBuffer( size_t * pxRequestedSizeBytes ) +{ + uint8_t * pucEthernetBuffer; + size_t xSize = *pxRequestedSizeBytes; + + if( xSize < baMINIMAL_BUFFER_SIZE ) + { + /* Buffers must be at least large enough to hold a TCP-packet with + * headers, or an ARP packet, in case TCP is not included. */ + xSize = baMINIMAL_BUFFER_SIZE; + } + + /* Round up xSize to the nearest multiple of N bytes, + * where N equals 'sizeof( size_t )'. */ + if( ( xSize & ( sizeof( size_t ) - 1U ) ) != 0U ) + { + xSize = ( xSize | ( sizeof( size_t ) - 1U ) ) + 1U; + } + + *pxRequestedSizeBytes = xSize; + + /* Allocate a buffer large enough to store the requested Ethernet frame size + * and a pointer to a network buffer structure (hence the addition of + * ipBUFFER_PADDING bytes). */ + pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xSize + ipBUFFER_PADDING ); + configASSERT( pucEthernetBuffer != NULL ); + + if( pucEthernetBuffer != NULL ) + { + /* Enough space is left at the start of the buffer to place a pointer to + * the network buffer structure that references this Ethernet buffer. + * Return a pointer to the start of the Ethernet buffer itself. */ + pucEthernetBuffer += ipBUFFER_PADDING; + } + + return pucEthernetBuffer; +} +/*-----------------------------------------------------------*/ + +void vReleaseNetworkBuffer( uint8_t * pucEthernetBuffer ) +{ + uint8_t * pucEthernetBufferCopy = pucEthernetBuffer; + + /* There is space before the Ethernet buffer in which a pointer to the + * network buffer that references this Ethernet buffer is stored. Remove the + * space before freeing the buffer. */ + if( pucEthernetBufferCopy != NULL ) + { + pucEthernetBufferCopy -= ipBUFFER_PADDING; + vPortFree( ( void * ) pucEthernetBufferCopy ); + } +} +/*-----------------------------------------------------------*/ + +NetworkBufferDescriptor_t * pxGetNetworkBufferWithDescriptor( size_t xRequestedSizeBytes, + TickType_t xBlockTimeTicks ) +{ + NetworkBufferDescriptor_t * pxReturn = NULL; + size_t uxCount; + size_t uxMaxAllowedBytes = ( SIZE_MAX >> 1 ); + size_t xRequestedSizeBytesCopy = xRequestedSizeBytes; + + if( ( xRequestedSizeBytesCopy <= uxMaxAllowedBytes ) && ( xNetworkBufferSemaphore != NULL ) ) + { + /* If there is a semaphore available, there is a network buffer available. */ + if( xSemaphoreTake( xNetworkBufferSemaphore, xBlockTimeTicks ) == pdPASS ) + { + /* Protect the structure as it is accessed from tasks and interrupts. */ + taskENTER_CRITICAL(); + { + pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList ); + ( void ) uxListRemove( &( pxReturn->xBufferListItem ) ); + } + taskEXIT_CRITICAL(); + + /* Reading UBaseType_t, no critical section needed. */ + uxCount = listCURRENT_LIST_LENGTH( &xFreeBuffersList ); + + if( uxMinimumFreeNetworkBuffers > uxCount ) + { + uxMinimumFreeNetworkBuffers = uxCount; + } + + /* Allocate storage of exactly the requested size to the buffer. */ + configASSERT( pxReturn->pucEthernetBuffer == NULL ); + + if( xRequestedSizeBytesCopy > 0U ) + { + if( ( xRequestedSizeBytesCopy < ( size_t ) baMINIMAL_BUFFER_SIZE ) ) + { + /* ARP packets can replace application packets, so the storage must be + * at least large enough to hold an ARP. */ + xRequestedSizeBytesCopy = baMINIMAL_BUFFER_SIZE; + } + + /* Add 2 bytes to xRequestedSizeBytesCopy and round up xRequestedSizeBytesCopy + * to the nearest multiple of N bytes, where N equals 'sizeof( size_t )'. */ + xRequestedSizeBytesCopy += 2U; + + if( ( xRequestedSizeBytesCopy & ( sizeof( size_t ) - 1U ) ) != 0U ) + { + xRequestedSizeBytesCopy = ( xRequestedSizeBytesCopy | ( sizeof( size_t ) - 1U ) ) + 1U; + } + + /* Extra space is obtained so a pointer to the network buffer can + * be stored at the beginning of the buffer. */ + pxReturn->pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xRequestedSizeBytesCopy + ipBUFFER_PADDING ); + + if( pxReturn->pucEthernetBuffer == NULL ) + { + /* The attempt to allocate storage for the buffer payload failed, + * so the network buffer structure cannot be used and must be + * released. */ + vReleaseNetworkBufferAndDescriptor( pxReturn ); + pxReturn = NULL; + } + else + { + /* Store a pointer to the network buffer structure in the + * buffer storage area, then move the buffer pointer on past the + * stored pointer so the pointer value is not overwritten by the + * application when the buffer is used. */ + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + *( ( NetworkBufferDescriptor_t ** ) ( pxReturn->pucEthernetBuffer ) ) = pxReturn; + pxReturn->pucEthernetBuffer += ipBUFFER_PADDING; + + /* Store the actual size of the allocated buffer, which may be + * greater than the original requested size. */ + pxReturn->xDataLength = xRequestedSizeBytesCopy; + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + /* make sure the buffer is not linked */ + pxReturn->pxNextBuffer = NULL; + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + } + } + else + { + /* A descriptor is being returned without an associated buffer being + * allocated. */ + } + } + } + + if( pxReturn == NULL ) + { + iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER(); + } + else + { + /* No action. */ + iptraceNETWORK_BUFFER_OBTAINED( pxReturn ); + } + + return pxReturn; +} +/*-----------------------------------------------------------*/ + +void vReleaseNetworkBufferAndDescriptor( NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ + BaseType_t xListItemAlreadyInFreeList; + + /* Ensure the buffer is returned to the list of free buffers before the + * counting semaphore is 'given' to say a buffer is available. Release the + * storage allocated to the buffer payload. THIS FILE SHOULD NOT BE USED + * IF THE PROJECT INCLUDES A MEMORY ALLOCATOR THAT WILL FRAGMENT THE HEAP + * MEMORY. For example, heap_2 must not be used, heap_4 can be used. */ + vReleaseNetworkBuffer( pxNetworkBuffer->pucEthernetBuffer ); + pxNetworkBuffer->pucEthernetBuffer = NULL; + pxNetworkBuffer->xDataLength = 0U; + + taskENTER_CRITICAL(); + { + xListItemAlreadyInFreeList = listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) ); + + if( xListItemAlreadyInFreeList == pdFALSE ) + { + vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) ); + } + } + taskEXIT_CRITICAL(); + + /* + * Update the network state machine, unless the program fails to release its 'xNetworkBufferSemaphore'. + * The program should only try to release its semaphore if 'xListItemAlreadyInFreeList' is false. + */ + if( xListItemAlreadyInFreeList == pdFALSE ) + { + if( xSemaphoreGive( xNetworkBufferSemaphore ) == pdTRUE ) + { + iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer ); + } + } + else + { + /* No action. */ + iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer ); + } +} +/*-----------------------------------------------------------*/ + +/* + * Returns the number of free network buffers + */ +UBaseType_t uxGetNumberOfFreeNetworkBuffers( void ) +{ + return listCURRENT_LIST_LENGTH( &xFreeBuffersList ); +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxGetMinimumFreeNetworkBuffers( void ) +{ + return uxMinimumFreeNetworkBuffers; +} +/*-----------------------------------------------------------*/ + +NetworkBufferDescriptor_t * pxResizeNetworkBufferWithDescriptor( NetworkBufferDescriptor_t * pxNetworkBuffer, + size_t xNewSizeBytes ) +{ + size_t xOriginalLength; + uint8_t * pucBuffer; + size_t uxSizeBytes = xNewSizeBytes; + NetworkBufferDescriptor_t * pxNetworkBufferCopy = pxNetworkBuffer; + + + + xOriginalLength = pxNetworkBufferCopy->xDataLength + ipBUFFER_PADDING; + uxSizeBytes = uxSizeBytes + ipBUFFER_PADDING; + + pucBuffer = pucGetNetworkBuffer( &( uxSizeBytes ) ); + + if( pucBuffer == NULL ) + { + /* In case the allocation fails, return NULL. */ + pxNetworkBufferCopy = NULL; + } + else + { + pxNetworkBufferCopy->xDataLength = uxSizeBytes; + + if( uxSizeBytes > xOriginalLength ) + { + uxSizeBytes = xOriginalLength; + } + + ( void ) memcpy( pucBuffer - ipBUFFER_PADDING, + pxNetworkBufferCopy->pucEthernetBuffer - ipBUFFER_PADDING, + uxSizeBytes ); + vReleaseNetworkBuffer( pxNetworkBufferCopy->pucEthernetBuffer ); + pxNetworkBufferCopy->pucEthernetBuffer = pucBuffer; + } + + return pxNetworkBufferCopy; +} diff --git a/FreeRTOS/source/portable/Compiler/CCS/pack_struct_end.h b/FreeRTOS/source/portable/Compiler/CCS/pack_struct_end.h new file mode 100644 index 0000000..0a48709 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/CCS/pack_struct_end.h @@ -0,0 +1,30 @@ +/** + * @file: pack_struct_end.h + * @author: jscott + * @date: Feb 1, 2022 + * @copyright: Hotstart 2022 Hotstart Thermal Management. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @brief: TI's Code Generation Tools do not add a trailing directive for packing structures + * + * Contains a semicolon to end the wrapped structure, + * and resets warnings that were supressed in pack_struct_start.h. + */ +; +#pragma diag_pop diff --git a/FreeRTOS/source/portable/Compiler/CCS/pack_struct_start.h b/FreeRTOS/source/portable/Compiler/CCS/pack_struct_start.h new file mode 100644 index 0000000..ff2a087 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/CCS/pack_struct_start.h @@ -0,0 +1,31 @@ +/** + * @file: pack_struct_start.h + * @author: jscott + * @date: Feb 1, 2022 + * @copyright: Hotstart 2022 Hotstart Thermal Management. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @brief: The leading compiler directive to pack the following structure to 1 byte + * + * Also suppress an incorrect warning from the CCS compiler: + * error #1916-D: definition at end of file not followed by a semicolon or a declarator + */ +#pragma diag_push +#pragma diag_suppress=1916 +#pragma pack(1) diff --git a/FreeRTOS/source/portable/Compiler/CompilerName/ReadMe.txt b/FreeRTOS/source/portable/Compiler/CompilerName/ReadMe.txt new file mode 100644 index 0000000..04a632a --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/CompilerName/ReadMe.txt @@ -0,0 +1,3 @@ +Update pack_struct_start.h and pack_struct_end.h for your architecure. +These files define the specifiers needed by your compiler to properly pack struct data +need by FreeRTOS+TCP. diff --git a/FreeRTOS/source/portable/Compiler/CompilerName/pack_struct_end.h b/FreeRTOS/source/portable/Compiler/CompilerName/pack_struct_end.h new file mode 100644 index 0000000..d554a26 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/CompilerName/pack_struct_end.h @@ -0,0 +1,34 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ +; /* FIX ME. Update for the compiler specifier needed at end of a struct declaration to pack the struct. */ diff --git a/FreeRTOS/source/portable/Compiler/CompilerName/pack_struct_start.h b/FreeRTOS/source/portable/Compiler/CompilerName/pack_struct_start.h new file mode 100644 index 0000000..0d90ee9 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/CompilerName/pack_struct_start.h @@ -0,0 +1,34 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ +/* FIX ME. Update for the compiler specifier needed at the start of a struct declaration to pack the struct. */ diff --git a/FreeRTOS/source/portable/Compiler/GCC/pack_struct_end.h b/FreeRTOS/source/portable/Compiler/GCC/pack_struct_end.h new file mode 100644 index 0000000..29fe44a --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/GCC/pack_struct_end.h @@ -0,0 +1,34 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ +__attribute__( ( packed ) ); diff --git a/FreeRTOS/source/portable/Compiler/GCC/pack_struct_start.h b/FreeRTOS/source/portable/Compiler/GCC/pack_struct_start.h new file mode 100644 index 0000000..c80d930 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/GCC/pack_struct_start.h @@ -0,0 +1,35 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ + +/* Nothing to do here. */ diff --git a/FreeRTOS/source/portable/Compiler/IAR/pack_struct_end.h b/FreeRTOS/source/portable/Compiler/IAR/pack_struct_end.h new file mode 100644 index 0000000..52995db --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/IAR/pack_struct_end.h @@ -0,0 +1,35 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ + +; diff --git a/FreeRTOS/source/portable/Compiler/IAR/pack_struct_start.h b/FreeRTOS/source/portable/Compiler/IAR/pack_struct_start.h new file mode 100644 index 0000000..885aacc --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/IAR/pack_struct_start.h @@ -0,0 +1,35 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ + +__packed diff --git a/FreeRTOS/source/portable/Compiler/Keil/pack_struct_end.h b/FreeRTOS/source/portable/Compiler/Keil/pack_struct_end.h new file mode 100644 index 0000000..83743d2 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/Keil/pack_struct_end.h @@ -0,0 +1,35 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ +; +#pragma pack(pop) diff --git a/FreeRTOS/source/portable/Compiler/Keil/pack_struct_start.h b/FreeRTOS/source/portable/Compiler/Keil/pack_struct_start.h new file mode 100644 index 0000000..cffdb86 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/Keil/pack_struct_start.h @@ -0,0 +1,35 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ + +#pragma pack(push,1) diff --git a/FreeRTOS/source/portable/Compiler/MSVC/pack_struct_end.h b/FreeRTOS/source/portable/Compiler/MSVC/pack_struct_end.h new file mode 100644 index 0000000..c0b16c8 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/MSVC/pack_struct_end.h @@ -0,0 +1,36 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ + +; +#pragma pack( pop ) diff --git a/FreeRTOS/source/portable/Compiler/MSVC/pack_struct_start.h b/FreeRTOS/source/portable/Compiler/MSVC/pack_struct_start.h new file mode 100644 index 0000000..e3dd6e5 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/MSVC/pack_struct_start.h @@ -0,0 +1,35 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ + +#pragma pack( push, 1 ) diff --git a/FreeRTOS/source/portable/Compiler/Renesas/pack_struct_end.h b/FreeRTOS/source/portable/Compiler/Renesas/pack_struct_end.h new file mode 100644 index 0000000..b7579ec --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/Renesas/pack_struct_end.h @@ -0,0 +1,47 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ + + +#ifdef _SH + #ifdef __RENESAS__ + ; + #pragma unpack + #endif +#endif +#ifdef __RX + #ifdef __CCRX__ + ; + #pragma packoption + #endif +#endif diff --git a/FreeRTOS/source/portable/Compiler/Renesas/pack_struct_start.h b/FreeRTOS/source/portable/Compiler/Renesas/pack_struct_start.h new file mode 100644 index 0000000..31649a9 --- /dev/null +++ b/FreeRTOS/source/portable/Compiler/Renesas/pack_struct_start.h @@ -0,0 +1,45 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* +* See the following URL for an explanation of this file: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Compiler_Porting.html +* +*****************************************************************************/ + + +#ifdef _SH + #ifdef __RENESAS__ + #pragma pack 1 + #endif +#endif +#ifdef __RX + #ifdef __CCRX__ + #pragma pack + #endif +#endif diff --git a/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/NetworkInterface.c new file mode 100644 index 0000000..e63ef8b --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/NetworkInterface.c @@ -0,0 +1,654 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +/* Some files from the Atmel Software Framework */ +/*_RB_ The SAM4E portable layer has three different header files called gmac.h! */ +#include "instance/gmac.h" +#include +#include + +#ifndef BMSR_LINK_STATUS + #define BMSR_LINK_STATUS 0x0004 /*!< Link status */ +#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 +#endif + +/* Interrupt events to process. Currently only the Rx event is processed + * although code for other events is included to allow for possible future + * expansion. */ +#define EMAC_IF_RX_EVENT 1UL +#define EMAC_IF_TX_EVENT 2UL +#define EMAC_IF_ERR_EVENT 4UL +#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) + +#define ETHERNET_CONF_PHY_ADDR BOARD_GMAC_PHY_ADDR + +#define HZ_PER_MHZ ( 1000000UL ) + +#ifndef EMAC_MAX_BLOCK_TIME_MS + #define EMAC_MAX_BLOCK_TIME_MS 100ul +#endif + +#if !defined( GMAC_USES_TX_CALLBACK ) || ( GMAC_USES_TX_CALLBACK != 1 ) + #error Please define GMAC_USES_TX_CALLBACK as 1 +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to 4x + * the size of the stack used by the idle task - but allow this to be overridden in + * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 4 * configMINIMAL_STACK_SIZE ) +#endif + +/*-----------------------------------------------------------*/ + +/* + * Wait a fixed time for the link status to indicate the network is up. + */ +static BaseType_t xGMACWaitLS( TickType_t xMaxTime ); + +#if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 ) + void vGMACGenerateChecksum( uint8_t * apBuffer ); +#endif + +/* + * Called from the ASF GMAC driver. + */ +static void prvRxCallback( uint32_t ulStatus ); +static void prvTxCallback( uint32_t ulStatus, + uint8_t * puc_buffer ); + +/* + * A deferred interrupt handler task that processes GMAC interrupts. + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/* + * Initialise the ASF GMAC driver. + */ +static BaseType_t prvGMACInit( void ); + +/* + * Try to obtain an Rx packet from the hardware. + */ +static uint32_t prvEMACRxPoll( void ); + +/*-----------------------------------------------------------*/ + +/* 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; +static volatile BaseType_t xGMACSwitchRequired; + +/* ethernet_phy_addr: the address of the PHY in use. + * Atmel was a bit ambiguous about it so the address will be stored + * in this variable, see ethernet_phy.c */ +extern int ethernet_phy_addr; + +/* LLMNR multicast address. */ +static const uint8_t llmnr_mac_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; + +/* The GMAC object as defined by the ASF drivers. */ +static gmac_device_t gs_gmac_dev; + +/* Holds the handle of the task used as a deferred interrupt processor. The + * handle is used so direct notifications can be sent to the task for all EMAC/DMA + * related interrupts. */ +TaskHandle_t xEMACTaskHandle = NULL; + +static QueueHandle_t xTxBufferQueue; +int tx_release_count[ 4 ]; + +/* xTXDescriptorSemaphore is a counting semaphore with + * a maximum count of GMAC_TX_BUFFERS, which is the number of + * DMA TX descriptors. */ +static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; + +/*-----------------------------------------------------------*/ + +/* + * GMAC interrupt handler. + */ +void GMAC_Handler( void ) +{ + xGMACSwitchRequired = pdFALSE; + + /* gmac_handler() may call prvRxCallback() which may change + * the value of xGMACSwitchRequired. */ + gmac_handler( &gs_gmac_dev ); + + if( xGMACSwitchRequired != pdFALSE ) + { + portEND_SWITCHING_ISR( xGMACSwitchRequired ); + } +} +/*-----------------------------------------------------------*/ + +static void prvRxCallback( uint32_t ulStatus ) +{ + if( ( ( ulStatus & GMAC_RSR_REC ) != 0 ) && ( xEMACTaskHandle != NULL ) ) + { + /* let the prvEMACHandlerTask know that there was an RX event. */ + ulISREvents |= EMAC_IF_RX_EVENT; + /* Only an RX interrupt can wakeup prvEMACHandlerTask. */ + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); + } +} +/*-----------------------------------------------------------*/ + +static void prvTxCallback( uint32_t ulStatus, + uint8_t * puc_buffer ) +{ + if( ( xTxBufferQueue != NULL ) && ( xEMACTaskHandle != NULL ) ) + { + /* let the prvEMACHandlerTask know that there was an RX event. */ + ulISREvents |= EMAC_IF_TX_EVENT; + + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); + xQueueSendFromISR( xTxBufferQueue, &puc_buffer, ( BaseType_t * ) &xGMACSwitchRequired ); + tx_release_count[ 2 ]++; + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + const TickType_t x5_Seconds = 5000UL; + + if( xEMACTaskHandle == NULL ) + { + prvGMACInit(); + + /* Wait at most 5 seconds for a Link Status in the PHY. */ + xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) ); + + /* The handler task is created at the highest possible priority to + * ensure the interrupt handler can return directly to it. */ + xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle ); + configASSERT( xEMACTaskHandle != NULL ); + } + + if( xTxBufferQueue == NULL ) + { + xTxBufferQueue = xQueueCreate( GMAC_TX_BUFFERS, sizeof( void * ) ); + configASSERT( xTxBufferQueue != NULL ); + } + + if( xTXDescriptorSemaphore == NULL ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) GMAC_TX_BUFFERS, ( UBaseType_t ) GMAC_TX_BUFFERS ); + configASSERT( xTXDescriptorSemaphore != NULL ); + } + + /* When returning non-zero, the stack will become active and + * start DHCP (in configured) */ + return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0; +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ + BaseType_t xResult; + + /* This function returns true if the Link Status in the PHY is high. */ + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + xResult = pdTRUE; + } + else + { + xResult = pdFALSE; + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t bReleaseAfterSend ) +{ +/* Do not wait too long for a free TX DMA buffer. */ + const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u ); + + do + { + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 ) + { + /* Do not attempt to send packets as long as the Link Status is low. */ + break; + } + + if( xTXDescriptorSemaphore == NULL ) + { + /* Semaphore has not been created yet? */ + break; + } + + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + /* Time-out waiting for a free TX descriptor. */ + tx_release_count[ 3 ]++; + break; + } + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Confirm that the pxDescriptor may be kept by the driver. */ + configASSERT( bReleaseAfterSend != pdFALSE ); + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + gmac_dev_write( &gs_gmac_dev, ( void * ) pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, prvTxCallback ); + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Confirm that the pxDescriptor may be kept by the driver. */ + bReleaseAfterSend = pdFALSE; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + /* Not interested in a call-back after TX. */ + iptraceNETWORK_INTERFACE_TRANSMIT(); + } while( ipFALSE_BOOL ); + + if( bReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvGMACInit( void ) +{ + uint32_t ncfgr; + + gmac_options_t gmac_option; + + memset( &gmac_option, '\0', sizeof( gmac_option ) ); + gmac_option.uc_copy_all_frame = 0; + gmac_option.uc_no_boardcast = 0; + memcpy( gmac_option.uc_mac_addr, ipLOCAL_MAC_ADDRESS, sizeof( gmac_option.uc_mac_addr ) ); + + gs_gmac_dev.p_hw = GMAC; + gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option ); + + NVIC_SetPriority( GMAC_IRQn, configMAC_INTERRUPT_PRIORITY ); + NVIC_EnableIRQ( GMAC_IRQn ); + + /* Contact the Ethernet PHY and store it's address in 'ethernet_phy_addr' */ + ethernet_phy_init( GMAC, ETHERNET_CONF_PHY_ADDR, sysclk_get_cpu_hz() ); + + ethernet_phy_auto_negotiate( GMAC, ethernet_phy_addr ); + ethernet_phy_set_link( GMAC, ethernet_phy_addr, 1 ); + + /* The GMAC driver will call a hook prvRxCallback(), which + * in turn will wake-up the task by calling vTaskNotifyGiveFromISR() */ + gmac_dev_set_rx_callback( &gs_gmac_dev, prvRxCallback ); + gmac_set_address( GMAC, 1, ( uint8_t * ) llmnr_mac_address ); + + ncfgr = GMAC_NCFGR_SPD | GMAC_NCFGR_FD; + + GMAC->GMAC_NCFGR = ( GMAC->GMAC_NCFGR & ~( GMAC_NCFGR_SPD | GMAC_NCFGR_FD ) ) | ncfgr; + + return 1; +} +/*-----------------------------------------------------------*/ + +static inline unsigned long ulReadMDIO( unsigned /*short*/ usAddress ) +{ + uint32_t ulValue, ulReturn; + int rc; + + gmac_enable_management( GMAC, 1 ); + rc = gmac_phy_read( GMAC, ethernet_phy_addr, usAddress, &ulValue ); + gmac_enable_management( GMAC, 0 ); + + if( rc == GMAC_OK ) + { + ulReturn = ulValue; + } + else + { + ulReturn = 0UL; + } + + return ulReturn; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xGMACWaitLS( TickType_t xMaxTime ) +{ + TickType_t xStartTime = xTaskGetTickCount(); + TickType_t xEndTime; + BaseType_t xReturn; + const TickType_t xShortTime = pdMS_TO_TICKS( 100UL ); + + for( ; ; ) + { + xEndTime = xTaskGetTickCount(); + + if( ( xEndTime - xStartTime ) > xMaxTime ) + { + /* Waited more than xMaxTime, return. */ + xReturn = pdFALSE; + break; + } + + /* Check the link status again. */ + ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + /* Link is up - return. */ + xReturn = pdTRUE; + break; + } + + /* Link is down - wait in the Blocked state for a short while (to allow + * other tasks to execute) before checking again. */ + vTaskDelay( xShortTime ); + } + + FreeRTOS_printf( ( "xGMACWaitLS: %ld (PHY %d) freq %lu Mz\n", + xReturn, + ethernet_phy_addr, + sysclk_get_cpu_hz() / HZ_PER_MHZ ) ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/*#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 ) */ + +void vGMACGenerateChecksum( uint8_t * apBuffer ) +{ + ProtocolPacket_t * xProtPacket = ( ProtocolPacket_t * ) apBuffer; + + if( xProtPacket->xTCPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) + { + IPHeader_t * pxIPHeader = &( xProtPacket->xTCPPacket.xIPHeader ); + + /* Calculate the IP header checksum. */ + pxIPHeader->usHeaderChecksum = 0x00; + pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); + pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); + + /* Calculate the TCP checksum for an outgoing packet. */ + usGenerateProtocolChecksum( ( uint8_t * ) apBuffer, pdTRUE ); + } +} + +/*#endif */ +/*-----------------------------------------------------------*/ + +static uint32_t prvEMACRxPoll( void ) +{ + unsigned char * pucUseBuffer; + uint32_t ulReceiveCount, ulResult, ulReturnValue = 0; + static NetworkBufferDescriptor_t * pxNextNetworkBufferDescriptor = NULL; + const UBaseType_t xMinDescriptorsToLeave = 2UL; + const TickType_t xBlockTime = pdMS_TO_TICKS( 100UL ); + static IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + + for( ; ; ) + { + /* If pxNextNetworkBufferDescriptor was not left pointing at a valid + * descriptor then allocate one now. */ + if( ( pxNextNetworkBufferDescriptor == NULL ) && ( uxGetNumberOfFreeNetworkBuffers() > xMinDescriptorsToLeave ) ) + { + pxNextNetworkBufferDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xBlockTime ); + } + + if( pxNextNetworkBufferDescriptor != NULL ) + { + /* Point pucUseBuffer to the buffer pointed to by the descriptor. */ + pucUseBuffer = ( unsigned char * ) ( pxNextNetworkBufferDescriptor->pucEthernetBuffer - ipconfigPACKET_FILLER_SIZE ); + } + else + { + /* As long as pxNextNetworkBufferDescriptor is NULL, the incoming + * messages will be flushed and ignored. */ + pucUseBuffer = NULL; + } + + /* Read the next packet from the hardware into pucUseBuffer. */ + ulResult = gmac_dev_read( &gs_gmac_dev, pucUseBuffer, ipTOTAL_ETHERNET_FRAME_SIZE, &ulReceiveCount ); + + if( ( ulResult != GMAC_OK ) || ( ulReceiveCount == 0 ) ) + { + /* No data from the hardware. */ + break; + } + + if( pxNextNetworkBufferDescriptor == NULL ) + { + /* Data was read from the hardware, but no descriptor was available + * for it, so it will be dropped. */ + iptraceETHERNET_RX_EVENT_LOST(); + continue; + } + + iptraceNETWORK_INTERFACE_RECEIVE(); + pxNextNetworkBufferDescriptor->xDataLength = ( size_t ) ulReceiveCount; + xRxEvent.pvData = ( void * ) pxNextNetworkBufferDescriptor; + + /* Send the descriptor to the IP task for processing. */ + if( xSendEventStructToIPTask( &xRxEvent, xBlockTime ) != pdTRUE ) + { + /* The buffer could not be sent to the stack so must be released + * again. */ + vReleaseNetworkBufferAndDescriptor( pxNextNetworkBufferDescriptor ); + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) ); + } + + /* Now the buffer has either been passed to the IP-task, + * or it has been released in the code above. */ + pxNextNetworkBufferDescriptor = NULL; + ulReturnValue++; + } + + return ulReturnValue; +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void * pvParameters ) +{ + TimeOut_t xPhyTime; + TickType_t xPhyRemTime; + UBaseType_t uxLastMinBufferCount = 0, uxCount; + UBaseType_t uxCurrentCount; + + #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + UBaseType_t uxLastMinQueueSpace; + #endif + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + NetworkBufferDescriptor_t * pxBuffer; + #endif + uint8_t * pucBuffer; + BaseType_t xResult = 0; + uint32_t xStatus; + const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS ); + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + configASSERT( xEMACTaskHandle != NULL ); + + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + + for( ; ; ) + { + uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); + + if( uxLastMinBufferCount != uxCurrentCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinBufferCount = uxCurrentCount; + FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", + uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); + } + + #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + { + uxCurrentCount = uxGetMinimumIPQueueSpace(); + + if( uxLastMinQueueSpace != uxCurrentCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinQueueSpace = uxCurrentCount; + FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); + } + } + #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ + + if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 ) + { + /* No events to process now, wait for the next. */ + ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); + } + + if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 ) + { + ulISREvents &= ~EMAC_IF_RX_EVENT; + + /* Wait for the EMAC interrupt to indicate that another packet has been + * received. */ + xResult = prvEMACRxPoll(); + } + + if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) + { + /* Future extension: code to release TX buffers if zero-copy is used. */ + ulISREvents &= ~EMAC_IF_TX_EVENT; + + while( xQueueReceive( xTxBufferQueue, &pucBuffer, 0 ) != pdFALSE ) + { + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + pxBuffer = pxPacketBuffer_to_NetworkBuffer( pucBuffer ); + + if( pxBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + tx_release_count[ 0 ]++; + } + else + { + tx_release_count[ 1 ]++; + } + } + #else /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + { + tx_release_count[ 0 ]++; + } + #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + uxCount = uxQueueMessagesWaiting( ( QueueHandle_t ) xTXDescriptorSemaphore ); + + if( uxCount < GMAC_TX_BUFFERS ) + { + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + } + } + } + + if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 ) + { + /* Future extension: logging about errors that occurred. */ + ulISREvents &= ~EMAC_IF_ERR_EVENT; + } + + if( 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 ) + { + /* Check the link status again. */ + xStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) ) + { + ulPHYLinkStatus = xStatus; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) ); + } + + 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 ); + } + } + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/component/gmac.h b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/component/gmac.h new file mode 100644 index 0000000..099dbba --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/component/gmac.h @@ -0,0 +1,748 @@ +/** + * \file + * + * Copyright (c) 2012 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 + * + */ + +#ifndef _SAM4E_GMAC_COMPONENT_ +#define _SAM4E_GMAC_COMPONENT_ + +/* ============================================================================= */ +/** SOFTWARE API DEFINITION FOR Gigabit Ethernet MAC */ +/* ============================================================================= */ +/** \addtogroup SAM4E_GMAC Gigabit Ethernet MAC */ +/*@{*/ + +#if !( defined( __ASSEMBLY__ ) || defined( __IAR_SYSTEMS_ASM__ ) ) +/** \brief GmacSa hardware registers */ + typedef struct + { + RwReg GMAC_SAB; /**< \brief (GmacSa Offset: 0x0) Specific Address 1 Bottom [31:0] Register */ + RwReg GMAC_SAT; /**< \brief (GmacSa Offset: 0x4) Specific Address 1 Top [47:32] Register */ + } GmacSa; +/** \brief Gmac hardware registers */ + #define GMACSA_NUMBER 4 + typedef struct + { + RwReg GMAC_NCR; /**< \brief (Gmac Offset: 0x000) Network Control Register */ + RwReg GMAC_NCFGR; /**< \brief (Gmac Offset: 0x004) Network Configuration Register */ + RoReg GMAC_NSR; /**< \brief (Gmac Offset: 0x008) Network Status Register */ + RwReg GMAC_UR; /**< \brief (Gmac Offset: 0x00C) User Register */ + RwReg GMAC_DCFGR; /**< \brief (Gmac Offset: 0x010) DMA Configuration Register */ + RwReg GMAC_TSR; /**< \brief (Gmac Offset: 0x014) Transmit Status Register */ + RwReg GMAC_RBQB; /**< \brief (Gmac Offset: 0x018) Receive Buffer Queue Base Address */ + RwReg GMAC_TBQB; /**< \brief (Gmac Offset: 0x01C) Transmit Buffer Queue Base Address */ + RwReg GMAC_RSR; /**< \brief (Gmac Offset: 0x020) Receive Status Register */ + RoReg GMAC_ISR; /**< \brief (Gmac Offset: 0x024) Interrupt Status Register */ + WoReg GMAC_IER; /**< \brief (Gmac Offset: 0x028) Interrupt Enable Register */ + WoReg GMAC_IDR; /**< \brief (Gmac Offset: 0x02C) Interrupt Disable Register */ + RoReg GMAC_IMR; /**< \brief (Gmac Offset: 0x030) Interrupt Mask Register */ + RwReg GMAC_MAN; /**< \brief (Gmac Offset: 0x034) PHY Maintenance Register */ + RoReg GMAC_RPQ; /**< \brief (Gmac Offset: 0x038) Received Pause Quantum Register */ + RwReg GMAC_TPQ; /**< \brief (Gmac Offset: 0x03C) Transmit Pause Quantum Register */ + RwReg GMAC_TPSF; /**< \brief (Gmac Offset: 0x040) TX Partial Store and Forward Register */ + RwReg GMAC_RPSF; /**< \brief (Gmac Offset: 0x044) RX Partial Store and Forward Register */ + RoReg Reserved1[ 14 ]; + RwReg GMAC_HRB; /**< \brief (Gmac Offset: 0x080) Hash Register Bottom [31:0] */ + RwReg GMAC_HRT; /**< \brief (Gmac Offset: 0x084) Hash Register Top [63:32] */ + GmacSa GMAC_SA[ GMACSA_NUMBER ]; /**< \brief (Gmac Offset: 0x088) 1 .. 4 */ + RwReg GMAC_TIDM[ 4 ]; /**< \brief (Gmac Offset: 0x0A8) Type ID Match 1 Register */ + RwReg GMAC_WOL; /**< \brief (Gmac Offset: 0x0B8) Wake on LAN Register */ + RwReg GMAC_IPGS; /**< \brief (Gmac Offset: 0x0BC) IPG Stretch Register */ + RwReg GMAC_SVLAN; /**< \brief (Gmac Offset: 0x0C0) Stacked VLAN Register */ + RwReg GMAC_TPFCP; /**< \brief (Gmac Offset: 0x0C4) Transmit PFC Pause Register */ + RwReg GMAC_SAMB1; /**< \brief (Gmac Offset: 0x0C8) Specific Address 1 Mask Bottom [31:0] Register */ + RwReg GMAC_SAMT1; /**< \brief (Gmac Offset: 0x0CC) Specific Address 1 Mask Top [47:32] Register */ + RoReg Reserved2[ 12 ]; + RoReg GMAC_OTLO; /**< \brief (Gmac Offset: 0x100) Octets Transmitted [31:0] Register */ + RoReg GMAC_OTHI; /**< \brief (Gmac Offset: 0x104) Octets Transmitted [47:32] Register */ + RoReg GMAC_FT; /**< \brief (Gmac Offset: 0x108) Frames Transmitted Register */ + RoReg GMAC_BCFT; /**< \brief (Gmac Offset: 0x10C) Broadcast Frames Transmitted Register */ + RoReg GMAC_MFT; /**< \brief (Gmac Offset: 0x110) Multicast Frames Transmitted Register */ + RoReg GMAC_PFT; /**< \brief (Gmac Offset: 0x114) Pause Frames Transmitted Register */ + RoReg GMAC_BFT64; /**< \brief (Gmac Offset: 0x118) 64 Byte Frames Transmitted Register */ + RoReg GMAC_TBFT127; /**< \brief (Gmac Offset: 0x11C) 65 to 127 Byte Frames Transmitted Register */ + RoReg GMAC_TBFT255; /**< \brief (Gmac Offset: 0x120) 128 to 255 Byte Frames Transmitted Register */ + RoReg GMAC_TBFT511; /**< \brief (Gmac Offset: 0x124) 256 to 511 Byte Frames Transmitted Register */ + RoReg GMAC_TBFT1023; /**< \brief (Gmac Offset: 0x128) 512 to 1023 Byte Frames Transmitted Register */ + RoReg GMAC_TBFT1518; /**< \brief (Gmac Offset: 0x12C) 1024 to 1518 Byte Frames Transmitted Register */ + RoReg GMAC_GTBFT1518; /**< \brief (Gmac Offset: 0x130) Greater Than 1518 Byte Frames Transmitted Register */ + RoReg GMAC_TUR; /**< \brief (Gmac Offset: 0x134) Transmit Under Runs Register */ + RoReg GMAC_SCF; /**< \brief (Gmac Offset: 0x138) Single Collision Frames Register */ + RoReg GMAC_MCF; /**< \brief (Gmac Offset: 0x13C) Multiple Collision Frames Register */ + RoReg GMAC_EC; /**< \brief (Gmac Offset: 0x140) Excessive Collisions Register */ + RoReg GMAC_LC; /**< \brief (Gmac Offset: 0x144) Late Collisions Register */ + RoReg GMAC_DTF; /**< \brief (Gmac Offset: 0x148) Deferred Transmission Frames Register */ + RoReg GMAC_CSE; /**< \brief (Gmac Offset: 0x14C) Carrier Sense Errors Register */ + RoReg GMAC_ORLO; /**< \brief (Gmac Offset: 0x150) Octets Received [31:0] Received */ + RoReg GMAC_ORHI; /**< \brief (Gmac Offset: 0x154) Octets Received [47:32] Received */ + RoReg GMAC_FR; /**< \brief (Gmac Offset: 0x158) Frames Received Register */ + RoReg GMAC_BCFR; /**< \brief (Gmac Offset: 0x15C) Broadcast Frames Received Register */ + RoReg GMAC_MFR; /**< \brief (Gmac Offset: 0x160) Multicast Frames Received Register */ + RoReg GMAC_PFR; /**< \brief (Gmac Offset: 0x164) Pause Frames Received Register */ + RoReg GMAC_BFR64; /**< \brief (Gmac Offset: 0x168) 64 Byte Frames Received Register */ + RoReg GMAC_TBFR127; /**< \brief (Gmac Offset: 0x16C) 65 to 127 Byte Frames Received Register */ + RoReg GMAC_TBFR255; /**< \brief (Gmac Offset: 0x170) 128 to 255 Byte Frames Received Register */ + RoReg GMAC_TBFR511; /**< \brief (Gmac Offset: 0x174) 256 to 511Byte Frames Received Register */ + RoReg GMAC_TBFR1023; /**< \brief (Gmac Offset: 0x178) 512 to 1023 Byte Frames Received Register */ + RoReg GMAC_TBFR1518; /**< \brief (Gmac Offset: 0x17C) 1024 to 1518 Byte Frames Received Register */ + RoReg GMAC_TMXBFR; /**< \brief (Gmac Offset: 0x180) 1519 to Maximum Byte Frames Received Register */ + RoReg GMAC_UFR; /**< \brief (Gmac Offset: 0x184) Undersize Frames Received Register */ + RoReg GMAC_OFR; /**< \brief (Gmac Offset: 0x188) Oversize Frames Received Register */ + RoReg GMAC_JR; /**< \brief (Gmac Offset: 0x18C) Jabbers Received Register */ + RoReg GMAC_FCSE; /**< \brief (Gmac Offset: 0x190) Frame Check Sequence Errors Register */ + RoReg GMAC_LFFE; /**< \brief (Gmac Offset: 0x194) Length Field Frame Errors Register */ + RoReg GMAC_RSE; /**< \brief (Gmac Offset: 0x198) Receive Symbol Errors Register */ + RoReg GMAC_AE; /**< \brief (Gmac Offset: 0x19C) Alignment Errors Register */ + RoReg GMAC_RRE; /**< \brief (Gmac Offset: 0x1A0) Receive Resource Errors Register */ + RoReg GMAC_ROE; /**< \brief (Gmac Offset: 0x1A4) Receive Overrun Register */ + RoReg GMAC_IHCE; /**< \brief (Gmac Offset: 0x1A8) IP Header Checksum Errors Register */ + RoReg GMAC_TCE; /**< \brief (Gmac Offset: 0x1AC) TCP Checksum Errors Register */ + RoReg GMAC_UCE; /**< \brief (Gmac Offset: 0x1B0) UDP Checksum Errors Register */ + RoReg Reserved3[ 5 ]; + RwReg GMAC_TSSS; /**< \brief (Gmac Offset: 0x1C8) 1588 Timer Sync Strobe Seconds Register */ + RwReg GMAC_TSSN; /**< \brief (Gmac Offset: 0x1CC) 1588 Timer Sync Strobe Nanoseconds Register */ + RwReg GMAC_TS; /**< \brief (Gmac Offset: 0x1D0) 1588 Timer Seconds Register */ + RwReg GMAC_TN; /**< \brief (Gmac Offset: 0x1D4) 1588 Timer Nanoseconds Register */ + WoReg GMAC_TA; /**< \brief (Gmac Offset: 0x1D8) 1588 Timer Adjust Register */ + RwReg GMAC_TI; /**< \brief (Gmac Offset: 0x1DC) 1588 Timer Increment Register */ + RoReg GMAC_EFTS; /**< \brief (Gmac Offset: 0x1E0) PTP Event Frame Transmitted Seconds */ + RoReg GMAC_EFTN; /**< \brief (Gmac Offset: 0x1E4) PTP Event Frame Transmitted Nanoseconds */ + RoReg GMAC_EFRS; /**< \brief (Gmac Offset: 0x1E8) PTP Event Frame Received Seconds */ + RoReg GMAC_EFRN; /**< \brief (Gmac Offset: 0x1EC) PTP Event Frame Received Nanoseconds */ + RoReg GMAC_PEFTS; /**< \brief (Gmac Offset: 0x1F0) PTP Peer Event Frame Transmitted Seconds */ + RoReg GMAC_PEFTN; /**< \brief (Gmac Offset: 0x1F4) PTP Peer Event Frame Transmitted Nanoseconds */ + RoReg GMAC_PEFRS; /**< \brief (Gmac Offset: 0x1F8) PTP Peer Event Frame Received Seconds */ + RoReg GMAC_PEFRN; /**< \brief (Gmac Offset: 0x1FC) PTP Peer Event Frame Received Nanoseconds */ + RoReg Reserved4[ 128 ]; + RoReg GMAC_ISRPQ[ 7 ]; /**< \brief (Gmac Offset: 0x400) Interrupt Status Register Priority Queue */ + RoReg Reserved5[ 9 ]; + RwReg GMAC_TBQBAPQ[ 7 ]; /**< \brief (Gmac Offset: 0x440) Transmit Buffer Queue Base Address Priority Queue */ + RoReg Reserved6[ 9 ]; + RwReg GMAC_RBQBAPQ[ 7 ]; /**< \brief (Gmac Offset: 0x480) Receive Buffer Queue Base Address Priority Queue */ + RoReg Reserved7[ 1 ]; + RwReg GMAC_RBSRPQ[ 7 ]; /**< \brief (Gmac Offset: 0x4A0) Receive Buffer Size Register Priority Queue */ + RoReg Reserved8[ 17 ]; + RwReg GMAC_ST1RPQ[ 16 ]; /**< \brief (Gmac Offset: 0x500) Screening Type1 Register Priority Queue */ + RwReg GMAC_ST2RPQ[ 16 ]; /**< \brief (Gmac Offset: 0x540) Screening Type2 Register Priority Queue */ + RoReg Reserved9[ 32 ]; + WoReg GMAC_IERPQ[ 7 ]; /**< \brief (Gmac Offset: 0x600) Interrupt Enable Register Priority Queue */ + RoReg Reserved10[ 1 ]; + WoReg GMAC_IDRPQ[ 7 ]; /**< \brief (Gmac Offset: 0x620) Interrupt Disable Register Priority Queue */ + RoReg Reserved11[ 1 ]; + RwReg GMAC_IMRPQ[ 7 ]; /**< \brief (Gmac Offset: 0x640) Interrupt Mask Register Priority Queue */ + } Gmac; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ +/* -------- GMAC_NCR : (GMAC Offset: 0x000) Network Control Register -------- */ +#define GMAC_NCR_LB ( 0x1u << 0 ) /**< \brief (GMAC_NCR) Loop Back */ +#define GMAC_NCR_LBL ( 0x1u << 1 ) /**< \brief (GMAC_NCR) Loop Back Local */ +#define GMAC_NCR_RXEN ( 0x1u << 2 ) /**< \brief (GMAC_NCR) Receive Enable */ +#define GMAC_NCR_TXEN ( 0x1u << 3 ) /**< \brief (GMAC_NCR) Transmit Enable */ +#define GMAC_NCR_MPE ( 0x1u << 4 ) /**< \brief (GMAC_NCR) Management Port Enable */ +#define GMAC_NCR_CLRSTAT ( 0x1u << 5 ) /**< \brief (GMAC_NCR) Clear Statistics Registers */ +#define GMAC_NCR_INCSTAT ( 0x1u << 6 ) /**< \brief (GMAC_NCR) Increment Statistics Registers */ +#define GMAC_NCR_WESTAT ( 0x1u << 7 ) /**< \brief (GMAC_NCR) Write Enable for Statistics Registers */ +#define GMAC_NCR_BP ( 0x1u << 8 ) /**< \brief (GMAC_NCR) Back pressure */ +#define GMAC_NCR_TSTART ( 0x1u << 9 ) /**< \brief (GMAC_NCR) Start Transmission */ +#define GMAC_NCR_THALT ( 0x1u << 10 ) /**< \brief (GMAC_NCR) Transmit Halt */ +#define GMAC_NCR_TXPF ( 0x1u << 11 ) /**< \brief (GMAC_NCR) Transmit Pause Frame */ +#define GMAC_NCR_TXZQPF ( 0x1u << 12 ) /**< \brief (GMAC_NCR) Transmit Zero Quantum Pause Frame */ +#define GMAC_NCR_RDS ( 0x1u << 14 ) /**< \brief (GMAC_NCR) Read Snapshot */ +#define GMAC_NCR_SRTSM ( 0x1u << 15 ) /**< \brief (GMAC_NCR) Store Receive Time Stamp to Memory */ +#define GMAC_NCR_ENPBPR ( 0x1u << 16 ) /**< \brief (GMAC_NCR) Enable PFC Priority-based Pause Reception */ +#define GMAC_NCR_TXPBPF ( 0x1u << 17 ) /**< \brief (GMAC_NCR) Transmit PFC Priority-based Pause Frame */ +#define GMAC_NCR_FNP ( 0x1u << 18 ) /**< \brief (GMAC_NCR) Flush Next Packet */ +/* -------- GMAC_NCFGR : (GMAC Offset: 0x004) Network Configuration Register -------- */ +#define GMAC_NCFGR_SPD ( 0x1u << 0 ) /**< \brief (GMAC_NCFGR) Speed */ +#define GMAC_NCFGR_FD ( 0x1u << 1 ) /**< \brief (GMAC_NCFGR) Full Duplex */ +#define GMAC_NCFGR_DNVLAN ( 0x1u << 2 ) /**< \brief (GMAC_NCFGR) Discard Non-VLAN FRAMES */ +#define GMAC_NCFGR_JFRAME ( 0x1u << 3 ) /**< \brief (GMAC_NCFGR) Jumbo Frame Size */ +#define GMAC_NCFGR_CAF ( 0x1u << 4 ) /**< \brief (GMAC_NCFGR) Copy All Frames */ +#define GMAC_NCFGR_NBC ( 0x1u << 5 ) /**< \brief (GMAC_NCFGR) No Broadcast */ +#define GMAC_NCFGR_MTIHEN ( 0x1u << 6 ) /**< \brief (GMAC_NCFGR) Multicast Hash Enable */ +#define GMAC_NCFGR_UNIHEN ( 0x1u << 7 ) /**< \brief (GMAC_NCFGR) Unicast Hash Enable */ +#define GMAC_NCFGR_MAXFS ( 0x1u << 8 ) /**< \brief (GMAC_NCFGR) 1536 Maximum Frame Size */ +#define GMAC_NCFGR_GBE ( 0x1u << 10 ) /**< \brief (GMAC_NCFGR) Gigabit Mode Enable */ +#define GMAC_NCFGR_PIS ( 0x1u << 11 ) /**< \brief (GMAC_NCFGR) Physical Interface Select */ +#define GMAC_NCFGR_RTY ( 0x1u << 12 ) /**< \brief (GMAC_NCFGR) Retry Test */ +#define GMAC_NCFGR_PEN ( 0x1u << 13 ) /**< \brief (GMAC_NCFGR) Pause Enable */ +#define GMAC_NCFGR_RXBUFO_Pos 14 +#define GMAC_NCFGR_RXBUFO_Msk ( 0x3u << GMAC_NCFGR_RXBUFO_Pos ) /**< \brief (GMAC_NCFGR) Receive Buffer Offset */ +#define GMAC_NCFGR_RXBUFO( value ) ( ( GMAC_NCFGR_RXBUFO_Msk & ( ( value ) << GMAC_NCFGR_RXBUFO_Pos ) ) ) +#define GMAC_NCFGR_LFERD ( 0x1u << 16 ) /**< \brief (GMAC_NCFGR) Length Field Error Frame Discard */ +#define GMAC_NCFGR_RFCS ( 0x1u << 17 ) /**< \brief (GMAC_NCFGR) Remove FCS */ +#define GMAC_NCFGR_CLK_Pos 18 +#define GMAC_NCFGR_CLK_Msk ( 0x7u << GMAC_NCFGR_CLK_Pos ) /**< \brief (GMAC_NCFGR) MDC CLock Division */ +#define GMAC_NCFGR_CLK_MCK_8 ( 0x0u << 18 ) /**< \brief (GMAC_NCFGR) MCK divided by 8 (MCK up to 20 MHz) */ +#define GMAC_NCFGR_CLK_MCK_16 ( 0x1u << 18 ) /**< \brief (GMAC_NCFGR) MCK divided by 16 (MCK up to 40 MHz) */ +#define GMAC_NCFGR_CLK_MCK_32 ( 0x2u << 18 ) /**< \brief (GMAC_NCFGR) MCK divided by 32 (MCK up to 80 MHz) */ +#define GMAC_NCFGR_CLK_MCK_48 ( 0x3u << 18 ) /**< \brief (GMAC_NCFGR) MCK divided by 48 (MCK up to 120MHz) */ +#define GMAC_NCFGR_CLK_MCK_64 ( 0x4u << 18 ) /**< \brief (GMAC_NCFGR) MCK divided by 64 (MCK up to 160 MHz) */ +#define GMAC_NCFGR_CLK_MCK_96 ( 0x5u << 18 ) /**< \brief (GMAC_NCFGR) MCK divided by 96 (MCK up to 240 MHz) */ +#define GMAC_NCFGR_CLK_MCK_128 ( 0x6u << 18 ) /**< \brief (GMAC_NCFGR) MCK divided by 128 (MCK up to 320 MHz) */ +#define GMAC_NCFGR_CLK_MCK_224 ( 0x7u << 18 ) /**< \brief (GMAC_NCFGR) MCK divided by 224 (MCK up to 540 MHz) */ +#define GMAC_NCFGR_DBW_Pos 21 +#define GMAC_NCFGR_DBW_Msk ( 0x3u << GMAC_NCFGR_DBW_Pos ) /**< \brief (GMAC_NCFGR) Data Bus Width */ +#define GMAC_NCFGR_DBW_DBW32 ( 0x0u << 21 ) /**< \brief (GMAC_NCFGR) 32-bit data bus width */ +#define GMAC_NCFGR_DBW_DBW64 ( 0x1u << 21 ) /**< \brief (GMAC_NCFGR) 64-bit data bus width */ +#define GMAC_NCFGR_DCPF ( 0x1u << 23 ) /**< \brief (GMAC_NCFGR) Disable Copy of Pause Frames */ +#define GMAC_NCFGR_RXCOEN ( 0x1u << 24 ) /**< \brief (GMAC_NCFGR) Receive Checksum Offload Enable */ +#define GMAC_NCFGR_EFRHD ( 0x1u << 25 ) /**< \brief (GMAC_NCFGR) Enable Frames Received in Half Duplex */ +#define GMAC_NCFGR_IRXFCS ( 0x1u << 26 ) /**< \brief (GMAC_NCFGR) Ignore RX FCS */ +#define GMAC_NCFGR_IPGSEN ( 0x1u << 28 ) /**< \brief (GMAC_NCFGR) IP Stretch Enable */ +#define GMAC_NCFGR_RXBP ( 0x1u << 29 ) /**< \brief (GMAC_NCFGR) Receive Bad Preamble */ +#define GMAC_NCFGR_IRXER ( 0x1u << 30 ) /**< \brief (GMAC_NCFGR) Ignore IPG rx_er */ +/* -------- GMAC_NSR : (GMAC Offset: 0x008) Network Status Register -------- */ +#define GMAC_NSR_MDIO ( 0x1u << 1 ) /**< \brief (GMAC_NSR) MDIO Input Status */ +#define GMAC_NSR_IDLE ( 0x1u << 2 ) /**< \brief (GMAC_NSR) PHY Management Logic Idle */ +/* -------- GMAC_UR : (GMAC Offset: 0x00C) User Register -------- */ +#define GMAC_UR_RGMII ( 0x1u << 0 ) /**< \brief (GMAC_UR) RGMII Mode */ +#define GMAC_UR_HDFC ( 0x1u << 6 ) /**< \brief (GMAC_UR) Half Duplex Flow Control */ +#define GMAC_UR_BPDG ( 0x1u << 7 ) /**< \brief (GMAC_UR) BPDG Bypass Deglitchers */ +/* -------- GMAC_DCFGR : (GMAC Offset: 0x010) DMA Configuration Register -------- */ +#define GMAC_DCFGR_FBLDO_Pos 0 +#define GMAC_DCFGR_FBLDO_Msk ( 0x1fu << GMAC_DCFGR_FBLDO_Pos ) /**< \brief (GMAC_DCFGR) Fixed Burst Length for DMA Data Operations: */ +#define GMAC_DCFGR_FBLDO_SINGLE ( 0x1u << 0 ) /**< \brief (GMAC_DCFGR) 00001: Always use SINGLE AHB bursts */ +#define GMAC_DCFGR_FBLDO_INCR4 ( 0x4u << 0 ) /**< \brief (GMAC_DCFGR) 001xx: Attempt to use INCR4 AHB bursts (Default) */ +#define GMAC_DCFGR_FBLDO_INCR8 ( 0x8u << 0 ) /**< \brief (GMAC_DCFGR) 01xxx: Attempt to use INCR8 AHB bursts */ +#define GMAC_DCFGR_FBLDO_INCR16 ( 0x10u << 0 ) /**< \brief (GMAC_DCFGR) 1xxxx: Attempt to use INCR16 AHB bursts */ +#define GMAC_DCFGR_ESMA ( 0x1u << 6 ) /**< \brief (GMAC_DCFGR) Endian Swap Mode Enable for Management Descriptor Accesses */ +#define GMAC_DCFGR_ESPA ( 0x1u << 7 ) /**< \brief (GMAC_DCFGR) Endian Swap Mode Enable for Packet Data Accesses */ +#define GMAC_DCFGR_RXBMS_Pos 8 +#define GMAC_DCFGR_RXBMS_Msk ( 0x3u << GMAC_DCFGR_RXBMS_Pos ) /**< \brief (GMAC_DCFGR) Receiver Packet Buffer Memory Size Select */ +#define GMAC_DCFGR_RXBMS_EIGHTH ( 0x0u << 8 ) /**< \brief (GMAC_DCFGR) 1 Kbyte Memory Size */ +#define GMAC_DCFGR_RXBMS_QUARTER ( 0x1u << 8 ) /**< \brief (GMAC_DCFGR) 2 Kbytes Memory Size */ +#define GMAC_DCFGR_RXBMS_HALF ( 0x2u << 8 ) /**< \brief (GMAC_DCFGR) 4 Kbytes Memory Size */ +#define GMAC_DCFGR_RXBMS_FULL ( 0x3u << 8 ) /**< \brief (GMAC_DCFGR) 8 Kbytes Memory Size */ +#define GMAC_DCFGR_TXPBMS ( 0x1u << 10 ) /**< \brief (GMAC_DCFGR) Transmitter Packet Buffer Memory Size Select */ +#define GMAC_DCFGR_TXCOEN ( 0x1u << 11 ) /**< \brief (GMAC_DCFGR) Transmitter Checksum Generation Offload Enable */ +#define GMAC_DCFGR_DRBS_Pos 16 +#define GMAC_DCFGR_DRBS_Msk ( 0xffu << GMAC_DCFGR_DRBS_Pos ) /**< \brief (GMAC_DCFGR) DMA Receive Buffer Size */ +#define GMAC_DCFGR_DRBS( value ) ( ( GMAC_DCFGR_DRBS_Msk & ( ( value ) << GMAC_DCFGR_DRBS_Pos ) ) ) +#define GMAC_DCFGR_DDRP ( 0x1u << 24 ) /**< \brief (GMAC_DCFGR) DMA Discard Receive Packets */ +/* -------- GMAC_TSR : (GMAC Offset: 0x014) Transmit Status Register -------- */ +#define GMAC_TSR_UBR ( 0x1u << 0 ) /**< \brief (GMAC_TSR) Used Bit Read */ +#define GMAC_TSR_COL ( 0x1u << 1 ) /**< \brief (GMAC_TSR) Collision Occurred */ +#define GMAC_TSR_RLE ( 0x1u << 2 ) /**< \brief (GMAC_TSR) Retry Limit Exceeded */ +#define GMAC_TSR_TXGO ( 0x1u << 3 ) /**< \brief (GMAC_TSR) Transmit Go */ +#define GMAC_TSR_TFC ( 0x1u << 4 ) /**< \brief (GMAC_TSR) Transmit Frame Corruption due to AHB error */ +#define GMAC_TSR_TXCOMP ( 0x1u << 5 ) /**< \brief (GMAC_TSR) Transmit Complete */ +#define GMAC_TSR_UND ( 0x1u << 6 ) /**< \brief (GMAC_TSR) Transmit Under Run */ +#define GMAC_TSR_LCO ( 0x1u << 7 ) /**< \brief (GMAC_TSR) Late Collision Occurred */ +#define GMAC_TSR_HRESP ( 0x1u << 8 ) /**< \brief (GMAC_TSR) HRESP Not OK */ +/* -------- GMAC_RBQB : (GMAC Offset: 0x018) Receive Buffer Queue Base Address -------- */ +#define GMAC_RBQB_ADDR_Pos 2 +#define GMAC_RBQB_ADDR_Msk ( 0x3fffffffu << GMAC_RBQB_ADDR_Pos ) /**< \brief (GMAC_RBQB) Receive buffer queue base address */ +#define GMAC_RBQB_ADDR( value ) ( ( GMAC_RBQB_ADDR_Msk & ( ( value ) << GMAC_RBQB_ADDR_Pos ) ) ) +/* -------- GMAC_TBQB : (GMAC Offset: 0x01C) Transmit Buffer Queue Base Address -------- */ +#define GMAC_TBQB_ADDR_Pos 2 +#define GMAC_TBQB_ADDR_Msk ( 0x3fffffffu << GMAC_TBQB_ADDR_Pos ) /**< \brief (GMAC_TBQB) Transmit Buffer Queue Base Address */ +#define GMAC_TBQB_ADDR( value ) ( ( GMAC_TBQB_ADDR_Msk & ( ( value ) << GMAC_TBQB_ADDR_Pos ) ) ) +/* -------- GMAC_RSR : (GMAC Offset: 0x020) Receive Status Register -------- */ +#define GMAC_RSR_BNA ( 0x1u << 0 ) /**< \brief (GMAC_RSR) Buffer Not Available */ +#define GMAC_RSR_REC ( 0x1u << 1 ) /**< \brief (GMAC_RSR) Frame Received */ +#define GMAC_RSR_RXOVR ( 0x1u << 2 ) /**< \brief (GMAC_RSR) Receive Overrun */ +#define GMAC_RSR_HNO ( 0x1u << 3 ) /**< \brief (GMAC_RSR) HRESP Not OK */ +/* -------- GMAC_ISR : (GMAC Offset: 0x024) Interrupt Status Register -------- */ +#define GMAC_ISR_MFS ( 0x1u << 0 ) /**< \brief (GMAC_ISR) Management Frame Sent */ +#define GMAC_ISR_RCOMP ( 0x1u << 1 ) /**< \brief (GMAC_ISR) Receive Complete */ +#define GMAC_ISR_RXUBR ( 0x1u << 2 ) /**< \brief (GMAC_ISR) RX Used Bit Read */ +#define GMAC_ISR_TXUBR ( 0x1u << 3 ) /**< \brief (GMAC_ISR) TX Used Bit Read */ +#define GMAC_ISR_TUR ( 0x1u << 4 ) /**< \brief (GMAC_ISR) Transmit Under Run */ +#define GMAC_ISR_RLEX ( 0x1u << 5 ) /**< \brief (GMAC_ISR) Retry Limit Exceeded or Late Collision */ +#define GMAC_ISR_TFC ( 0x1u << 6 ) /**< \brief (GMAC_ISR) Transmit Frame Corruption due to AHB error */ +#define GMAC_ISR_TCOMP ( 0x1u << 7 ) /**< \brief (GMAC_ISR) Transmit Complete */ +#define GMAC_ISR_ROVR ( 0x1u << 10 ) /**< \brief (GMAC_ISR) Receive Overrun */ +#define GMAC_ISR_HRESP ( 0x1u << 11 ) /**< \brief (GMAC_ISR) HRESP Not OK */ +#define GMAC_ISR_PFNZ ( 0x1u << 12 ) /**< \brief (GMAC_ISR) Pause Frame with Non-zero Pause Quantum Received */ +#define GMAC_ISR_PTZ ( 0x1u << 13 ) /**< \brief (GMAC_ISR) Pause Time Zero */ +#define GMAC_ISR_PFTR ( 0x1u << 14 ) /**< \brief (GMAC_ISR) Pause Frame Transmitted */ +#define GMAC_ISR_EXINT ( 0x1u << 15 ) /**< \brief (GMAC_ISR) External Interrupt */ +#define GMAC_ISR_DRQFR ( 0x1u << 18 ) /**< \brief (GMAC_ISR) PTP Delay Request Frame Received */ +#define GMAC_ISR_SFR ( 0x1u << 19 ) /**< \brief (GMAC_ISR) PTP Sync Frame Received */ +#define GMAC_ISR_DRQFT ( 0x1u << 20 ) /**< \brief (GMAC_ISR) PTP Delay Request Frame Transmitted */ +#define GMAC_ISR_SFT ( 0x1u << 21 ) /**< \brief (GMAC_ISR) PTP Sync Frame Transmitted */ +#define GMAC_ISR_PDRQFR ( 0x1u << 22 ) /**< \brief (GMAC_ISR) PDelay Request Frame Received */ +#define GMAC_ISR_PDRSFR ( 0x1u << 23 ) /**< \brief (GMAC_ISR) PDelay Response Frame Received */ +#define GMAC_ISR_PDRQFT ( 0x1u << 24 ) /**< \brief (GMAC_ISR) PDelay Request Frame Transmitted */ +#define GMAC_ISR_PDRSFT ( 0x1u << 25 ) /**< \brief (GMAC_ISR) PDelay Response Frame Transmitted */ +#define GMAC_ISR_SRI ( 0x1u << 26 ) /**< \brief (GMAC_ISR) TSU Seconds Register Increment */ +#define GMAC_ISR_WOL ( 0x1u << 28 ) /**< \brief (GMAC_ISR) Wake On LAN */ +/* -------- GMAC_IER : (GMAC Offset: 0x028) Interrupt Enable Register -------- */ +#define GMAC_IER_MFS ( 0x1u << 0 ) /**< \brief (GMAC_IER) Management Frame Sent */ +#define GMAC_IER_RCOMP ( 0x1u << 1 ) /**< \brief (GMAC_IER) Receive Complete */ +#define GMAC_IER_RXUBR ( 0x1u << 2 ) /**< \brief (GMAC_IER) RX Used Bit Read */ +#define GMAC_IER_TXUBR ( 0x1u << 3 ) /**< \brief (GMAC_IER) TX Used Bit Read */ +#define GMAC_IER_TUR ( 0x1u << 4 ) /**< \brief (GMAC_IER) Transmit Under Run */ +#define GMAC_IER_RLEX ( 0x1u << 5 ) /**< \brief (GMAC_IER) Retry Limit Exceeded or Late Collision */ +#define GMAC_IER_TFC ( 0x1u << 6 ) /**< \brief (GMAC_IER) Transmit Frame Corruption due to AHB error */ +#define GMAC_IER_TCOMP ( 0x1u << 7 ) /**< \brief (GMAC_IER) Transmit Complete */ +#define GMAC_IER_ROVR ( 0x1u << 10 ) /**< \brief (GMAC_IER) Receive Overrun */ +#define GMAC_IER_HRESP ( 0x1u << 11 ) /**< \brief (GMAC_IER) HRESP Not OK */ +#define GMAC_IER_PFNZ ( 0x1u << 12 ) /**< \brief (GMAC_IER) Pause Frame with Non-zero Pause Quantum Received */ +#define GMAC_IER_PTZ ( 0x1u << 13 ) /**< \brief (GMAC_IER) Pause Time Zero */ +#define GMAC_IER_PFTR ( 0x1u << 14 ) /**< \brief (GMAC_IER) Pause Frame Transmitted */ +#define GMAC_IER_EXINT ( 0x1u << 15 ) /**< \brief (GMAC_IER) External Interrupt */ +#define GMAC_IER_DRQFR ( 0x1u << 18 ) /**< \brief (GMAC_IER) PTP Delay Request Frame Received */ +#define GMAC_IER_SFR ( 0x1u << 19 ) /**< \brief (GMAC_IER) PTP Sync Frame Received */ +#define GMAC_IER_DRQFT ( 0x1u << 20 ) /**< \brief (GMAC_IER) PTP Delay Request Frame Transmitted */ +#define GMAC_IER_SFT ( 0x1u << 21 ) /**< \brief (GMAC_IER) PTP Sync Frame Transmitted */ +#define GMAC_IER_PDRQFR ( 0x1u << 22 ) /**< \brief (GMAC_IER) PDelay Request Frame Received */ +#define GMAC_IER_PDRSFR ( 0x1u << 23 ) /**< \brief (GMAC_IER) PDelay Response Frame Received */ +#define GMAC_IER_PDRQFT ( 0x1u << 24 ) /**< \brief (GMAC_IER) PDelay Request Frame Transmitted */ +#define GMAC_IER_PDRSFT ( 0x1u << 25 ) /**< \brief (GMAC_IER) PDelay Response Frame Transmitted */ +#define GMAC_IER_SRI ( 0x1u << 26 ) /**< \brief (GMAC_IER) TSU Seconds Register Increment */ +#define GMAC_IER_WOL ( 0x1u << 28 ) /**< \brief (GMAC_IER) Wake On LAN */ +/* -------- GMAC_IDR : (GMAC Offset: 0x02C) Interrupt Disable Register -------- */ +#define GMAC_IDR_MFS ( 0x1u << 0 ) /**< \brief (GMAC_IDR) Management Frame Sent */ +#define GMAC_IDR_RCOMP ( 0x1u << 1 ) /**< \brief (GMAC_IDR) Receive Complete */ +#define GMAC_IDR_RXUBR ( 0x1u << 2 ) /**< \brief (GMAC_IDR) RX Used Bit Read */ +#define GMAC_IDR_TXUBR ( 0x1u << 3 ) /**< \brief (GMAC_IDR) TX Used Bit Read */ +#define GMAC_IDR_TUR ( 0x1u << 4 ) /**< \brief (GMAC_IDR) Transmit Under Run */ +#define GMAC_IDR_RLEX ( 0x1u << 5 ) /**< \brief (GMAC_IDR) Retry Limit Exceeded or Late Collision */ +#define GMAC_IDR_TFC ( 0x1u << 6 ) /**< \brief (GMAC_IDR) Transmit Frame Corruption due to AHB error */ +#define GMAC_IDR_TCOMP ( 0x1u << 7 ) /**< \brief (GMAC_IDR) Transmit Complete */ +#define GMAC_IDR_ROVR ( 0x1u << 10 ) /**< \brief (GMAC_IDR) Receive Overrun */ +#define GMAC_IDR_HRESP ( 0x1u << 11 ) /**< \brief (GMAC_IDR) HRESP Not OK */ +#define GMAC_IDR_PFNZ ( 0x1u << 12 ) /**< \brief (GMAC_IDR) Pause Frame with Non-zero Pause Quantum Received */ +#define GMAC_IDR_PTZ ( 0x1u << 13 ) /**< \brief (GMAC_IDR) Pause Time Zero */ +#define GMAC_IDR_PFTR ( 0x1u << 14 ) /**< \brief (GMAC_IDR) Pause Frame Transmitted */ +#define GMAC_IDR_EXINT ( 0x1u << 15 ) /**< \brief (GMAC_IDR) External Interrupt */ +#define GMAC_IDR_DRQFR ( 0x1u << 18 ) /**< \brief (GMAC_IDR) PTP Delay Request Frame Received */ +#define GMAC_IDR_SFR ( 0x1u << 19 ) /**< \brief (GMAC_IDR) PTP Sync Frame Received */ +#define GMAC_IDR_DRQFT ( 0x1u << 20 ) /**< \brief (GMAC_IDR) PTP Delay Request Frame Transmitted */ +#define GMAC_IDR_SFT ( 0x1u << 21 ) /**< \brief (GMAC_IDR) PTP Sync Frame Transmitted */ +#define GMAC_IDR_PDRQFR ( 0x1u << 22 ) /**< \brief (GMAC_IDR) PDelay Request Frame Received */ +#define GMAC_IDR_PDRSFR ( 0x1u << 23 ) /**< \brief (GMAC_IDR) PDelay Response Frame Received */ +#define GMAC_IDR_PDRQFT ( 0x1u << 24 ) /**< \brief (GMAC_IDR) PDelay Request Frame Transmitted */ +#define GMAC_IDR_PDRSFT ( 0x1u << 25 ) /**< \brief (GMAC_IDR) PDelay Response Frame Transmitted */ +#define GMAC_IDR_SRI ( 0x1u << 26 ) /**< \brief (GMAC_IDR) TSU Seconds Register Increment */ +#define GMAC_IDR_WOL ( 0x1u << 28 ) /**< \brief (GMAC_IDR) Wake On LAN */ +/* -------- GMAC_IMR : (GMAC Offset: 0x030) Interrupt Mask Register -------- */ +#define GMAC_IMR_MFS ( 0x1u << 0 ) /**< \brief (GMAC_IMR) Management Frame Sent */ +#define GMAC_IMR_RCOMP ( 0x1u << 1 ) /**< \brief (GMAC_IMR) Receive Complete */ +#define GMAC_IMR_RXUBR ( 0x1u << 2 ) /**< \brief (GMAC_IMR) RX Used Bit Read */ +#define GMAC_IMR_TXUBR ( 0x1u << 3 ) /**< \brief (GMAC_IMR) TX Used Bit Read */ +#define GMAC_IMR_TUR ( 0x1u << 4 ) /**< \brief (GMAC_IMR) Transmit Under Run */ +#define GMAC_IMR_RLEX ( 0x1u << 5 ) /**< \brief (GMAC_IMR) Retry Limit Exceeded or Late Collision */ +#define GMAC_IMR_TFC ( 0x1u << 6 ) /**< \brief (GMAC_IMR) Transmit Frame Corruption due to AHB error */ +#define GMAC_IMR_TCOMP ( 0x1u << 7 ) /**< \brief (GMAC_IMR) Transmit Complete */ +#define GMAC_IMR_ROVR ( 0x1u << 10 ) /**< \brief (GMAC_IMR) Receive Overrun */ +#define GMAC_IMR_HRESP ( 0x1u << 11 ) /**< \brief (GMAC_IMR) HRESP Not OK */ +#define GMAC_IMR_PFNZ ( 0x1u << 12 ) /**< \brief (GMAC_IMR) Pause Frame with Non-zero Pause Quantum Received */ +#define GMAC_IMR_PTZ ( 0x1u << 13 ) /**< \brief (GMAC_IMR) Pause Time Zero */ +#define GMAC_IMR_PFTR ( 0x1u << 14 ) /**< \brief (GMAC_IMR) Pause Frame Transmitted */ +#define GMAC_IMR_EXINT ( 0x1u << 15 ) /**< \brief (GMAC_IMR) External Interrupt */ +#define GMAC_IMR_DRQFR ( 0x1u << 18 ) /**< \brief (GMAC_IMR) PTP Delay Request Frame Received */ +#define GMAC_IMR_SFR ( 0x1u << 19 ) /**< \brief (GMAC_IMR) PTP Sync Frame Received */ +#define GMAC_IMR_DRQFT ( 0x1u << 20 ) /**< \brief (GMAC_IMR) PTP Delay Request Frame Transmitted */ +#define GMAC_IMR_SFT ( 0x1u << 21 ) /**< \brief (GMAC_IMR) PTP Sync Frame Transmitted */ +#define GMAC_IMR_PDRQFR ( 0x1u << 22 ) /**< \brief (GMAC_IMR) PDelay Request Frame Received */ +#define GMAC_IMR_PDRSFR ( 0x1u << 23 ) /**< \brief (GMAC_IMR) PDelay Response Frame Received */ +#define GMAC_IMR_PDRQFT ( 0x1u << 24 ) /**< \brief (GMAC_IMR) PDelay Request Frame Transmitted */ +#define GMAC_IMR_PDRSFT ( 0x1u << 25 ) /**< \brief (GMAC_IMR) PDelay Response Frame Transmitted */ +/* -------- GMAC_MAN : (GMAC Offset: 0x034) PHY Maintenance Register -------- */ +#define GMAC_MAN_DATA_Pos 0 +#define GMAC_MAN_DATA_Msk ( 0xffffu << GMAC_MAN_DATA_Pos ) /**< \brief (GMAC_MAN) PHY Data */ +#define GMAC_MAN_DATA( value ) ( ( GMAC_MAN_DATA_Msk & ( ( value ) << GMAC_MAN_DATA_Pos ) ) ) +#define GMAC_MAN_WTN_Pos 16 +#define GMAC_MAN_WTN_Msk ( 0x3u << GMAC_MAN_WTN_Pos ) /**< \brief (GMAC_MAN) Write Ten */ +#define GMAC_MAN_WTN( value ) ( ( GMAC_MAN_WTN_Msk & ( ( value ) << GMAC_MAN_WTN_Pos ) ) ) +#define GMAC_MAN_REGA_Pos 18 +#define GMAC_MAN_REGA_Msk ( 0x1fu << GMAC_MAN_REGA_Pos ) /**< \brief (GMAC_MAN) Register Address */ +#define GMAC_MAN_REGA( value ) ( ( GMAC_MAN_REGA_Msk & ( ( value ) << GMAC_MAN_REGA_Pos ) ) ) +#define GMAC_MAN_PHYA_Pos 23 +#define GMAC_MAN_PHYA_Msk ( 0x1fu << GMAC_MAN_PHYA_Pos ) /**< \brief (GMAC_MAN) PHY Address */ +#define GMAC_MAN_PHYA( value ) ( ( GMAC_MAN_PHYA_Msk & ( ( value ) << GMAC_MAN_PHYA_Pos ) ) ) +#define GMAC_MAN_OP_Pos 28 +#define GMAC_MAN_OP_Msk ( 0x3u << GMAC_MAN_OP_Pos ) /**< \brief (GMAC_MAN) Operation */ +#define GMAC_MAN_OP( value ) ( ( GMAC_MAN_OP_Msk & ( ( value ) << GMAC_MAN_OP_Pos ) ) ) +#define GMAC_MAN_CLTTO ( 0x1u << 30 ) /**< \brief (GMAC_MAN) Clause 22 Operation */ +#define GMAC_MAN_WZO ( 0x1u << 31 ) /**< \brief (GMAC_MAN) Write ZERO */ +/* -------- GMAC_RPQ : (GMAC Offset: 0x038) Received Pause Quantum Register -------- */ +#define GMAC_RPQ_RPQ_Pos 0 +#define GMAC_RPQ_RPQ_Msk ( 0xffffu << GMAC_RPQ_RPQ_Pos ) /**< \brief (GMAC_RPQ) Received Pause Quantum */ +/* -------- GMAC_TPQ : (GMAC Offset: 0x03C) Transmit Pause Quantum Register -------- */ +#define GMAC_TPQ_TPQ_Pos 0 +#define GMAC_TPQ_TPQ_Msk ( 0xffffu << GMAC_TPQ_TPQ_Pos ) /**< \brief (GMAC_TPQ) Transmit Pause Quantum */ +#define GMAC_TPQ_TPQ( value ) ( ( GMAC_TPQ_TPQ_Msk & ( ( value ) << GMAC_TPQ_TPQ_Pos ) ) ) +/* -------- GMAC_TPSF : (GMAC Offset: 0x040) TX Partial Store and Forward Register -------- */ +#define GMAC_TPSF_TPB1ADR_Pos 0 +#define GMAC_TPSF_TPB1ADR_Msk ( 0xfffu << GMAC_TPSF_TPB1ADR_Pos ) /**< \brief (GMAC_TPSF) tx_pbuf_addr-1:0 */ +#define GMAC_TPSF_TPB1ADR( value ) ( ( GMAC_TPSF_TPB1ADR_Msk & ( ( value ) << GMAC_TPSF_TPB1ADR_Pos ) ) ) +#define GMAC_TPSF_ENTXP ( 0x1u << 31 ) /**< \brief (GMAC_TPSF) Enable TX Partial Store and Forward Operation */ +/* -------- GMAC_RPSF : (GMAC Offset: 0x044) RX Partial Store and Forward Register -------- */ +#define GMAC_RPSF_RPB1ADR_Pos 0 +#define GMAC_RPSF_RPB1ADR_Msk ( 0xfffu << GMAC_RPSF_RPB1ADR_Pos ) /**< \brief (GMAC_RPSF) rx_pbuf_addr-1:0 */ +#define GMAC_RPSF_RPB1ADR( value ) ( ( GMAC_RPSF_RPB1ADR_Msk & ( ( value ) << GMAC_RPSF_RPB1ADR_Pos ) ) ) +#define GMAC_RPSF_ENRXP ( 0x1u << 31 ) /**< \brief (GMAC_RPSF) Enable RX Partial Store and Forward Operation */ +/* -------- GMAC_HRB : (GMAC Offset: 0x080) Hash Register Bottom [31:0] -------- */ +#define GMAC_HRB_ADDR_Pos 0 +#define GMAC_HRB_ADDR_Msk ( 0xffffffffu << GMAC_HRB_ADDR_Pos ) /**< \brief (GMAC_HRB) Hash Address */ +#define GMAC_HRB_ADDR( value ) ( ( GMAC_HRB_ADDR_Msk & ( ( value ) << GMAC_HRB_ADDR_Pos ) ) ) +/* -------- GMAC_HRT : (GMAC Offset: 0x084) Hash Register Top [63:32] -------- */ +#define GMAC_HRT_ADDR_Pos 0 +#define GMAC_HRT_ADDR_Msk ( 0xffffffffu << GMAC_HRT_ADDR_Pos ) /**< \brief (GMAC_HRT) Hash Address */ +#define GMAC_HRT_ADDR( value ) ( ( GMAC_HRT_ADDR_Msk & ( ( value ) << GMAC_HRT_ADDR_Pos ) ) ) +/* -------- GMAC_SAB1 : (GMAC Offset: 0x088) Specific Address 1 Bottom [31:0] Register -------- */ +#define GMAC_SAB1_ADDR_Pos 0 +#define GMAC_SAB1_ADDR_Msk ( 0xffffffffu << GMAC_SAB1_ADDR_Pos ) /**< \brief (GMAC_SAB1) Specific Address 1 */ +#define GMAC_SAB1_ADDR( value ) ( ( GMAC_SAB1_ADDR_Msk & ( ( value ) << GMAC_SAB1_ADDR_Pos ) ) ) +/* -------- GMAC_SAT1 : (GMAC Offset: 0x08C) Specific Address 1 Top [47:32] Register -------- */ +#define GMAC_SAT1_ADDR_Pos 0 +#define GMAC_SAT1_ADDR_Msk ( 0xffffu << GMAC_SAT1_ADDR_Pos ) /**< \brief (GMAC_SAT1) Specific Address 1 */ +#define GMAC_SAT1_ADDR( value ) ( ( GMAC_SAT1_ADDR_Msk & ( ( value ) << GMAC_SAT1_ADDR_Pos ) ) ) +/* -------- GMAC_SAB2 : (GMAC Offset: 0x090) Specific Address 2 Bottom [31:0] Register -------- */ +#define GMAC_SAB2_ADDR_Pos 0 +#define GMAC_SAB2_ADDR_Msk ( 0xffffffffu << GMAC_SAB2_ADDR_Pos ) /**< \brief (GMAC_SAB2) Specific Address 2 */ +#define GMAC_SAB2_ADDR( value ) ( ( GMAC_SAB2_ADDR_Msk & ( ( value ) << GMAC_SAB2_ADDR_Pos ) ) ) +/* -------- GMAC_SAT2 : (GMAC Offset: 0x094) Specific Address 2 Top [47:32] Register -------- */ +#define GMAC_SAT2_ADDR_Pos 0 +#define GMAC_SAT2_ADDR_Msk ( 0xffffu << GMAC_SAT2_ADDR_Pos ) /**< \brief (GMAC_SAT2) Specific Address 2 */ +#define GMAC_SAT2_ADDR( value ) ( ( GMAC_SAT2_ADDR_Msk & ( ( value ) << GMAC_SAT2_ADDR_Pos ) ) ) +/* -------- GMAC_SAB3 : (GMAC Offset: 0x098) Specific Address 3 Bottom [31:0] Register -------- */ +#define GMAC_SAB3_ADDR_Pos 0 +#define GMAC_SAB3_ADDR_Msk ( 0xffffffffu << GMAC_SAB3_ADDR_Pos ) /**< \brief (GMAC_SAB3) Specific Address 3 */ +#define GMAC_SAB3_ADDR( value ) ( ( GMAC_SAB3_ADDR_Msk & ( ( value ) << GMAC_SAB3_ADDR_Pos ) ) ) +/* -------- GMAC_SAT3 : (GMAC Offset: 0x09C) Specific Address 3 Top [47:32] Register -------- */ +#define GMAC_SAT3_ADDR_Pos 0 +#define GMAC_SAT3_ADDR_Msk ( 0xffffu << GMAC_SAT3_ADDR_Pos ) /**< \brief (GMAC_SAT3) Specific Address 3 */ +#define GMAC_SAT3_ADDR( value ) ( ( GMAC_SAT3_ADDR_Msk & ( ( value ) << GMAC_SAT3_ADDR_Pos ) ) ) +/* -------- GMAC_SAB4 : (GMAC Offset: 0x0A0) Specific Address 4 Bottom [31:0] Register -------- */ +#define GMAC_SAB4_ADDR_Pos 0 +#define GMAC_SAB4_ADDR_Msk ( 0xffffffffu << GMAC_SAB4_ADDR_Pos ) /**< \brief (GMAC_SAB4) Specific Address 4 */ +#define GMAC_SAB4_ADDR( value ) ( ( GMAC_SAB4_ADDR_Msk & ( ( value ) << GMAC_SAB4_ADDR_Pos ) ) ) +/* -------- GMAC_SAT4 : (GMAC Offset: 0x0A4) Specific Address 4 Top [47:32] Register -------- */ +#define GMAC_SAT4_ADDR_Pos 0 +#define GMAC_SAT4_ADDR_Msk ( 0xffffu << GMAC_SAT4_ADDR_Pos ) /**< \brief (GMAC_SAT4) Specific Address 4 */ +#define GMAC_SAT4_ADDR( value ) ( ( GMAC_SAT4_ADDR_Msk & ( ( value ) << GMAC_SAT4_ADDR_Pos ) ) ) +/* -------- GMAC_TIDM[4] : (GMAC Offset: 0x0A8) Type ID Match 1 Register -------- */ +#define GMAC_TIDM_TID_Pos 0 +#define GMAC_TIDM_TID_Msk ( 0xffffu << GMAC_TIDM_TID_Pos ) /**< \brief (GMAC_TIDM[4]) Type ID Match 1 */ +#define GMAC_TIDM_TID( value ) ( ( GMAC_TIDM_TID_Msk & ( ( value ) << GMAC_TIDM_TID_Pos ) ) ) +/* -------- GMAC_WOL : (GMAC Offset: 0x0B8) Wake on LAN Register -------- */ +#define GMAC_WOL_IP_Pos 0 +#define GMAC_WOL_IP_Msk ( 0xffffu << GMAC_WOL_IP_Pos ) /**< \brief (GMAC_WOL) ARP Request IP Address */ +#define GMAC_WOL_IP( value ) ( ( GMAC_WOL_IP_Msk & ( ( value ) << GMAC_WOL_IP_Pos ) ) ) +#define GMAC_WOL_MAG ( 0x1u << 16 ) /**< \brief (GMAC_WOL) Magic Packet Event Enable */ +#define GMAC_WOL_ARP ( 0x1u << 17 ) /**< \brief (GMAC_WOL) ARP Request IP Address */ +#define GMAC_WOL_SA1 ( 0x1u << 18 ) /**< \brief (GMAC_WOL) Specific Address Register 1 Event Enable */ +#define GMAC_WOL_MTI ( 0x1u << 19 ) /**< \brief (GMAC_WOL) Multicast Hash Event Enable */ +/* -------- GMAC_IPGS : (GMAC Offset: 0x0BC) IPG Stretch Register -------- */ +#define GMAC_IPGS_FL_Pos 0 +#define GMAC_IPGS_FL_Msk ( 0xffffu << GMAC_IPGS_FL_Pos ) /**< \brief (GMAC_IPGS) Frame Length */ +#define GMAC_IPGS_FL( value ) ( ( GMAC_IPGS_FL_Msk & ( ( value ) << GMAC_IPGS_FL_Pos ) ) ) +/* -------- GMAC_SVLAN : (GMAC Offset: 0x0C0) Stacked VLAN Register -------- */ +#define GMAC_SVLAN_VLAN_TYPE_Pos 0 +#define GMAC_SVLAN_VLAN_TYPE_Msk ( 0xffffu << GMAC_SVLAN_VLAN_TYPE_Pos ) /**< \brief (GMAC_SVLAN) User Defined VLAN_TYPE Field */ +#define GMAC_SVLAN_VLAN_TYPE( value ) ( ( GMAC_SVLAN_VLAN_TYPE_Msk & ( ( value ) << GMAC_SVLAN_VLAN_TYPE_Pos ) ) ) +#define GMAC_SVLAN_ESVLAN ( 0x1u << 31 ) /**< \brief (GMAC_SVLAN) Enable Stacked VLAN Processing Mode */ +/* -------- GMAC_TPFCP : (GMAC Offset: 0x0C4) Transmit PFC Pause Register -------- */ +#define GMAC_TPFCP_PEV_Pos 0 +#define GMAC_TPFCP_PEV_Msk ( 0xffu << GMAC_TPFCP_PEV_Pos ) /**< \brief (GMAC_TPFCP) Priority Enable Vector */ +#define GMAC_TPFCP_PEV( value ) ( ( GMAC_TPFCP_PEV_Msk & ( ( value ) << GMAC_TPFCP_PEV_Pos ) ) ) +#define GMAC_TPFCP_PQ_Pos 8 +#define GMAC_TPFCP_PQ_Msk ( 0xffu << GMAC_TPFCP_PQ_Pos ) /**< \brief (GMAC_TPFCP) Pause Quantum */ +#define GMAC_TPFCP_PQ( value ) ( ( GMAC_TPFCP_PQ_Msk & ( ( value ) << GMAC_TPFCP_PQ_Pos ) ) ) +/* -------- GMAC_SAMB1 : (GMAC Offset: 0x0C8) Specific Address 1 Mask Bottom [31:0] Register -------- */ +#define GMAC_SAMB1_ADDR_Pos 0 +#define GMAC_SAMB1_ADDR_Msk ( 0xffffffffu << GMAC_SAMB1_ADDR_Pos ) /**< \brief (GMAC_SAMB1) Specific Address 1 Mask */ +#define GMAC_SAMB1_ADDR( value ) ( ( GMAC_SAMB1_ADDR_Msk & ( ( value ) << GMAC_SAMB1_ADDR_Pos ) ) ) +/* -------- GMAC_SAMT1 : (GMAC Offset: 0x0CC) Specific Address 1 Mask Top [47:32] Register -------- */ +#define GMAC_SAMT1_ADDR_Pos 0 +#define GMAC_SAMT1_ADDR_Msk ( 0xffffu << GMAC_SAMT1_ADDR_Pos ) /**< \brief (GMAC_SAMT1) Specific Address 1 Mask */ +#define GMAC_SAMT1_ADDR( value ) ( ( GMAC_SAMT1_ADDR_Msk & ( ( value ) << GMAC_SAMT1_ADDR_Pos ) ) ) +/* -------- GMAC_OTLO : (GMAC Offset: 0x100) Octets Transmitted [31:0] Register -------- */ +#define GMAC_OTLO_TXO_Pos 0 +#define GMAC_OTLO_TXO_Msk ( 0xffffffffu << GMAC_OTLO_TXO_Pos ) /**< \brief (GMAC_OTLO) Transmitted Octets */ +/* -------- GMAC_OTHI : (GMAC Offset: 0x104) Octets Transmitted [47:32] Register -------- */ +#define GMAC_OTHI_TXO_Pos 0 +#define GMAC_OTHI_TXO_Msk ( 0xffffu << GMAC_OTHI_TXO_Pos ) /**< \brief (GMAC_OTHI) Transmitted Octets */ +/* -------- GMAC_FT : (GMAC Offset: 0x108) Frames Transmitted Register -------- */ +#define GMAC_FT_FTX_Pos 0 +#define GMAC_FT_FTX_Msk ( 0xffffffffu << GMAC_FT_FTX_Pos ) /**< \brief (GMAC_FT) Frames Transmitted without Error */ +/* -------- GMAC_BCFT : (GMAC Offset: 0x10C) Broadcast Frames Transmitted Register -------- */ +#define GMAC_BCFT_BFTX_Pos 0 +#define GMAC_BCFT_BFTX_Msk ( 0xffffffffu << GMAC_BCFT_BFTX_Pos ) /**< \brief (GMAC_BCFT) Broadcast Frames Transmitted without Error */ +/* -------- GMAC_MFT : (GMAC Offset: 0x110) Multicast Frames Transmitted Register -------- */ +#define GMAC_MFT_MFTX_Pos 0 +#define GMAC_MFT_MFTX_Msk ( 0xffffffffu << GMAC_MFT_MFTX_Pos ) /**< \brief (GMAC_MFT) Multicast Frames Transmitted without Error */ +/* -------- GMAC_PFT : (GMAC Offset: 0x114) Pause Frames Transmitted Register -------- */ +#define GMAC_PFT_PFTX_Pos 0 +#define GMAC_PFT_PFTX_Msk ( 0xffffu << GMAC_PFT_PFTX_Pos ) /**< \brief (GMAC_PFT) Pause Frames Transmitted Register */ +/* -------- GMAC_BFT64 : (GMAC Offset: 0x118) 64 Byte Frames Transmitted Register -------- */ +#define GMAC_BFT64_NFTX_Pos 0 +#define GMAC_BFT64_NFTX_Msk ( 0xffffffffu << GMAC_BFT64_NFTX_Pos ) /**< \brief (GMAC_BFT64) 64 Byte Frames Transmitted without Error */ +/* -------- GMAC_TBFT127 : (GMAC Offset: 0x11C) 65 to 127 Byte Frames Transmitted Register -------- */ +#define GMAC_TBFT127_NFTX_Pos 0 +#define GMAC_TBFT127_NFTX_Msk ( 0xffffffffu << GMAC_TBFT127_NFTX_Pos ) /**< \brief (GMAC_TBFT127) 65 to 127 Byte Frames Transmitted without Error */ +/* -------- GMAC_TBFT255 : (GMAC Offset: 0x120) 128 to 255 Byte Frames Transmitted Register -------- */ +#define GMAC_TBFT255_NFTX_Pos 0 +#define GMAC_TBFT255_NFTX_Msk ( 0xffffffffu << GMAC_TBFT255_NFTX_Pos ) /**< \brief (GMAC_TBFT255) 128 to 255 Byte Frames Transmitted without Error */ +/* -------- GMAC_TBFT511 : (GMAC Offset: 0x124) 256 to 511 Byte Frames Transmitted Register -------- */ +#define GMAC_TBFT511_NFTX_Pos 0 +#define GMAC_TBFT511_NFTX_Msk ( 0xffffffffu << GMAC_TBFT511_NFTX_Pos ) /**< \brief (GMAC_TBFT511) 256 to 511 Byte Frames Transmitted without Error */ +/* -------- GMAC_TBFT1023 : (GMAC Offset: 0x128) 512 to 1023 Byte Frames Transmitted Register -------- */ +#define GMAC_TBFT1023_NFTX_Pos 0 +#define GMAC_TBFT1023_NFTX_Msk ( 0xffffffffu << GMAC_TBFT1023_NFTX_Pos ) /**< \brief (GMAC_TBFT1023) 512 to 1023 Byte Frames Transmitted without Error */ +/* -------- GMAC_TBFT1518 : (GMAC Offset: 0x12C) 1024 to 1518 Byte Frames Transmitted Register -------- */ +#define GMAC_TBFT1518_NFTX_Pos 0 +#define GMAC_TBFT1518_NFTX_Msk ( 0xffffffffu << GMAC_TBFT1518_NFTX_Pos ) /**< \brief (GMAC_TBFT1518) 1024 to 1518 Byte Frames Transmitted without Error */ +/* -------- GMAC_GTBFT1518 : (GMAC Offset: 0x130) Greater Than 1518 Byte Frames Transmitted Register -------- */ +#define GMAC_GTBFT1518_NFTX_Pos 0 +#define GMAC_GTBFT1518_NFTX_Msk ( 0xffffffffu << GMAC_GTBFT1518_NFTX_Pos ) /**< \brief (GMAC_GTBFT1518) Greater than 1518 Byte Frames Transmitted without Error */ +/* -------- GMAC_TUR : (GMAC Offset: 0x134) Transmit Under Runs Register -------- */ +#define GMAC_TUR_TXUNR_Pos 0 +#define GMAC_TUR_TXUNR_Msk ( 0x3ffu << GMAC_TUR_TXUNR_Pos ) /**< \brief (GMAC_TUR) Transmit Under Runs */ +/* -------- GMAC_SCF : (GMAC Offset: 0x138) Single Collision Frames Register -------- */ +#define GMAC_SCF_SCOL_Pos 0 +#define GMAC_SCF_SCOL_Msk ( 0x3ffffu << GMAC_SCF_SCOL_Pos ) /**< \brief (GMAC_SCF) Single Collision */ +/* -------- GMAC_MCF : (GMAC Offset: 0x13C) Multiple Collision Frames Register -------- */ +#define GMAC_MCF_MCOL_Pos 0 +#define GMAC_MCF_MCOL_Msk ( 0x3ffffu << GMAC_MCF_MCOL_Pos ) /**< \brief (GMAC_MCF) Multiple Collision */ +/* -------- GMAC_EC : (GMAC Offset: 0x140) Excessive Collisions Register -------- */ +#define GMAC_EC_XCOL_Pos 0 +#define GMAC_EC_XCOL_Msk ( 0x3ffu << GMAC_EC_XCOL_Pos ) /**< \brief (GMAC_EC) Excessive Collisions */ +/* -------- GMAC_LC : (GMAC Offset: 0x144) Late Collisions Register -------- */ +#define GMAC_LC_LCOL_Pos 0 +#define GMAC_LC_LCOL_Msk ( 0x3ffu << GMAC_LC_LCOL_Pos ) /**< \brief (GMAC_LC) Late Collisions */ +/* -------- GMAC_DTF : (GMAC Offset: 0x148) Deferred Transmission Frames Register -------- */ +#define GMAC_DTF_DEFT_Pos 0 +#define GMAC_DTF_DEFT_Msk ( 0x3ffffu << GMAC_DTF_DEFT_Pos ) /**< \brief (GMAC_DTF) Deferred Transmission */ +/* -------- GMAC_CSE : (GMAC Offset: 0x14C) Carrier Sense Errors Register -------- */ +#define GMAC_CSE_CSR_Pos 0 +#define GMAC_CSE_CSR_Msk ( 0x3ffu << GMAC_CSE_CSR_Pos ) /**< \brief (GMAC_CSE) Carrier Sense Error */ +/* -------- GMAC_ORLO : (GMAC Offset: 0x150) Octets Received [31:0] Received -------- */ +#define GMAC_ORLO_RXO_Pos 0 +#define GMAC_ORLO_RXO_Msk ( 0xffffffffu << GMAC_ORLO_RXO_Pos ) /**< \brief (GMAC_ORLO) Received Octets */ +/* -------- GMAC_ORHI : (GMAC Offset: 0x154) Octets Received [47:32] Received -------- */ +#define GMAC_ORHI_RXO_Pos 0 +#define GMAC_ORHI_RXO_Msk ( 0xffffu << GMAC_ORHI_RXO_Pos ) /**< \brief (GMAC_ORHI) Received Octets */ +/* -------- GMAC_FR : (GMAC Offset: 0x158) Frames Received Register -------- */ +#define GMAC_FR_FRX_Pos 0 +#define GMAC_FR_FRX_Msk ( 0xffffffffu << GMAC_FR_FRX_Pos ) /**< \brief (GMAC_FR) Frames Received without Error */ +/* -------- GMAC_BCFR : (GMAC Offset: 0x15C) Broadcast Frames Received Register -------- */ +#define GMAC_BCFR_BFRX_Pos 0 +#define GMAC_BCFR_BFRX_Msk ( 0xffffffffu << GMAC_BCFR_BFRX_Pos ) /**< \brief (GMAC_BCFR) Broadcast Frames Received without Error */ +/* -------- GMAC_MFR : (GMAC Offset: 0x160) Multicast Frames Received Register -------- */ +#define GMAC_MFR_MFRX_Pos 0 +#define GMAC_MFR_MFRX_Msk ( 0xffffffffu << GMAC_MFR_MFRX_Pos ) /**< \brief (GMAC_MFR) Multicast Frames Received without Error */ +/* -------- GMAC_PFR : (GMAC Offset: 0x164) Pause Frames Received Register -------- */ +#define GMAC_PFR_PFRX_Pos 0 +#define GMAC_PFR_PFRX_Msk ( 0xffffu << GMAC_PFR_PFRX_Pos ) /**< \brief (GMAC_PFR) Pause Frames Received Register */ +/* -------- GMAC_BFR64 : (GMAC Offset: 0x168) 64 Byte Frames Received Register -------- */ +#define GMAC_BFR64_NFRX_Pos 0 +#define GMAC_BFR64_NFRX_Msk ( 0xffffffffu << GMAC_BFR64_NFRX_Pos ) /**< \brief (GMAC_BFR64) 64 Byte Frames Received without Error */ +/* -------- GMAC_TBFR127 : (GMAC Offset: 0x16C) 65 to 127 Byte Frames Received Register -------- */ +#define GMAC_TBFR127_NFRX_Pos 0 +#define GMAC_TBFR127_NFRX_Msk ( 0xffffffffu << GMAC_TBFR127_NFRX_Pos ) /**< \brief (GMAC_TBFR127) 65 to 127 Byte Frames Received without Error */ +/* -------- GMAC_TBFR255 : (GMAC Offset: 0x170) 128 to 255 Byte Frames Received Register -------- */ +#define GMAC_TBFR255_NFRX_Pos 0 +#define GMAC_TBFR255_NFRX_Msk ( 0xffffffffu << GMAC_TBFR255_NFRX_Pos ) /**< \brief (GMAC_TBFR255) 128 to 255 Byte Frames Received without Error */ +/* -------- GMAC_TBFR511 : (GMAC Offset: 0x174) 256 to 511Byte Frames Received Register -------- */ +#define GMAC_TBFR511_NFRX_Pos 0 +#define GMAC_TBFR511_NFRX_Msk ( 0xffffffffu << GMAC_TBFR511_NFRX_Pos ) /**< \brief (GMAC_TBFR511) 256 to 511 Byte Frames Received without Error */ +/* -------- GMAC_TBFR1023 : (GMAC Offset: 0x178) 512 to 1023 Byte Frames Received Register -------- */ +#define GMAC_TBFR1023_NFRX_Pos 0 +#define GMAC_TBFR1023_NFRX_Msk ( 0xffffffffu << GMAC_TBFR1023_NFRX_Pos ) /**< \brief (GMAC_TBFR1023) 512 to 1023 Byte Frames Received without Error */ +/* -------- GMAC_TBFR1518 : (GMAC Offset: 0x17C) 1024 to 1518 Byte Frames Received Register -------- */ +#define GMAC_TBFR1518_NFRX_Pos 0 +#define GMAC_TBFR1518_NFRX_Msk ( 0xffffffffu << GMAC_TBFR1518_NFRX_Pos ) /**< \brief (GMAC_TBFR1518) 1024 to 1518 Byte Frames Received without Error */ +/* -------- GMAC_TMXBFR : (GMAC Offset: 0x180) 1519 to Maximum Byte Frames Received Register -------- */ +#define GMAC_TMXBFR_NFRX_Pos 0 +#define GMAC_TMXBFR_NFRX_Msk ( 0xffffffffu << GMAC_TMXBFR_NFRX_Pos ) /**< \brief (GMAC_TMXBFR) 1519 to Maximum Byte Frames Received without Error */ +/* -------- GMAC_UFR : (GMAC Offset: 0x184) Undersize Frames Received Register -------- */ +#define GMAC_UFR_UFRX_Pos 0 +#define GMAC_UFR_UFRX_Msk ( 0x3ffu << GMAC_UFR_UFRX_Pos ) /**< \brief (GMAC_UFR) Undersize Frames Received */ +/* -------- GMAC_OFR : (GMAC Offset: 0x188) Oversize Frames Received Register -------- */ +#define GMAC_OFR_OFRX_Pos 0 +#define GMAC_OFR_OFRX_Msk ( 0x3ffu << GMAC_OFR_OFRX_Pos ) /**< \brief (GMAC_OFR) Oversized Frames Received */ +/* -------- GMAC_JR : (GMAC Offset: 0x18C) Jabbers Received Register -------- */ +#define GMAC_JR_JRX_Pos 0 +#define GMAC_JR_JRX_Msk ( 0x3ffu << GMAC_JR_JRX_Pos ) /**< \brief (GMAC_JR) Jabbers Received */ +/* -------- GMAC_FCSE : (GMAC Offset: 0x190) Frame Check Sequence Errors Register -------- */ +#define GMAC_FCSE_FCKR_Pos 0 +#define GMAC_FCSE_FCKR_Msk ( 0x3ffu << GMAC_FCSE_FCKR_Pos ) /**< \brief (GMAC_FCSE) Frame Check Sequence Errors */ +/* -------- GMAC_LFFE : (GMAC Offset: 0x194) Length Field Frame Errors Register -------- */ +#define GMAC_LFFE_LFER_Pos 0 +#define GMAC_LFFE_LFER_Msk ( 0x3ffu << GMAC_LFFE_LFER_Pos ) /**< \brief (GMAC_LFFE) Length Field Frame Errors */ +/* -------- GMAC_RSE : (GMAC Offset: 0x198) Receive Symbol Errors Register -------- */ +#define GMAC_RSE_RXSE_Pos 0 +#define GMAC_RSE_RXSE_Msk ( 0x3ffu << GMAC_RSE_RXSE_Pos ) /**< \brief (GMAC_RSE) Receive Symbol Errors */ +/* -------- GMAC_AE : (GMAC Offset: 0x19C) Alignment Errors Register -------- */ +#define GMAC_AE_AER_Pos 0 +#define GMAC_AE_AER_Msk ( 0x3ffu << GMAC_AE_AER_Pos ) /**< \brief (GMAC_AE) Alignment Errors */ +/* -------- GMAC_RRE : (GMAC Offset: 0x1A0) Receive Resource Errors Register -------- */ +#define GMAC_RRE_RXRER_Pos 0 +#define GMAC_RRE_RXRER_Msk ( 0x3ffffu << GMAC_RRE_RXRER_Pos ) /**< \brief (GMAC_RRE) Receive Resource Errors */ +/* -------- GMAC_ROE : (GMAC Offset: 0x1A4) Receive Overrun Register -------- */ +#define GMAC_ROE_RXOVR_Pos 0 +#define GMAC_ROE_RXOVR_Msk ( 0x3ffu << GMAC_ROE_RXOVR_Pos ) /**< \brief (GMAC_ROE) Receive Overruns */ +/* -------- GMAC_IHCE : (GMAC Offset: 0x1A8) IP Header Checksum Errors Register -------- */ +#define GMAC_IHCE_HCKER_Pos 0 +#define GMAC_IHCE_HCKER_Msk ( 0xffu << GMAC_IHCE_HCKER_Pos ) /**< \brief (GMAC_IHCE) IP Header Checksum Errors */ +/* -------- GMAC_TCE : (GMAC Offset: 0x1AC) TCP Checksum Errors Register -------- */ +#define GMAC_TCE_TCKER_Pos 0 +#define GMAC_TCE_TCKER_Msk ( 0xffu << GMAC_TCE_TCKER_Pos ) /**< \brief (GMAC_TCE) TCP Checksum Errors */ +/* -------- GMAC_UCE : (GMAC Offset: 0x1B0) UDP Checksum Errors Register -------- */ +#define GMAC_UCE_UCKER_Pos 0 +#define GMAC_UCE_UCKER_Msk ( 0xffu << GMAC_UCE_UCKER_Pos ) /**< \brief (GMAC_UCE) UDP Checksum Errors */ +/* -------- GMAC_TSSS : (GMAC Offset: 0x1C8) 1588 Timer Sync Strobe Seconds Register -------- */ +#define GMAC_TSSS_VTS_Pos 0 +#define GMAC_TSSS_VTS_Msk ( 0xffffffffu << GMAC_TSSS_VTS_Pos ) /**< \brief (GMAC_TSSS) Value of Timer Seconds Register Capture */ +#define GMAC_TSSS_VTS( value ) ( ( GMAC_TSSS_VTS_Msk & ( ( value ) << GMAC_TSSS_VTS_Pos ) ) ) +/* -------- GMAC_TSSN : (GMAC Offset: 0x1CC) 1588 Timer Sync Strobe Nanoseconds Register -------- */ +#define GMAC_TSSN_VTN_Pos 0 +#define GMAC_TSSN_VTN_Msk ( 0x3fffffffu << GMAC_TSSN_VTN_Pos ) /**< \brief (GMAC_TSSN) Value Timer Nanoseconds Register Capture */ +#define GMAC_TSSN_VTN( value ) ( ( GMAC_TSSN_VTN_Msk & ( ( value ) << GMAC_TSSN_VTN_Pos ) ) ) +/* -------- GMAC_TS : (GMAC Offset: 0x1D0) 1588 Timer Seconds Register -------- */ +#define GMAC_TS_TCS_Pos 0 +#define GMAC_TS_TCS_Msk ( 0xffffffffu << GMAC_TS_TCS_Pos ) /**< \brief (GMAC_TS) Timer Count in Seconds */ +#define GMAC_TS_TCS( value ) ( ( GMAC_TS_TCS_Msk & ( ( value ) << GMAC_TS_TCS_Pos ) ) ) +/* -------- GMAC_TN : (GMAC Offset: 0x1D4) 1588 Timer Nanoseconds Register -------- */ +#define GMAC_TN_TNS_Pos 0 +#define GMAC_TN_TNS_Msk ( 0x3fffffffu << GMAC_TN_TNS_Pos ) /**< \brief (GMAC_TN) Timer Count in Nanoseconds */ +#define GMAC_TN_TNS( value ) ( ( GMAC_TN_TNS_Msk & ( ( value ) << GMAC_TN_TNS_Pos ) ) ) +/* -------- GMAC_TA : (GMAC Offset: 0x1D8) 1588 Timer Adjust Register -------- */ +#define GMAC_TA_ITDT_Pos 0 +#define GMAC_TA_ITDT_Msk ( 0x3fffffffu << GMAC_TA_ITDT_Pos ) /**< \brief (GMAC_TA) Increment/Decrement */ +#define GMAC_TA_ITDT( value ) ( ( GMAC_TA_ITDT_Msk & ( ( value ) << GMAC_TA_ITDT_Pos ) ) ) +#define GMAC_TA_ADJ ( 0x1u << 31 ) /**< \brief (GMAC_TA) Adjust 1588 Timer */ +/* -------- GMAC_TI : (GMAC Offset: 0x1DC) 1588 Timer Increment Register -------- */ +#define GMAC_TI_CNS_Pos 0 +#define GMAC_TI_CNS_Msk ( 0xffu << GMAC_TI_CNS_Pos ) /**< \brief (GMAC_TI) Count Nanoseconds */ +#define GMAC_TI_CNS( value ) ( ( GMAC_TI_CNS_Msk & ( ( value ) << GMAC_TI_CNS_Pos ) ) ) +#define GMAC_TI_ACNS_Pos 8 +#define GMAC_TI_ACNS_Msk ( 0xffu << GMAC_TI_ACNS_Pos ) /**< \brief (GMAC_TI) Alternative Count Nanoseconds */ +#define GMAC_TI_ACNS( value ) ( ( GMAC_TI_ACNS_Msk & ( ( value ) << GMAC_TI_ACNS_Pos ) ) ) +#define GMAC_TI_NIT_Pos 16 +#define GMAC_TI_NIT_Msk ( 0xffu << GMAC_TI_NIT_Pos ) /**< \brief (GMAC_TI) Number of Increments */ +#define GMAC_TI_NIT( value ) ( ( GMAC_TI_NIT_Msk & ( ( value ) << GMAC_TI_NIT_Pos ) ) ) +/* -------- GMAC_EFTS : (GMAC Offset: 0x1E0) PTP Event Frame Transmitted Seconds -------- */ +#define GMAC_EFTS_RUD_Pos 0 +#define GMAC_EFTS_RUD_Msk ( 0xffffffffu << GMAC_EFTS_RUD_Pos ) /**< \brief (GMAC_EFTS) Register Update */ +/* -------- GMAC_EFTN : (GMAC Offset: 0x1E4) PTP Event Frame Transmitted Nanoseconds -------- */ +#define GMAC_EFTN_RUD_Pos 0 +#define GMAC_EFTN_RUD_Msk ( 0x3fffffffu << GMAC_EFTN_RUD_Pos ) /**< \brief (GMAC_EFTN) Register Update */ +/* -------- GMAC_EFRS : (GMAC Offset: 0x1E8) PTP Event Frame Received Seconds -------- */ +#define GMAC_EFRS_RUD_Pos 0 +#define GMAC_EFRS_RUD_Msk ( 0xffffffffu << GMAC_EFRS_RUD_Pos ) /**< \brief (GMAC_EFRS) Register Update */ +/* -------- GMAC_EFRN : (GMAC Offset: 0x1EC) PTP Event Frame Received Nanoseconds -------- */ +#define GMAC_EFRN_RUD_Pos 0 +#define GMAC_EFRN_RUD_Msk ( 0x3fffffffu << GMAC_EFRN_RUD_Pos ) /**< \brief (GMAC_EFRN) Register Update */ +/* -------- GMAC_PEFTS : (GMAC Offset: 0x1F0) PTP Peer Event Frame Transmitted Seconds -------- */ +#define GMAC_PEFTS_RUD_Pos 0 +#define GMAC_PEFTS_RUD_Msk ( 0xffffffffu << GMAC_PEFTS_RUD_Pos ) /**< \brief (GMAC_PEFTS) Register Update */ +/* -------- GMAC_PEFTN : (GMAC Offset: 0x1F4) PTP Peer Event Frame Transmitted Nanoseconds -------- */ +#define GMAC_PEFTN_RUD_Pos 0 +#define GMAC_PEFTN_RUD_Msk ( 0x3fffffffu << GMAC_PEFTN_RUD_Pos ) /**< \brief (GMAC_PEFTN) Register Update */ +/* -------- GMAC_PEFRS : (GMAC Offset: 0x1F8) PTP Peer Event Frame Received Seconds -------- */ +#define GMAC_PEFRS_RUD_Pos 0 +#define GMAC_PEFRS_RUD_Msk ( 0xffffffffu << GMAC_PEFRS_RUD_Pos ) /**< \brief (GMAC_PEFRS) Register Update */ +/* -------- GMAC_PEFRN : (GMAC Offset: 0x1FC) PTP Peer Event Frame Received Nanoseconds -------- */ +#define GMAC_PEFRN_RUD_Pos 0 +#define GMAC_PEFRN_RUD_Msk ( 0x3fffffffu << GMAC_PEFRN_RUD_Pos ) /**< \brief (GMAC_PEFRN) Register Update */ +/* -------- GMAC_ISRPQ[7] : (GMAC Offset: 0x400) Interrupt Status Register Priority Queue -------- */ +#define GMAC_ISRPQ_RCOMP ( 0x1u << 1 ) /**< \brief (GMAC_ISRPQ[7]) Receive Complete */ +#define GMAC_ISRPQ_RXUBR ( 0x1u << 2 ) /**< \brief (GMAC_ISRPQ[7]) RX Used Bit Read */ +#define GMAC_ISRPQ_RLEX ( 0x1u << 5 ) /**< \brief (GMAC_ISRPQ[7]) Retry Limit Exceeded or Late Collision */ +#define GMAC_ISRPQ_TFC ( 0x1u << 6 ) /**< \brief (GMAC_ISRPQ[7]) Transmit Frame Corruption due to AHB error */ +#define GMAC_ISRPQ_TCOMP ( 0x1u << 7 ) /**< \brief (GMAC_ISRPQ[7]) Transmit Complete */ +#define GMAC_ISRPQ_ROVR ( 0x1u << 10 ) /**< \brief (GMAC_ISRPQ[7]) Receive Overrun */ +#define GMAC_ISRPQ_HRESP ( 0x1u << 11 ) /**< \brief (GMAC_ISRPQ[7]) HRESP Not OK */ +/* -------- GMAC_TBQBAPQ[7] : (GMAC Offset: 0x440) Transmit Buffer Queue Base Address Priority Queue -------- */ +#define GMAC_TBQBAPQ_TXBQBA_Pos 2 +#define GMAC_TBQBAPQ_TXBQBA_Msk ( 0x3fu << GMAC_TBQBAPQ_TXBQBA_Pos ) /**< \brief (GMAC_TBQBAPQ[7]) Transmit Buffer Queue Base Address */ +#define GMAC_TBQBAPQ_TXBQBA( value ) ( ( GMAC_TBQBAPQ_TXBQBA_Msk & ( ( value ) << GMAC_TBQBAPQ_TXBQBA_Pos ) ) ) +/* -------- GMAC_RBQBAPQ[7] : (GMAC Offset: 0x480) Receive Buffer Queue Base Address Priority Queue -------- */ +#define GMAC_RBQBAPQ_RXBQBA_Pos 2 +#define GMAC_RBQBAPQ_RXBQBA_Msk ( 0x3fu << GMAC_RBQBAPQ_RXBQBA_Pos ) /**< \brief (GMAC_RBQBAPQ[7]) Receive Buffer Queue Base Address */ +#define GMAC_RBQBAPQ_RXBQBA( value ) ( ( GMAC_RBQBAPQ_RXBQBA_Msk & ( ( value ) << GMAC_RBQBAPQ_RXBQBA_Pos ) ) ) +/* -------- GMAC_RBSRPQ[7] : (GMAC Offset: 0x4A0) Receive Buffer Size Register Priority Queue -------- */ +#define GMAC_RBSRPQ_RBS_Pos 0 +#define GMAC_RBSRPQ_RBS_Msk ( 0xffffu << GMAC_RBSRPQ_RBS_Pos ) /**< \brief (GMAC_RBSRPQ[7]) Receive Buffer Size */ +#define GMAC_RBSRPQ_RBS( value ) ( ( GMAC_RBSRPQ_RBS_Msk & ( ( value ) << GMAC_RBSRPQ_RBS_Pos ) ) ) +/* -------- GMAC_ST1RPQ[16] : (GMAC Offset: 0x500) Screening Type1 Register Priority Queue -------- */ +#define GMAC_ST1RPQ_QNB_Pos 0 +#define GMAC_ST1RPQ_QNB_Msk ( 0xfu << GMAC_ST1RPQ_QNB_Pos ) /**< \brief (GMAC_ST1RPQ[16]) Que Number (0->7) */ +#define GMAC_ST1RPQ_QNB( value ) ( ( GMAC_ST1RPQ_QNB_Msk & ( ( value ) << GMAC_ST1RPQ_QNB_Pos ) ) ) +#define GMAC_ST1RPQ_DSTCM_Pos 4 +#define GMAC_ST1RPQ_DSTCM_Msk ( 0xffu << GMAC_ST1RPQ_DSTCM_Pos ) /**< \brief (GMAC_ST1RPQ[16]) Differentiated Services or Traffic Class Match */ +#define GMAC_ST1RPQ_DSTCM( value ) ( ( GMAC_ST1RPQ_DSTCM_Msk & ( ( value ) << GMAC_ST1RPQ_DSTCM_Pos ) ) ) +#define GMAC_ST1RPQ_UDPM_Pos 12 +#define GMAC_ST1RPQ_UDPM_Msk ( 0xffffu << GMAC_ST1RPQ_UDPM_Pos ) /**< \brief (GMAC_ST1RPQ[16]) UDP Port Match */ +#define GMAC_ST1RPQ_UDPM( value ) ( ( GMAC_ST1RPQ_UDPM_Msk & ( ( value ) << GMAC_ST1RPQ_UDPM_Pos ) ) ) +#define GMAC_ST1RPQ_DSTCE ( 0x1u << 28 ) /**< \brief (GMAC_ST1RPQ[16]) Differentiated Services or Traffic Class Match Enable */ +#define GMAC_ST1RPQ_UDPE ( 0x1u << 29 ) /**< \brief (GMAC_ST1RPQ[16]) UDP Port Match Enable */ +/* -------- GMAC_ST2RPQ[16] : (GMAC Offset: 0x540) Screening Type2 Register Priority Queue -------- */ +#define GMAC_ST2RPQ_QNB_Pos 0 +#define GMAC_ST2RPQ_QNB_Msk ( 0xfu << GMAC_ST2RPQ_QNB_Pos ) /**< \brief (GMAC_ST2RPQ[16]) Que Number (0->7) */ +#define GMAC_ST2RPQ_QNB( value ) ( ( GMAC_ST2RPQ_QNB_Msk & ( ( value ) << GMAC_ST2RPQ_QNB_Pos ) ) ) +#define GMAC_ST2RPQ_VLANP_Pos 4 +#define GMAC_ST2RPQ_VLANP_Msk ( 0xfu << GMAC_ST2RPQ_VLANP_Pos ) /**< \brief (GMAC_ST2RPQ[16]) VLAN Priority */ +#define GMAC_ST2RPQ_VLANP( value ) ( ( GMAC_ST2RPQ_VLANP_Msk & ( ( value ) << GMAC_ST2RPQ_VLANP_Pos ) ) ) +#define GMAC_ST2RPQ_VLANE ( 0x1u << 8 ) /**< \brief (GMAC_ST2RPQ[16]) VLAN Enable */ +/* -------- GMAC_IERPQ[7] : (GMAC Offset: 0x600) Interrupt Enable Register Priority Queue -------- */ +#define GMAC_IERPQ_RCOMP ( 0x1u << 1 ) /**< \brief (GMAC_IERPQ[7]) Receive Complete */ +#define GMAC_IERPQ_RXUBR ( 0x1u << 2 ) /**< \brief (GMAC_IERPQ[7]) RX Used Bit Read */ +#define GMAC_IERPQ_RLEX ( 0x1u << 5 ) /**< \brief (GMAC_IERPQ[7]) Retry Limit Exceeded or Late Collision */ +#define GMAC_IERPQ_TFC ( 0x1u << 6 ) /**< \brief (GMAC_IERPQ[7]) Transmit Frame Corruption due to AHB error */ +#define GMAC_IERPQ_TCOMP ( 0x1u << 7 ) /**< \brief (GMAC_IERPQ[7]) Transmit Complete */ +#define GMAC_IERPQ_ROVR ( 0x1u << 10 ) /**< \brief (GMAC_IERPQ[7]) Receive Overrun */ +#define GMAC_IERPQ_HRESP ( 0x1u << 11 ) /**< \brief (GMAC_IERPQ[7]) HRESP Not OK */ +/* -------- GMAC_IDRPQ[7] : (GMAC Offset: 0x620) Interrupt Disable Register Priority Queue -------- */ +#define GMAC_IDRPQ_RCOMP ( 0x1u << 1 ) /**< \brief (GMAC_IDRPQ[7]) Receive Complete */ +#define GMAC_IDRPQ_RXUBR ( 0x1u << 2 ) /**< \brief (GMAC_IDRPQ[7]) RX Used Bit Read */ +#define GMAC_IDRPQ_RLEX ( 0x1u << 5 ) /**< \brief (GMAC_IDRPQ[7]) Retry Limit Exceeded or Late Collision */ +#define GMAC_IDRPQ_TFC ( 0x1u << 6 ) /**< \brief (GMAC_IDRPQ[7]) Transmit Frame Corruption due to AHB error */ +#define GMAC_IDRPQ_TCOMP ( 0x1u << 7 ) /**< \brief (GMAC_IDRPQ[7]) Transmit Complete */ +#define GMAC_IDRPQ_ROVR ( 0x1u << 10 ) /**< \brief (GMAC_IDRPQ[7]) Receive Overrun */ +#define GMAC_IDRPQ_HRESP ( 0x1u << 11 ) /**< \brief (GMAC_IDRPQ[7]) HRESP Not OK */ +/* -------- GMAC_IMRPQ[7] : (GMAC Offset: 0x640) Interrupt Mask Register Priority Queue -------- */ +#define GMAC_IMRPQ_RCOMP ( 0x1u << 1 ) /**< \brief (GMAC_IMRPQ[7]) Receive Complete */ +#define GMAC_IMRPQ_RXUBR ( 0x1u << 2 ) /**< \brief (GMAC_IMRPQ[7]) RX Used Bit Read */ +#define GMAC_IMRPQ_RLEX ( 0x1u << 5 ) /**< \brief (GMAC_IMRPQ[7]) Retry Limit Exceeded or Late Collision */ +#define GMAC_IMRPQ_AHB ( 0x1u << 6 ) /**< \brief (GMAC_IMRPQ[7]) AHB Error */ +#define GMAC_IMRPQ_TCOMP ( 0x1u << 7 ) /**< \brief (GMAC_IMRPQ[7]) Transmit Complete */ +#define GMAC_IMRPQ_ROVR ( 0x1u << 10 ) /**< \brief (GMAC_IMRPQ[7]) Receive Overrun */ +#define GMAC_IMRPQ_HRESP ( 0x1u << 11 ) /**< \brief (GMAC_IMRPQ[7]) HRESP Not OK */ + +/*@}*/ + + +#endif /* _SAM4E_GMAC_COMPONENT_ */ diff --git a/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/ethernet_phy.c b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/ethernet_phy.c new file mode 100644 index 0000000..e72175e --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/ethernet_phy.c @@ -0,0 +1,518 @@ +/** + * \file + * + * \brief API driver for KSZ8051MNL PHY component. + * + * Copyright (c) 2013 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 + * + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "FreeRTOSIPConfig.h" + +#include "ethernet_phy.h" +#include "instance/gmac.h" + +/*/ @cond 0 */ +/**INDENT-OFF**/ +#ifdef __cplusplus + extern "C" { +#endif +/**INDENT-ON**/ +/*/ @endcond */ + +/** + * \defgroup ksz8051mnl_ethernet_phy_group PHY component (KSZ8051MNL) + * + * Driver for the ksz8051mnl component. This driver provides access to the main + * features of the PHY. + * + * \section dependencies Dependencies + * This driver depends on the following modules: + * - \ref gmac_group Ethernet Media Access Controller (GMAC) module. + * + * @{ + */ + +SPhyProps phyProps; + +/* Max PHY number */ +#define ETH_PHY_MAX_ADDR 31 + +/* Ethernet PHY operation max retry count */ +#define ETH_PHY_RETRY_MAX 1000000 + +/* Ethernet PHY operation timeout */ +#define ETH_PHY_TIMEOUT 10 + +/** + * \brief Find a valid PHY Address ( from addrStart to 31 ). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * \param uc_start_addr Start address of the PHY to be searched. + * + * \return 0xFF when no valid PHY address is found. + */ +int ethernet_phy_addr = 0; +static uint8_t ethernet_phy_find_valid( Gmac * p_gmac, + uint8_t uc_phy_addr, + uint8_t uc_start_addr ) +{ + uint32_t ul_value = 0; + uint8_t uc_cnt; + uint8_t uc_phy_address = uc_phy_addr; + + gmac_enable_management( p_gmac, true ); + +/* + #define GMII_OUI_MSB 0x0022 + #define GMII_OUI_LSB 0x05 + * + * PHYID1 = 0x0022 + * PHYID2 = 0x1550 + * 0001_0101_0101_0000 = 0x1550 <= mask should be 0xFFF0 + */ + /* Check the current PHY address */ + gmac_phy_read( p_gmac, uc_phy_addr, GMII_PHYID1, &ul_value ); + + /* Find another one */ + if( ul_value != GMII_OUI_MSB ) + { + ethernet_phy_addr = 0xFF; + + for( uc_cnt = uc_start_addr; uc_cnt <= ETH_PHY_MAX_ADDR; uc_cnt++ ) + { + uc_phy_address = ( uc_phy_address + 1 ) & 0x1F; + ul_value = 0; + gmac_phy_read( p_gmac, uc_phy_address, GMII_PHYID1, &ul_value ); + + if( ul_value == GMII_OUI_MSB ) + { + ethernet_phy_addr = uc_phy_address; + break; + } + } + } + + gmac_enable_management( p_gmac, false ); + + if( ethernet_phy_addr != 0xFF ) + { + gmac_phy_read( p_gmac, uc_phy_address, GMII_BMSR, &ul_value ); + } + + return ethernet_phy_addr; +} + + +/** + * \brief Perform a HW initialization to the PHY and set up clocks. + * + * This should be called only once to initialize the PHY pre-settings. + * The PHY address is the reset status of CRS, RXD[3:0] (the emacPins' pullups). + * The COL pin is used to select MII mode on reset (pulled up for Reduced MII). + * The RXDV pin is used to select test mode on reset (pulled up for test mode). + * The above pins should be predefined for corresponding settings in resetPins. + * The GMAC peripheral pins are configured after the reset is done. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * \param ul_mck GMAC MCK. + * + * Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ +uint8_t ethernet_phy_init( Gmac * p_gmac, + uint8_t uc_phy_addr, + uint32_t mck ) +{ + uint8_t uc_rc = GMAC_TIMEOUT; + uint8_t uc_phy; + + ethernet_phy_reset( GMAC, uc_phy_addr ); + + /* Configure GMAC runtime clock */ + uc_rc = gmac_set_mdc_clock( p_gmac, mck ); + + if( uc_rc != GMAC_OK ) + { + return 0; + } + + /* Check PHY Address */ + uc_phy = ethernet_phy_find_valid( p_gmac, uc_phy_addr, 0 ); + + if( uc_phy == 0xFF ) + { + return 0; + } + + if( uc_phy != uc_phy_addr ) + { + ethernet_phy_reset( p_gmac, uc_phy_addr ); + } + + phy_props.phy_chn = uc_phy; + return uc_phy; +} + + +/** + * \brief Get the Link & speed settings, and automatically set up the GMAC with the + * settings. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * \param uc_apply_setting_flag Set to 0 to not apply the PHY configurations, else to apply. + * + * Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ +uint8_t ethernet_phy_set_link( Gmac * p_gmac, + uint8_t uc_phy_addr, + uint8_t uc_apply_setting_flag ) +{ + uint32_t ul_stat1; + uint32_t ul_stat2; + uint8_t uc_phy_address, uc_speed = true, uc_fd = true; + uint8_t uc_rc = GMAC_TIMEOUT; + + gmac_enable_management( p_gmac, true ); + + uc_phy_address = uc_phy_addr; + + uc_rc = gmac_phy_read( p_gmac, uc_phy_address, GMII_BMSR, &ul_stat1 ); + + if( uc_rc != GMAC_OK ) + { + /* Disable PHY management and start the GMAC transfer */ + gmac_enable_management( p_gmac, false ); + + return uc_rc; + } + + if( ( ul_stat1 & GMII_LINK_STATUS ) == 0 ) + { + /* Disable PHY management and start the GMAC transfer */ + gmac_enable_management( p_gmac, false ); + + return GMAC_INVALID; + } + + if( uc_apply_setting_flag == 0 ) + { + /* Disable PHY management and start the GMAC transfer */ + gmac_enable_management( p_gmac, false ); + + return uc_rc; + } + + /* Read advertisement */ + uc_rc = gmac_phy_read( p_gmac, uc_phy_address, GMII_ANAR, &ul_stat2 ); + phy_props.phy_stat1 = ul_stat1; + phy_props.phy_stat2 = ul_stat2; + + if( uc_rc != GMAC_OK ) + { + /* Disable PHY management and start the GMAC transfer */ + gmac_enable_management( p_gmac, false ); + + return uc_rc; + } + + if( ( ul_stat1 & GMII_100BASE_TX_FD ) && ( ul_stat2 & GMII_100TX_FDX ) ) + { + /* Set GMAC for 100BaseTX and Full Duplex */ + uc_speed = true; + uc_fd = true; + } + else + if( ( ul_stat1 & GMII_100BASE_T4_HD ) && ( ul_stat2 & GMII_100TX_HDX ) ) + { + /* Set MII for 100BaseTX and Half Duplex */ + uc_speed = true; + uc_fd = false; + } + else + if( ( ul_stat1 & GMII_10BASE_T_FD ) && ( ul_stat2 & GMII_10_FDX ) ) + { + /* Set MII for 10BaseT and Full Duplex */ + uc_speed = false; + uc_fd = true; + } + else + if( ( ul_stat1 & GMII_10BASE_T_HD ) && ( ul_stat2 & GMII_10_HDX ) ) + { + /* Set MII for 10BaseT and Half Duplex */ + uc_speed = false; + uc_fd = false; + } + + gmac_set_speed( p_gmac, uc_speed ); + gmac_enable_full_duplex( p_gmac, uc_fd ); + + /* Start the GMAC transfers */ + gmac_enable_management( p_gmac, false ); + return uc_rc; +} + +PhyProps_t phy_props; + +/** + * \brief Issue an auto negotiation of the PHY. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * + * Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ +uint8_t ethernet_phy_auto_negotiate( Gmac * p_gmac, + uint8_t uc_phy_addr ) +{ + uint32_t ul_retry_max = ETH_PHY_RETRY_MAX; + uint32_t ul_value; + uint32_t ul_phy_anar; + uint32_t ul_retry_count = 0; + uint8_t uc_speed = 0; + uint8_t uc_fd = 0; + uint8_t uc_rc = GMAC_TIMEOUT; + + gmac_enable_management( p_gmac, true ); + + /* Set up control register */ + uc_rc = gmac_phy_read( p_gmac, uc_phy_addr, GMII_BMCR, &ul_value ); + + if( uc_rc != GMAC_OK ) + { + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = -1; + return uc_rc; + } + + ul_value &= ~( uint32_t ) GMII_AUTONEG; /* Remove auto-negotiation enable */ + ul_value &= ~( uint32_t ) ( GMII_LOOPBACK | GMII_POWER_DOWN ); + ul_value |= ( uint32_t ) GMII_ISOLATE; /* Electrically isolate PHY */ + uc_rc = gmac_phy_write( p_gmac, uc_phy_addr, GMII_BMCR, ul_value ); + + if( uc_rc != GMAC_OK ) + { + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = -2; + return uc_rc; + } + + /* + * Set the Auto_negotiation Advertisement Register. + * MII advertising for Next page. + * 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3. + */ + ul_phy_anar = GMII_100TX_FDX | GMII_100TX_HDX | GMII_10_FDX | GMII_10_HDX | + GMII_AN_IEEE_802_3; + uc_rc = gmac_phy_write( p_gmac, uc_phy_addr, GMII_ANAR, ul_phy_anar ); + + if( uc_rc != GMAC_OK ) + { + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = -3; + return uc_rc; + } + + /* Read & modify control register */ + uc_rc = gmac_phy_read( p_gmac, uc_phy_addr, GMII_BMCR, &ul_value ); + + if( uc_rc != GMAC_OK ) + { + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = -4; + return uc_rc; + } + + ul_value |= GMII_SPEED_SELECT | GMII_AUTONEG | GMII_DUPLEX_MODE; + uc_rc = gmac_phy_write( p_gmac, uc_phy_addr, GMII_BMCR, ul_value ); + + if( uc_rc != GMAC_OK ) + { + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = -5; + return uc_rc; + } + + /* Restart auto negotiation */ + ul_value |= ( uint32_t ) GMII_RESTART_AUTONEG; + ul_value &= ~( uint32_t ) GMII_ISOLATE; + uc_rc = gmac_phy_write( p_gmac, uc_phy_addr, GMII_BMCR, ul_value ); + + if( uc_rc != GMAC_OK ) + { + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = -6; + return uc_rc; + } + + /* Check if auto negotiation is completed */ + while( 1 ) + { + uc_rc = gmac_phy_read( p_gmac, uc_phy_addr, GMII_BMSR, &ul_value ); + + if( uc_rc != GMAC_OK ) + { + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = -7; + return uc_rc; + } + + /* Done successfully */ + if( ul_value & GMII_AUTONEG_COMP ) + { + break; + } + + /* Timeout check */ + if( ul_retry_max ) + { + if( ++ul_retry_count >= ul_retry_max ) + { + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = -8; + return GMAC_TIMEOUT; + } + } + } + + /* Get the auto negotiate link partner base page */ + uc_rc = gmac_phy_read( p_gmac, uc_phy_addr, GMII_PCR1, &phy_props.phy_params ); + + if( uc_rc != GMAC_OK ) + { + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = -9; + return uc_rc; + } + + /* Set up the GMAC link speed */ + if( ( ul_phy_anar & phy_props.phy_params ) & GMII_100TX_FDX ) + { + /* Set MII for 100BaseTX and Full Duplex */ + uc_speed = true; + uc_fd = true; + } + else if( ( ul_phy_anar & phy_props.phy_params ) & GMII_10_FDX ) + { + /* Set MII for 10BaseT and Full Duplex */ + uc_speed = false; + uc_fd = true; + } + else if( ( ul_phy_anar & phy_props.phy_params ) & GMII_100TX_HDX ) + { + /* Set MII for 100BaseTX and half Duplex */ + uc_speed = true; + uc_fd = false; + } + else if( ( ul_phy_anar & phy_props.phy_params ) & GMII_10_HDX ) + { + /* Set MII for 10BaseT and half Duplex */ + uc_speed = false; + uc_fd = false; + } + + gmac_set_speed( p_gmac, uc_speed ); + gmac_enable_full_duplex( p_gmac, uc_fd ); + + /* Select Media Independent Interface type */ + gmac_select_mii_mode( p_gmac, ETH_PHY_MODE ); + + gmac_enable_transmit( GMAC, true ); + gmac_enable_receive( GMAC, true ); + + gmac_enable_management( p_gmac, false ); + phy_props.phy_result = 1; + return uc_rc; +} + +/** + * \brief Issue a SW reset to reset all registers of the PHY. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * + * \Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ +uint8_t ethernet_phy_reset( Gmac * p_gmac, + uint8_t uc_phy_addr ) +{ + uint32_t ul_bmcr = GMII_RESET; + uint8_t uc_phy_address = uc_phy_addr; + uint32_t ul_timeout = ETH_PHY_TIMEOUT; + uint8_t uc_rc = GMAC_TIMEOUT; + + gmac_enable_management( p_gmac, true ); + + ul_bmcr = GMII_RESET; + gmac_phy_write( p_gmac, uc_phy_address, GMII_BMCR, ul_bmcr ); + + do + { + gmac_phy_read( p_gmac, uc_phy_address, GMII_BMCR, &ul_bmcr ); + ul_timeout--; + } while( ( ul_bmcr & GMII_RESET ) && ul_timeout ); + + gmac_enable_management( p_gmac, false ); + + if( !ul_timeout ) + { + uc_rc = GMAC_OK; + } + + return( uc_rc ); +} + +/*/ @cond 0 */ +/**INDENT-OFF**/ +#ifdef __cplusplus + } +#endif +/**INDENT-ON**/ +/*/ @endcond */ + +/** + * \} + */ diff --git a/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/ethernet_phy.h b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/ethernet_phy.h new file mode 100644 index 0000000..8c2cb54 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/ethernet_phy.h @@ -0,0 +1,287 @@ +/** + * \file + * + * \brief KSZ8051MNL (Ethernet PHY) driver for SAM. + * + * Copyright (c) 2013 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 + * + */ + +#ifndef ETHERNET_PHY_H_INCLUDED + #define ETHERNET_PHY_H_INCLUDED + + #include "compiler.h" + + #ifdef __cplusplus + extern "C" { + #endif + +/* IEEE defined Registers */ + #define GMII_BMCR 0x00 /* Basic Control */ + #define GMII_BMSR 0x01 /* Basic Status */ + #define GMII_PHYID1 0x02 /* PHY Identifier 1 */ + #define GMII_PHYID2 0x03 /* PHY Identifier 2 */ + #define GMII_ANAR 0x04 /* Auto_Negotiation Advertisement */ + #define GMII_ANLPAR 0x05 /* Auto_negotiation Link Partner Ability */ + #define GMII_ANER 0x06 /* Auto-negotiation Expansion */ + #define GMII_ANNPR 0x07 /* Auto-negotiation Next Page */ + #define GMII_ANLPNPAR 0x08 /* Link Partner Next Page Ability */ +/*#define GMII_1000BTCR 9 // 1000Base-T Control // Reserved */ +/*#define GMII_1000BTSR 10 // 1000Base-T Status // Reserved */ + #define GMII_AFECR1 0x11 /* AFE Control 1 */ +/*#define GMII_ERDWR 12 // Extend Register - Data Write Register */ +/*#define GMII_ERDRR 13 // Extend Register - Data Read Register */ +/*14 reserved */ + #define GMII_RXERCR 0x15 /* RXER Counter */ + + #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_REG_05_LPA 0x05 /* Link partner ability reg */ + #define PHY_REG_06_ANER 0x06 /* 6 RW Auto-Negotiation Expansion Register */ + #define PHY_REG_07_ANNPTR 0x07 /* 7 RW Auto-Negotiation Next Page TX */ + #define PHY_REG_08_RESERVED0 0x08 /* 0x08..0x0Fh 8-15 RW RESERVED */ + + #define PHY_REG_10_PHYSTS 0x10 /* 16 RO PHY Status Register */ + #define PHY_REG_11_MICR 0x11 /* 17 RW MII Interrupt Control Register */ + #define PHY_REG_12_MISR 0x12 /* 18 RO MII Interrupt Status Register */ + #define PHY_REG_13_RESERVED1 0x13 /* 19 RW RESERVED */ + #define PHY_REG_14_FCSCR 0x14 /* 20 RO False Carrier Sense Counter Register */ + #define PHY_REG_15_RECR 0x15 /* 21 RO Receive Error Counter Register */ + #define PHY_REG_16_PCSR 0x16 /* 22 RW PCS Sub-Layer Configuration and Status Register */ + #define PHY_REG_17_RBR 0x17 /* 23 RW RMII and Bypass Register */ + #define PHY_REG_18_LEDCR 0x18 /* 24 RW LED Direct Control Register */ + #define PHY_REG_19_PHYCR 0x19 /* 25 RW PHY Control Register */ + #define PHY_REG_1A_10BTSCR 0x1A /* 26 RW 10Base-T Status/Control Register */ + #define PHY_REG_1B_CDCTRL1 0x1B /* 27 RW CD Test Control Register and BIST Extensions Register */ + #define PHY_REG_1B_INT_CTRL 0x1B /* 27 RW KSZ8041NL interrupt control */ + #define PHY_REG_1C_RESERVED2 0x1C /* 28 RW RESERVED */ + #define PHY_REG_1D_EDCR 0x1D /* 29 RW Energy Detect Control Register */ + #define PHY_REG_1E_RESERVED3 0x1E /* */ + #define PHY_REG_1F_RESERVED4 0x1F /* 30-31 RW RESERVED */ + + #define PHY_REG_1E_PHYCR_1 0x1E /* */ + #define PHY_REG_1F_PHYCR_2 0x1F /* */ + + #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 ) + + typedef struct _SPhyProps + { + unsigned char speed; + unsigned char mdix; + unsigned char duplex; + unsigned char spare; + } SPhyProps; + + const char * phyPrintable( const SPhyProps * apProps ); + + extern SPhyProps phyProps; + + #define GMII_OMSOR 0x16 /* Operation Mode Strap Override */ + #define GMII_OMSSR 0x17 /* Operation Mode Strap Status */ + #define GMII_ECR 0x18 /* Expanded Control */ +/*#define GMII_DPPSR 19 // Digital PMA/PCS Status */ +/*20 reserved */ +/*#define GMII_RXERCR 21 // RXER Counter Register */ +/*22-26 reserved */ + #define GMII_ICSR 0x1B /* Interrupt Control/Status */ +/*#define GMII_DDC1R 28 // Digital Debug Control 1 Register */ + #define GMII_LCSR 0x1D /* LinkMD Control/Status */ + +/*29-30 reserved */ + #define GMII_PCR1 0x1E /* PHY Control 1 */ + #define GMII_PCR2 0x1F /* PHY Control 2 */ + +/* + * //Extend Registers + #define GMII_CCR 256 // Common Control Register + #define GMII_SSR 257 // Strap Status Register + #define GMII_OMSOR 258 // Operation Mode Strap Override Register + #define GMII_OMSSR 259 // Operation Mode Strap Status Register + #define GMII_RCCPSR 260 // RGMII Clock and Control Pad Skew Register + #define GMII_RRDPSR 261 // RGMII RX Data Pad Skew Register + #define GMII_ATR 263 // Analog Test Register + */ + + +/* Bit definitions: GMII_BMCR 0x00 Basic Control */ + #define GMII_RESET ( 1 << 15 ) /* 1= Software Reset; 0=Normal Operation */ + #define GMII_LOOPBACK ( 1 << 14 ) /* 1=loopback Enabled; 0=Normal Operation */ + #define GMII_SPEED_SELECT ( 1 << 13 ) /* 1=100Mbps; 0=10Mbps */ + #define GMII_AUTONEG ( 1 << 12 ) /* Auto-negotiation Enable */ + #define GMII_POWER_DOWN ( 1 << 11 ) /* 1=Power down 0=Normal operation */ + #define GMII_ISOLATE ( 1 << 10 ) /* 1 = Isolates 0 = Normal operation */ + #define GMII_RESTART_AUTONEG ( 1 << 9 ) /* 1 = Restart auto-negotiation 0 = Normal operation */ + #define GMII_DUPLEX_MODE ( 1 << 8 ) /* 1 = Full duplex operation 0 = Normal operation */ + #define GMII_COLLISION_TEST ( 1 << 7 ) /* 1 = Enable COL test; 0 = Disable COL test */ +/*#define GMII_SPEED_SELECT_MSB (1 << 6) // Reserved */ +/* Reserved 6 to 0 // Read as 0, ignore on write */ + +/* Bit definitions: GMII_BMSR 0x01 Basic Status */ + #define GMII_100BASE_T4 ( 1 << 15 ) /* 100BASE-T4 Capable */ + #define GMII_100BASE_TX_FD ( 1 << 14 ) /* 100BASE-TX Full Duplex Capable */ + #define GMII_100BASE_T4_HD ( 1 << 13 ) /* 100BASE-TX Half Duplex Capable */ + #define GMII_10BASE_T_FD ( 1 << 12 ) /* 10BASE-T Full Duplex Capable */ + #define GMII_10BASE_T_HD ( 1 << 11 ) /* 10BASE-T Half Duplex Capable */ +/* Reserved 10 to79 // Read as 0, ignore on write */ +/*#define GMII_EXTEND_STATUS (1 << 8) // 1 = Extend Status Information In Reg 15 */ +/* Reserved 7 */ + #define GMII_MF_PREAMB_SUPPR ( 1 << 6 ) /* MII Frame Preamble Suppression */ + #define GMII_AUTONEG_COMP ( 1 << 5 ) /* Auto-negotiation Complete */ + #define GMII_REMOTE_FAULT ( 1 << 4 ) /* Remote Fault */ + #define GMII_AUTONEG_ABILITY ( 1 << 3 ) /* Auto Configuration Ability */ + #define GMII_LINK_STATUS ( 1 << 2 ) /* Link Status */ + #define GMII_JABBER_DETECT ( 1 << 1 ) /* Jabber Detect */ + #define GMII_EXTEND_CAPAB ( 1 << 0 ) /* Extended Capability */ + + +/* Bit definitions: GMII_PHYID1 0x02 PHY Identifier 1 */ +/* Bit definitions: GMII_PHYID2 0x03 PHY Identifier 2 */ + #define GMII_LSB_MASK 0x3F + #define GMII_OUI_MSB 0x0022 + #define GMII_OUI_LSB 0x05 + + +/* Bit definitions: GMII_ANAR 0x04 Auto_Negotiation Advertisement */ +/* Bit definitions: GMII_ANLPAR 0x05 Auto_negotiation Link Partner Ability */ + #define GMII_NP ( 1 << 15 ) /* Next page Indication */ +/* Reserved 7 */ + #define GMII_RF ( 1 << 13 ) /* Remote Fault */ +/* Reserved 12 // Write as 0, ignore on read */ + #define GMII_PAUSE_MASK ( 3 << 11 ) /* 0,0 = No Pause 1,0 = Asymmetric Pause(link partner) */ + /* 0,1 = Symmetric Pause 1,1 = Symmetric&Asymmetric Pause(local device) */ + #define GMII_100T4 ( 1 << 9 ) /* 100BASE-T4 Support */ + #define GMII_100TX_FDX ( 1 << 8 ) /* 100BASE-TX Full Duplex Support */ + #define GMII_100TX_HDX ( 1 << 7 ) /* 100BASE-TX Support */ + #define GMII_10_FDX ( 1 << 6 ) /* 10BASE-T Full Duplex Support */ + #define GMII_10_HDX ( 1 << 5 ) /* 10BASE-T Support */ +/* Selector 4 to 0 // Protocol Selection Bits */ + #define GMII_AN_IEEE_802_3 0x0001 /* [00001] = IEEE 802.3 */ + + +/* Bit definitions: GMII_ANER 0x06 Auto-negotiation Expansion */ +/* Reserved 15 to 5 // Read as 0, ignore on write */ + #define GMII_PDF ( 1 << 4 ) /* Local Device Parallel Detection Fault */ + #define GMII_LP_NP_ABLE ( 1 << 3 ) /* Link Partner Next Page Able */ + #define GMII_NP_ABLE ( 1 << 2 ) /* Local Device Next Page Able */ + #define GMII_PAGE_RX ( 1 << 1 ) /* New Page Received */ + #define GMII_LP_AN_ABLE ( 1 << 0 ) /* Link Partner Auto-negotiation Able */ + +/** + * \brief Perform a HW initialization to the PHY and set up clocks. + * + * This should be called only once to initialize the PHY pre-settings. + * The PHY address is the reset status of CRS, RXD[3:0] (the GmacPins' pullups). + * The COL pin is used to select MII mode on reset (pulled up for Reduced MII). + * The RXDV pin is used to select test mode on reset (pulled up for test mode). + * The above pins should be predefined for corresponding settings in resetPins. + * The GMAC peripheral pins are configured after the reset is done. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * \param ul_mck GMAC MCK. + * + * Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ + uint8_t ethernet_phy_init( Gmac * p_gmac, + uint8_t uc_phy_addr, + uint32_t ul_mck ); + + +/** + * \brief Get the Link & speed settings, and automatically set up the GMAC with the + * settings. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * \param uc_apply_setting_flag Set to 0 to not apply the PHY configurations, else to apply. + * + * Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ + uint8_t ethernet_phy_set_link( Gmac * p_gmac, + uint8_t uc_phy_addr, + uint8_t uc_apply_setting_flag ); + + +/** + * \brief Issue an auto negotiation of the PHY. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * + * Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ + uint8_t ethernet_phy_auto_negotiate( Gmac * p_gmac, + uint8_t uc_phy_addr ); + +/** + * \brief Issue a SW reset to reset all registers of the PHY. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * + * \Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ + uint8_t ethernet_phy_reset( Gmac * p_gmac, + uint8_t uc_phy_addr ); + + typedef struct xPHY_PROPS + { + signed char phy_result; + uint32_t phy_params; + uint32_t phy_stat1; + uint32_t phy_stat2; + unsigned char phy_chn; + } PhyProps_t; + extern PhyProps_t phy_props; + + #ifdef __cplusplus + } /* extern "C" */ + #endif + +#endif /* #ifndef ETHERNET_PHY_H_INCLUDED */ diff --git a/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/gmac.c b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/gmac.c new file mode 100644 index 0000000..0876c55 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/gmac.c @@ -0,0 +1,1036 @@ +/** + * \file + * + * \brief GMAC (Ethernet MAC) driver for SAM. + * + * Copyright (c) 2013 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 + * + */ + +/* Standard includes. */ +#include +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#include "FreeRTOSIPConfig.h" + +#include "compiler.h" +#include "instance/gmac.h" +#include "ethernet_phy.h" + +/*/ @cond 0 */ +/**INDENT-OFF**/ +#ifdef __cplusplus + extern "C" { +#endif +/**INDENT-ON**/ +/*/ @endcond */ + +#ifndef ARRAY_SIZE + #define ARRAY_SIZE( x ) ( int ) ( sizeof( x ) / sizeof( x )[ 0 ] ) +#endif + +/** + * \defgroup gmac_group Ethernet Media Access Controller + * + * See \ref gmac_quickstart. + * + * Driver for the GMAC (Ethernet Media Access Controller). + * This file contains basic functions for the GMAC, with support for all modes, settings + * and clock speeds. + * + * \section dependencies Dependencies + * This driver does not depend on other modules. + * + * @{ + */ + +/** TX descriptor lists */ +COMPILER_ALIGNED( 8 ) +static gmac_tx_descriptor_t gs_tx_desc[ GMAC_TX_BUFFERS ]; +#if ( GMAC_USES_TX_CALLBACK != 0 ) +/** TX callback lists */ + static gmac_dev_tx_cb_t gs_tx_callback[ GMAC_TX_BUFFERS ]; +#endif +/** RX descriptors lists */ +COMPILER_ALIGNED( 8 ) +static gmac_rx_descriptor_t gs_rx_desc[ GMAC_RX_BUFFERS ]; + +#if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + +/** Send Buffer. Section 3.6 of AMBA 2.0 spec states that burst should not cross the + * 1K Boundaries. Receive buffer manager write operations are burst of 2 words => 3 lsb bits + * of the address shall be set to 0. + */ + COMPILER_ALIGNED( 8 ) + static uint8_t gs_uc_tx_buffer[ GMAC_TX_BUFFERS * GMAC_TX_UNITSIZE ]; +#endif /* ipconfigZERO_COPY_TX_DRIVER */ + +/** Receive Buffer */ +COMPILER_ALIGNED( 8 ) +static uint8_t gs_uc_rx_buffer[ GMAC_RX_BUFFERS * GMAC_RX_UNITSIZE ]; + +/** + * GMAC device memory management struct. + */ +typedef struct gmac_dev_mem +{ + /* Pointer to allocated buffer for RX. The address should be 8-byte aligned + * and the size should be GMAC_RX_UNITSIZE * wRxSize. */ + uint8_t * p_rx_buffer; + /* Pointer to allocated RX descriptor list. */ + gmac_rx_descriptor_t * p_rx_dscr; + /* RX size, in number of registered units (RX descriptors). */ + /* Increased size from 16- to 32-bits, because it's more efficient */ + uint32_t us_rx_size; + + /* Pointer to allocated buffer for TX. The address should be 8-byte aligned + * and the size should be GMAC_TX_UNITSIZE * wTxSize. */ + uint8_t * p_tx_buffer; + /* Pointer to allocated TX descriptor list. */ + gmac_tx_descriptor_t * p_tx_dscr; + /* TX size, in number of registered units (TX descriptors). */ + uint32_t us_tx_size; +} gmac_dev_mem_t; + +/** Return count in buffer */ +#define CIRC_CNT( head, tail, size ) ( ( ( head ) - ( tail ) ) % ( size ) ) + +/* + * Return space available, from 0 to size-1. + * Always leave one free char as a completely full buffer that has (head == tail), + * which is the same as empty. + */ +#define CIRC_SPACE( head, tail, size ) CIRC_CNT( ( tail ), ( ( head ) + 1 ), ( size ) ) + +/** Circular buffer is empty ? */ +#define CIRC_EMPTY( head, tail ) ( head == tail ) +/** Clear circular buffer */ +#define CIRC_CLEAR( head, tail ) do { ( head ) = 0; ( tail ) = 0; } while( ipFALSE_BOOL ) + +/** Increment head or tail */ +static __inline void circ_inc32( int32_t * lHeadOrTail, + uint32_t ulSize ) +{ + ( *lHeadOrTail )++; + + if( ( *lHeadOrTail ) >= ( int32_t ) ulSize ) + { + ( *lHeadOrTail ) = 0; + } +} + +/** + * \brief Wait PHY operation to be completed. + * + * \param p_gmac HW controller address. + * \param ul_retry The retry times, 0 to wait forever until completeness. + * + * Return GMAC_OK if the operation is completed successfully. + */ +static uint8_t gmac_wait_phy( Gmac * p_gmac, + const uint32_t ul_retry ) +{ + volatile uint32_t ul_retry_count = 0; + const uint32_t xPHYPollDelay = pdMS_TO_TICKS( 1ul ); + + while( !gmac_is_phy_idle( p_gmac ) ) + { + if( ul_retry == 0 ) + { + continue; + } + + ul_retry_count++; + + if( ul_retry_count >= ul_retry ) + { + return GMAC_TIMEOUT; + } + + /* Block the task to allow other tasks to execute while the PHY + * is not connected. */ + vTaskDelay( xPHYPollDelay ); + } + + return GMAC_OK; +} + +/** + * \brief Disable transfer, reset registers and descriptor lists. + * + * \param p_dev Pointer to GMAC driver instance. + * + */ +static void gmac_reset_tx_mem( gmac_device_t * p_dev ) +{ + Gmac * p_hw = p_dev->p_hw; + uint8_t * p_tx_buff = p_dev->p_tx_buffer; + gmac_tx_descriptor_t * p_td = p_dev->p_tx_dscr; + + uint32_t ul_index; + uint32_t ul_address; + + /* Disable TX */ + gmac_enable_transmit( p_hw, 0 ); + + /* Set up the TX descriptors */ + CIRC_CLEAR( p_dev->l_tx_head, p_dev->l_tx_tail ); + + for( ul_index = 0; ul_index < p_dev->ul_tx_list_size; ul_index++ ) + { + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + ul_address = ( uint32_t ) 0u; + } + #else + { + ul_address = ( uint32_t ) ( &( p_tx_buff[ ul_index * GMAC_TX_UNITSIZE ] ) ); + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + p_td[ ul_index ].addr = ul_address; + p_td[ ul_index ].status.val = GMAC_TXD_USED; + } + + p_td[ p_dev->ul_tx_list_size - 1 ].status.val = + GMAC_TXD_USED | GMAC_TXD_WRAP; + + /* Set transmit buffer queue */ + gmac_set_tx_queue( p_hw, ( uint32_t ) p_td ); +} + +/** + * \brief Disable receiver, reset registers and descriptor list. + * + * \param p_drv Pointer to GMAC Driver instance. + */ +static void gmac_reset_rx_mem( gmac_device_t * p_dev ) +{ + Gmac * p_hw = p_dev->p_hw; + uint8_t * p_rx_buff = p_dev->p_rx_buffer; + gmac_rx_descriptor_t * pRd = p_dev->p_rx_dscr; + + uint32_t ul_index; + uint32_t ul_address; + + /* Disable RX */ + gmac_enable_receive( p_hw, 0 ); + + /* Set up the RX descriptors */ + p_dev->ul_rx_idx = 0; + + for( ul_index = 0; ul_index < p_dev->ul_rx_list_size; ul_index++ ) + { + ul_address = ( uint32_t ) ( &( p_rx_buff[ ul_index * GMAC_RX_UNITSIZE ] ) ); + pRd[ ul_index ].addr.val = ul_address & GMAC_RXD_ADDR_MASK; + pRd[ ul_index ].status.val = 0; + } + + pRd[ p_dev->ul_rx_list_size - 1 ].addr.val |= GMAC_RXD_WRAP; + + /* Set receive buffer queue */ + gmac_set_rx_queue( p_hw, ( uint32_t ) pRd ); +} + + +/** + * \brief Initialize the allocated buffer lists for GMAC driver to transfer data. + * Must be invoked after gmac_dev_init() but before RX/TX starts. + * + * \note If input address is not 8-byte aligned, the address is automatically + * adjusted and the list size is reduced by one. + * + * \param p_gmac Pointer to GMAC instance. + * \param p_gmac_dev Pointer to GMAC device instance. + * \param p_dev_mm Pointer to the GMAC memory management control block. + * \param p_tx_cb Pointer to allocated TX callback list. + * + * \return GMAC_OK or GMAC_PARAM. + */ +static uint8_t gmac_init_mem( Gmac * p_gmac, + gmac_device_t * p_gmac_dev, + gmac_dev_mem_t * p_dev_mm +#if ( GMAC_USES_TX_CALLBACK != 0 ) + , + gmac_dev_tx_cb_t * p_tx_cb +#endif + ) +{ + if( ( p_dev_mm->us_rx_size <= 1 ) || p_dev_mm->us_tx_size <= 1 + #if ( GMAC_USES_TX_CALLBACK != 0 ) + || p_tx_cb == NULL + #endif + ) + { + return GMAC_PARAM; + } + + /* Assign RX buffers */ + if( ( ( uint32_t ) p_dev_mm->p_rx_buffer & 0x7 ) || + ( ( uint32_t ) p_dev_mm->p_rx_dscr & 0x7 ) ) + { + p_dev_mm->us_rx_size--; + } + + p_gmac_dev->p_rx_buffer = + ( uint8_t * ) ( ( uint32_t ) p_dev_mm->p_rx_buffer & 0xFFFFFFF8 ); + p_gmac_dev->p_rx_dscr = + ( gmac_rx_descriptor_t * ) ( ( uint32_t ) p_dev_mm->p_rx_dscr + & 0xFFFFFFF8 ); + p_gmac_dev->ul_rx_list_size = p_dev_mm->us_rx_size; + + /* Assign TX buffers */ + if( ( ( uint32_t ) p_dev_mm->p_tx_buffer & 0x7 ) || + ( ( uint32_t ) p_dev_mm->p_tx_dscr & 0x7 ) ) + { + p_dev_mm->us_tx_size--; + } + + p_gmac_dev->p_tx_buffer = + ( uint8_t * ) ( ( uint32_t ) p_dev_mm->p_tx_buffer & 0xFFFFFFF8 ); + p_gmac_dev->p_tx_dscr = + ( gmac_tx_descriptor_t * ) ( ( uint32_t ) p_dev_mm->p_tx_dscr + & 0xFFFFFFF8 ); + p_gmac_dev->ul_tx_list_size = p_dev_mm->us_tx_size; + #if ( GMAC_USES_TX_CALLBACK != 0 ) + p_gmac_dev->func_tx_cb_list = p_tx_cb; + #endif + /* Reset TX & RX */ + gmac_reset_rx_mem( p_gmac_dev ); + gmac_reset_tx_mem( p_gmac_dev ); + + /* Enable Rx and Tx, plus the statistics register */ + gmac_enable_transmit( p_gmac, true ); + gmac_enable_receive( p_gmac, true ); + gmac_enable_statistics_write( p_gmac, true ); + + /* Set up the interrupts for transmission and errors */ + gmac_enable_interrupt( p_gmac, + GMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */ + GMAC_IER_TUR | /* Enable transmit underrun interrupt. */ + GMAC_IER_RLEX | /* Enable retry limit exceeded interrupt. */ + GMAC_IER_TFC | /* Enable transmit buffers exhausted in mid-frame interrupt. */ + GMAC_IER_TCOMP | /* Enable transmit complete interrupt. */ + GMAC_IER_ROVR | /* Enable receive overrun interrupt. */ + GMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */ + GMAC_IER_PFNZ | /* Enable pause frame received interrupt. */ + GMAC_IER_PTZ ); /* Enable pause time zero interrupt. */ + + return GMAC_OK; +} + +/** + * \brief Read the PHY register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_address PHY address. + * \param uc_address Register address. + * \param p_value Pointer to a 32-bit location to store read data. + * + * \Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ +uint8_t gmac_phy_read( Gmac * p_gmac, + uint8_t uc_phy_address, + uint8_t uc_address, + uint32_t * p_value ) +{ + gmac_maintain_phy( p_gmac, uc_phy_address, uc_address, 1, 0 ); + + if( gmac_wait_phy( p_gmac, MAC_PHY_RETRY_MAX ) == GMAC_TIMEOUT ) + { + return GMAC_TIMEOUT; + } + + *p_value = gmac_get_phy_data( p_gmac ); + return GMAC_OK; +} + +/** + * \brief Write the PHY register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_address PHY Address. + * \param uc_address Register Address. + * \param ul_value Data to write, actually 16-bit data. + * + * \Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. + */ +uint8_t gmac_phy_write( Gmac * p_gmac, + uint8_t uc_phy_address, + uint8_t uc_address, + uint32_t ul_value ) +{ + gmac_maintain_phy( p_gmac, uc_phy_address, uc_address, 0, ul_value ); + + if( gmac_wait_phy( p_gmac, MAC_PHY_RETRY_MAX ) == GMAC_TIMEOUT ) + { + return GMAC_TIMEOUT; + } + + return GMAC_OK; +} + +/** + * \brief Initialize the GMAC driver. + * + * \param p_gmac Pointer to the GMAC instance. + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param p_opt GMAC configure options. + */ +void gmac_dev_init( Gmac * p_gmac, + gmac_device_t * p_gmac_dev, + gmac_options_t * p_opt ) +{ + gmac_dev_mem_t gmac_dev_mm; + + /* Disable TX & RX and more */ + gmac_network_control( p_gmac, 0 ); + gmac_disable_interrupt( p_gmac, ~0u ); + + + gmac_clear_statistics( p_gmac ); + + /* Clear all status bits in the receive status register. */ + gmac_clear_rx_status( p_gmac, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA ); + + /* Clear all status bits in the transmit status register */ + gmac_clear_tx_status( p_gmac, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE + | GMAC_TSR_TFC | GMAC_TSR_TXCOMP | GMAC_TSR_UND ); + + /* Clear interrupts */ + gmac_get_interrupt_status( p_gmac ); + #if !defined( ETHERNET_CONF_DATA_OFFSET ) + + /* Receive Buffer Offset + * Indicates the number of bytes by which the received data + * is offset from the start of the receive buffer + * which can be handy for alignment reasons */ + /* Note: FreeRTOS+TCP wants to have this offset set to 2 bytes */ + #error ETHERNET_CONF_DATA_OFFSET not defined, assuming 0 + #endif + + /* Enable the copy of data into the buffers + * ignore broadcasts, and not copy FCS. */ + + gmac_set_configure( p_gmac, + ( gmac_get_configure( p_gmac ) & ~GMAC_NCFGR_RXBUFO_Msk ) | + GMAC_NCFGR_RFCS | /* Remove FCS, frame check sequence (last 4 bytes) */ + GMAC_NCFGR_PEN | /* Pause Enable */ + GMAC_NCFGR_RXBUFO( ETHERNET_CONF_DATA_OFFSET ) | + GMAC_RXD_RXCOEN ); + + /* + * GMAC_DCFGR_TXCOEN: (GMAC_DCFGR) Transmitter Checksum Generation Offload Enable. + * Note: that SAM4E does have RX checksum offloading + * but TX checksum offloading has NOT been implemented. + */ + + gmac_set_dma( p_gmac, + gmac_get_dma( p_gmac ) | GMAC_DCFGR_TXCOEN ); + + gmac_enable_copy_all( p_gmac, p_opt->uc_copy_all_frame ); + gmac_disable_broadcast( p_gmac, p_opt->uc_no_boardcast ); + + /* Fill in GMAC device memory management */ + gmac_dev_mm.p_rx_buffer = gs_uc_rx_buffer; + gmac_dev_mm.p_rx_dscr = gs_rx_desc; + gmac_dev_mm.us_rx_size = GMAC_RX_BUFFERS; + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + gmac_dev_mm.p_tx_buffer = NULL; + } + #else + { + gmac_dev_mm.p_tx_buffer = gs_uc_tx_buffer; + } + #endif + gmac_dev_mm.p_tx_dscr = gs_tx_desc; + gmac_dev_mm.us_tx_size = GMAC_TX_BUFFERS; + + gmac_init_mem( p_gmac, p_gmac_dev, &gmac_dev_mm + #if ( GMAC_USES_TX_CALLBACK != 0 ) + , gs_tx_callback + #endif + ); + + gmac_set_address( p_gmac, 0, p_opt->uc_mac_addr ); +} + +/** + * \brief Frames can be read from the GMAC in multiple sections. + * + * Returns > 0 if a complete frame is available + * It also it cleans up incomplete older frames + */ + +static uint32_t gmac_dev_poll( gmac_device_t * p_gmac_dev ) +{ + uint32_t ulReturn = 0; + int32_t ulIndex = p_gmac_dev->ul_rx_idx; + gmac_rx_descriptor_t * pxHead = &p_gmac_dev->p_rx_dscr[ ulIndex ]; + + /* Discard any incomplete frames */ + while( ( pxHead->addr.val & GMAC_RXD_OWNERSHIP ) && + ( pxHead->status.val & GMAC_RXD_SOF ) == 0 ) + { + pxHead->addr.val &= ~( GMAC_RXD_OWNERSHIP ); + circ_inc32( &ulIndex, p_gmac_dev->ul_rx_list_size ); + pxHead = &p_gmac_dev->p_rx_dscr[ ulIndex ]; + p_gmac_dev->ul_rx_idx = ulIndex; + #if ( GMAC_STATS != 0 ) + { + gmacStats.incompCount++; + } + #endif + } + + while( ( pxHead->addr.val & GMAC_RXD_OWNERSHIP ) != 0 ) + { + if( ( pxHead->status.val & GMAC_RXD_EOF ) != 0 ) + { + /* Here a complete frame has been seen with SOF and EOF */ + ulReturn = pxHead->status.bm.len; + break; + } + + circ_inc32( &ulIndex, p_gmac_dev->ul_rx_list_size ); + pxHead = &p_gmac_dev->p_rx_dscr[ ulIndex ]; + + if( ( pxHead->addr.val & GMAC_RXD_OWNERSHIP ) == 0 ) + { + /* CPU is not the owner (yet) */ + break; + } + + if( ( pxHead->status.val & GMAC_RXD_SOF ) != 0 ) + { + /* Strange, we found a new Start Of Frame + * discard previous segments */ + int32_t ulPrev = p_gmac_dev->ul_rx_idx; + pxHead = &p_gmac_dev->p_rx_dscr[ ulPrev ]; + + do + { + pxHead->addr.val &= ~( GMAC_RXD_OWNERSHIP ); + circ_inc32( &ulPrev, p_gmac_dev->ul_rx_list_size ); + pxHead = &p_gmac_dev->p_rx_dscr[ ulPrev ]; + #if ( GMAC_STATS != 0 ) + { + gmacStats.truncCount++; + } + #endif + } while( ulPrev != ulIndex ); + + p_gmac_dev->ul_rx_idx = ulIndex; + } + } + + return ulReturn; +} + +/** + * \brief Frames can be read from the GMAC in multiple sections. + * Read ul_frame_size bytes from the GMAC receive buffers to pcTo. + * p_rcv_size is the size of the entire frame. Generally gmac_read + * will be repeatedly called until the sum of all the ul_frame_size equals + * the value of p_rcv_size. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param p_frame Address of the frame buffer. + * \param ul_frame_size Length of the frame. + * \param p_rcv_size Received frame size. + * + * \return GMAC_OK if receiving frame successfully, otherwise failed. + */ +uint32_t gmac_dev_read( gmac_device_t * p_gmac_dev, + uint8_t * p_frame, + uint32_t ul_frame_size, + uint32_t * p_rcv_size ) +{ + int32_t nextIdx; /* A copy of the Rx-index 'ul_rx_idx' */ + int32_t bytesLeft = gmac_dev_poll( p_gmac_dev ); + gmac_rx_descriptor_t * pxHead; + + if( bytesLeft == 0 ) + { + return GMAC_RX_NULL; + } + + /* gmac_dev_poll has confirmed that there is a complete frame at + * the current position 'ul_rx_idx' + */ + nextIdx = p_gmac_dev->ul_rx_idx; + + /* Read +2 bytes because buffers are aligned at -2 bytes */ + bytesLeft = min( bytesLeft + 2, ( int32_t ) ul_frame_size ); + + /* The frame will be copied in 1 or 2 memcpy's */ + if( ( p_frame != NULL ) && ( bytesLeft != 0 ) ) + { + const uint8_t * source; + int32_t left; + int32_t toCopy; + + source = p_gmac_dev->p_rx_buffer + nextIdx * GMAC_RX_UNITSIZE; + left = bytesLeft; + toCopy = ( p_gmac_dev->ul_rx_list_size - nextIdx ) * GMAC_RX_UNITSIZE; + + if( toCopy > left ) + { + toCopy = left; + } + + memcpy( p_frame, source, toCopy ); + left -= toCopy; + + if( left != 0ul ) + { + memcpy( p_frame + toCopy, ( void * ) p_gmac_dev->p_rx_buffer, left ); + } + } + + do + { + pxHead = &p_gmac_dev->p_rx_dscr[ nextIdx ]; + pxHead->addr.val &= ~( GMAC_RXD_OWNERSHIP ); + circ_inc32( &nextIdx, p_gmac_dev->ul_rx_list_size ); + } while( ( pxHead->status.val & GMAC_RXD_EOF ) == 0 ); + + p_gmac_dev->ul_rx_idx = nextIdx; + + *p_rcv_size = bytesLeft; + + return GMAC_OK; +} + + +extern void vGMACGenerateChecksum( uint8_t * apBuffer ); + +/** + * \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the + * GMAC Tx buffers, and then indicates to the GMAC that the buffer is ready. + * If lEndOfFrame is true then the data being copied is the end of the frame + * and the frame can be transmitted. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param p_buffer Pointer to the data buffer. + * \param ul_size Length of the frame. + * \param func_tx_cb Transmit callback function. + * + * \return Length sent. + */ +uint32_t gmac_dev_write( gmac_device_t * p_gmac_dev, + void * p_buffer, + uint32_t ul_size, + gmac_dev_tx_cb_t func_tx_cb ) +{ + volatile gmac_tx_descriptor_t * p_tx_td; + + #if ( GMAC_USES_TX_CALLBACK != 0 ) + volatile gmac_dev_tx_cb_t * p_func_tx_cb; + #endif + + Gmac * p_hw = p_gmac_dev->p_hw; + + #if ( GMAC_USES_TX_CALLBACK == 0 ) + ( void ) func_tx_cb; + #endif + + /* Check parameter */ + if( ul_size > GMAC_TX_UNITSIZE ) + { + return GMAC_PARAM; + } + + /* Pointers to the current transmit descriptor */ + p_tx_td = &p_gmac_dev->p_tx_dscr[ p_gmac_dev->l_tx_head ]; + + /* If no free TxTd, buffer can't be sent, schedule the wakeup callback */ +/* if (CIRC_SPACE(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail, */ +/* p_gmac_dev->ul_tx_list_size) == 0) */ + { + if( ( p_tx_td->status.val & GMAC_TXD_USED ) == 0 ) + { + return GMAC_TX_BUSY; + } + } + #if ( GMAC_USES_TX_CALLBACK != 0 ) + /* Pointers to the current Tx callback */ + p_func_tx_cb = &p_gmac_dev->func_tx_cb_list[ p_gmac_dev->l_tx_head ]; + #endif + + /* Set up/copy data to transmission buffer */ + if( p_buffer && ul_size ) + { + /* Driver manages the ring buffer */ + + /* Calculating the checksum here is faster than calculating it from the GMAC buffer + * because within p_buffer, it is well aligned */ + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Zero-copy... */ + p_tx_td->addr = ( uint32_t ) p_buffer; + } + #else + { + /* Or Memcopy... */ + memcpy( ( void * ) p_tx_td->addr, p_buffer, ul_size ); + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + vGMACGenerateChecksum( ( uint8_t * ) p_tx_td->addr ); + } + + #if ( GMAC_USES_TX_CALLBACK != 0 ) + /* Tx callback */ + *p_func_tx_cb = func_tx_cb; + #endif + + /* Update transmit descriptor status */ + + /* The buffer size defined is the length of ethernet frame, + * so it's always the last buffer of the frame. */ + if( p_gmac_dev->l_tx_head == ( int32_t ) ( p_gmac_dev->ul_tx_list_size - 1 ) ) + { + /* No need to 'and' with GMAC_TXD_LEN_MASK because ul_size has been checked */ + p_tx_td->status.val = + ul_size | GMAC_TXD_LAST | GMAC_TXD_WRAP; + } + else + { + p_tx_td->status.val = + ul_size | GMAC_TXD_LAST; + } + + circ_inc32( &p_gmac_dev->l_tx_head, p_gmac_dev->ul_tx_list_size ); + + /* Now start to transmit if it is still not done */ + gmac_start_transmission( p_hw ); + + return GMAC_OK; +} + +/** + * \brief Get current load of transmit. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * + * \return Current load of transmit. + */ +#if ( GMAC_USES_TX_CALLBACK != 0 ) +/* Without defining GMAC_USES_TX_CALLBACK, l_tx_tail won't be updated */ + uint32_t gmac_dev_get_tx_load( gmac_device_t * p_gmac_dev ) + { + uint16_t us_head = p_gmac_dev->l_tx_head; + uint16_t us_tail = p_gmac_dev->l_tx_tail; + + return CIRC_CNT( us_head, us_tail, p_gmac_dev->ul_tx_list_size ); + } +#endif + +/** + * \brief Register/Clear RX callback. Callback will be invoked after the next received + * frame. + * + * When gmac_dev_read() returns GMAC_RX_NULL, the application task calls + * gmac_dev_set_rx_callback() to register func_rx_cb() callback and enters suspend state. + * The callback is in charge to resume the task once a new frame has been + * received. The next time gmac_dev_read() is called, it will be successful. + * + * This function is usually invoked from the RX callback itself with NULL + * callback, to unregister. Once the callback has resumed the application task, + * there is no need to invoke the callback again. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param func_tx_cb Receive callback function. + */ +void gmac_dev_set_rx_callback( gmac_device_t * p_gmac_dev, + gmac_dev_rx_cb_t func_rx_cb ) +{ + Gmac * p_hw = p_gmac_dev->p_hw; + + if( func_rx_cb == NULL ) + { + gmac_disable_interrupt( p_hw, GMAC_IDR_RCOMP ); + p_gmac_dev->func_rx_cb = NULL; + } + else + { + p_gmac_dev->func_rx_cb = func_rx_cb; + gmac_enable_interrupt( p_hw, GMAC_IER_RCOMP ); + } +} + +/** + * \brief Register/Clear TX wakeup callback. + * + * When gmac_dev_write() returns GMAC_TX_BUSY (all transmit descriptor busy), the application + * task calls gmac_dev_set_tx_wakeup_callback() to register func_wakeup() callback and + * enters suspend state. The callback is in charge to resume the task once + * several transmit descriptors have been released. The next time gmac_dev_write() will be called, + * it shall be successful. + * + * This function is usually invoked with NULL callback from the TX wakeup + * callback itself, to unregister. Once the callback has resumed the + * application task, there is no need to invoke the callback again. + * + * \param p_gmac_dev Pointer to GMAC device instance. + * \param func_wakeup Pointer to wakeup callback function. + * \param uc_threshold Number of free transmit descriptor before wakeup callback invoked. + * + * \return GMAC_OK, GMAC_PARAM on parameter error. + */ +#if ( GMAC_USES_WAKEUP_CALLBACK ) + uint8_t gmac_dev_set_tx_wakeup_callback( gmac_device_t * p_gmac_dev, + gmac_dev_wakeup_cb_t func_wakeup_cb, + uint8_t uc_threshold ) + { + if( func_wakeup_cb == NULL ) + { + p_gmac_dev->func_wakeup_cb = NULL; + } + else + { + if( uc_threshold <= p_gmac_dev->ul_tx_list_size ) + { + p_gmac_dev->func_wakeup_cb = func_wakeup_cb; + p_gmac_dev->uc_wakeup_threshold = uc_threshold; + } + else + { + return GMAC_PARAM; + } + } + + return GMAC_OK; + } +#endif /* GMAC_USES_WAKEUP_CALLBACK */ + +/** + * \brief Reset TX & RX queue & statistics. + * + * \param p_gmac_dev Pointer to GMAC device instance. + */ +void gmac_dev_reset( gmac_device_t * p_gmac_dev ) +{ + Gmac * p_hw = p_gmac_dev->p_hw; + + gmac_reset_rx_mem( p_gmac_dev ); + gmac_reset_tx_mem( p_gmac_dev ); + gmac_network_control( p_hw, GMAC_NCR_TXEN | GMAC_NCR_RXEN + | GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT ); +} + +void gmac_dev_halt( Gmac * p_gmac ); + +void gmac_dev_halt( Gmac * p_gmac ) +{ + gmac_network_control( p_gmac, GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT ); + gmac_disable_interrupt( p_gmac, ~0u ); +} + + +/** + * \brief GMAC Interrupt handler. + * + * \param p_gmac_dev Pointer to GMAC device instance. + */ + +#if ( GMAC_STATS != 0 ) + extern int logPrintf( const char * pcFormat, + ... ); + + void gmac_show_irq_counts() + { + int index; + + for( index = 0; index < ARRAY_SIZE( intPairs ); index++ ) + { + if( gmacStats.intStatus[ intPairs[ index ].index ] ) + { + logPrintf( "%s : %6u\n", intPairs[ index ].name, gmacStats.intStatus[ intPairs[ index ].index ] ); + } + } + } +#endif /* if ( GMAC_STATS != 0 ) */ + +void gmac_handler( gmac_device_t * p_gmac_dev ) +{ + Gmac * p_hw = p_gmac_dev->p_hw; + + #if ( GMAC_USES_TX_CALLBACK != 0 ) + gmac_tx_descriptor_t * p_tx_td; + gmac_dev_tx_cb_t * p_tx_cb = NULL; + uint32_t ul_tx_status_flag; + #endif + #if ( GMAC_STATS != 0 ) + int index; + #endif + + /* volatile */ uint32_t ul_isr; + /* volatile */ uint32_t ul_rsr; + /* volatile */ uint32_t ul_tsr; + + ul_isr = gmac_get_interrupt_status( p_hw ); + ul_rsr = gmac_get_rx_status( p_hw ); + ul_tsr = gmac_get_tx_status( p_hw ); + +/* Why clear bits that are ignored anyway ? */ +/* ul_isr &= ~(gmac_get_interrupt_mask(p_hw) | 0xF8030300); */ + #if ( GMAC_STATS != 0 ) + { + for( index = 0; index < ARRAY_SIZE( intPairs ); index++ ) + { + if( ul_isr & intPairs[ index ].mask ) + { + gmacStats.intStatus[ intPairs[ index ].index ]++; + } + } + } + #endif /* GMAC_STATS != 0 */ + + /* RX packet */ + if( ( ul_isr & GMAC_ISR_RCOMP ) || ( ul_rsr & ( GMAC_RSR_REC | GMAC_RSR_RXOVR | GMAC_RSR_BNA ) ) ) + { + /* Clear status */ + gmac_clear_rx_status( p_hw, ul_rsr ); + + if( ul_isr & GMAC_ISR_RCOMP ) + { + ul_rsr |= GMAC_RSR_REC; + } + + /* Invoke callbacks which can be useful to wake up a task */ + if( p_gmac_dev->func_rx_cb ) + { + p_gmac_dev->func_rx_cb( ul_rsr ); + } + } + + /* TX packet */ + if( ( ul_isr & GMAC_ISR_TCOMP ) || ( ul_tsr & ( GMAC_TSR_TXCOMP | GMAC_TSR_COL | GMAC_TSR_RLE | GMAC_TSR_UND ) ) ) + { + #if ( GMAC_USES_TX_CALLBACK != 0 ) + ul_tx_status_flag = GMAC_TSR_TXCOMP; + #endif + /* A frame transmitted */ + + /* Check RLE */ + if( ul_tsr & GMAC_TSR_RLE ) + { + /* Status RLE & Number of discarded buffers */ + #if ( GMAC_USES_TX_CALLBACK != 0 ) + ul_tx_status_flag = GMAC_TSR_RLE | CIRC_CNT( p_gmac_dev->l_tx_head, + p_gmac_dev->l_tx_tail, p_gmac_dev->ul_tx_list_size ); + p_tx_cb = &p_gmac_dev->func_tx_cb_list[ p_gmac_dev->l_tx_tail ]; + #endif + gmac_reset_tx_mem( p_gmac_dev ); + gmac_enable_transmit( p_hw, 1 ); + } + + /* Clear status */ + gmac_clear_tx_status( p_hw, ul_tsr ); + + #if ( GMAC_USES_TX_CALLBACK != 0 ) + if( !CIRC_EMPTY( p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail ) ) + { + /* Check the buffers */ + do + { + p_tx_td = &p_gmac_dev->p_tx_dscr[ p_gmac_dev->l_tx_tail ]; + p_tx_cb = &p_gmac_dev->func_tx_cb_list[ p_gmac_dev->l_tx_tail ]; + + /* Any error? Exit if buffer has not been sent yet */ + if( ( p_tx_td->status.val & GMAC_TXD_USED ) == 0 ) + { + break; + } + + /* Notify upper layer that a packet has been sent */ + if( *p_tx_cb ) + { + ( *p_tx_cb )( ul_tx_status_flag, ( void * ) p_tx_td->addr ); + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + p_tx_td->addr = 0ul; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + } + + circ_inc32( &p_gmac_dev->l_tx_tail, p_gmac_dev->ul_tx_list_size ); + } while( CIRC_CNT( p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail, + p_gmac_dev->ul_tx_list_size ) ); + } + + if( ul_tsr & GMAC_TSR_RLE ) + { + /* Notify upper layer RLE */ + if( *p_tx_cb ) + { + ( *p_tx_cb )( ul_tx_status_flag, NULL ); + } + } + #endif /* GMAC_USES_TX_CALLBACK */ + + #if ( GMAC_USES_WAKEUP_CALLBACK ) + + /* If a wakeup has been scheduled, notify upper layer that it can + * send other packets, and the sending will be successful. */ + if( ( CIRC_SPACE( p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail, + p_gmac_dev->ul_tx_list_size ) >= p_gmac_dev->uc_wakeup_threshold ) && + p_gmac_dev->func_wakeup_cb ) + { + p_gmac_dev->func_wakeup_cb(); + } + #endif + } +} + +/*@} */ + +/*/ @cond 0 */ +/**INDENT-OFF**/ +#ifdef __cplusplus + } +#endif +/**INDENT-ON**/ +/*/ @endcond */ diff --git a/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/gmac.h b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/gmac.h new file mode 100644 index 0000000..a6cd2ac --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/gmac.h @@ -0,0 +1,1489 @@ +/** + * \file + * + * \brief GMAC (Ethernet MAC) driver for SAM. + * + * Copyright (c) 2013 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 + * + */ + +#ifndef GMAC_H_INCLUDED + #define GMAC_H_INCLUDED + + #include "compiler.h" + +/*/ @cond 0 */ +/**INDENT-OFF**/ + #ifdef __cplusplus + extern "C" { + #endif +/**INDENT-ON**/ +/*/ @endcond */ + +/** The buffer addresses written into the descriptors must be aligned, so the + * last few bits are zero. These bits have special meaning for the GMAC + * peripheral and cannot be used as part of the address. */ + #define GMAC_RXD_ADDR_MASK 0xFFFFFFFC + #define GMAC_RXD_WRAP ( 1ul << 1 ) /**< Wrap bit */ + #define GMAC_RXD_OWNERSHIP ( 1ul << 0 ) /**< Ownership bit */ + + #define GMAC_RXD_BROADCAST ( 1ul << 31 ) /**< Broadcast detected */ + #define GMAC_RXD_MULTIHASH ( 1ul << 30 ) /**< Multicast hash match */ + #define GMAC_RXD_UNIHASH ( 1ul << 29 ) /**< Unicast hash match */ + #define GMAC_RXD_ADDR_FOUND ( 1ul << 27 ) /**< Specific address match found */ + #define GMAC_RXD_ADDR ( 3ul << 25 ) /**< Address match */ + #define GMAC_RXD_RXCOEN ( 1ul << 24 ) /**< RXCOEN related function */ + #define GMAC_RXD_TYPE ( 3ul << 22 ) /**< Type ID match */ + #define GMAC_RXD_VLAN ( 1ul << 21 ) /**< VLAN tag detected */ + #define GMAC_RXD_PRIORITY ( 1ul << 20 ) /**< Priority tag detected */ + #define GMAC_RXD_PRIORITY_MASK ( 3ul << 17 ) /**< VLAN priority */ + #define GMAC_RXD_CFI ( 1ul << 16 ) /**< Concatenation Format Indicator only if bit 21 is set */ + #define GMAC_RXD_EOF ( 1ul << 15 ) /**< End of frame */ + #define GMAC_RXD_SOF ( 1ul << 14 ) /**< Start of frame */ + #define GMAC_RXD_FCS ( 1ul << 13 ) /**< Frame check sequence */ + #define GMAC_RXD_OFFSET_MASK /**< Receive buffer offset */ + #define GMAC_RXD_LEN_MASK ( 0xFFF ) /**< Length of frame including FCS (if selected) */ + #define GMAC_RXD_LENJUMBO_MASK ( 0x3FFF ) /**< Jumbo frame length */ + + #define GMAC_TXD_USED ( 1ul << 31 ) /**< Frame is transmitted */ + #define GMAC_TXD_WRAP ( 1ul << 30 ) /**< Last descriptor */ + #define GMAC_TXD_ERROR ( 1ul << 29 ) /**< Retry limit exceeded, error */ + #define GMAC_TXD_UNDERRUN ( 1ul << 28 ) /**< Transmit underrun */ + #define GMAC_TXD_EXHAUSTED ( 1ul << 27 ) /**< Buffer exhausted */ + #define GMAC_TXD_LATE ( 1ul << 26 ) /**< Late collision,transmit error */ + #define GMAC_TXD_CHECKSUM_ERROR ( 7ul << 20 ) /**< Checksum error */ + #define GMAC_TXD_NOCRC ( 1ul << 16 ) /**< No CRC */ + #define GMAC_TXD_LAST ( 1ul << 15 ) /**< Last buffer in frame */ + #define GMAC_TXD_LEN_MASK ( 0x1FFF ) /**< Length of buffer */ + +/** The MAC can support frame lengths up to 1536 bytes */ + #define GMAC_FRAME_LENTGH_MAX 1536 + + #define GMAC_RX_UNITSIZE 128 /**< Fixed size for RX buffer */ + #define GMAC_TX_UNITSIZE 1518 /**< Size for ETH frame length */ + +/** GMAC clock speed */ + #define GMAC_MCK_SPEED_240MHZ ( 240 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_160MHZ ( 160 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_120MHZ ( 120 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_80MHZ ( 80 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_40MHZ ( 40 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_20MHZ ( 20 * 1000 * 1000 ) + +/** GMAC maintain code default value*/ + #define GMAC_MAN_CODE_VALUE ( 10 ) + +/** GMAC maintain start of frame default value*/ + #define GMAC_MAN_SOF_VALUE ( 1 ) + +/** GMAC maintain read/write*/ + #define GMAC_MAN_RW_TYPE ( 2 ) + +/** GMAC maintain read only*/ + #define GMAC_MAN_READ_ONLY ( 1 ) + +/** GMAC address length */ + #define GMAC_ADDR_LENGTH ( 6 ) + + + #define GMAC_DUPLEX_HALF 0 + #define GMAC_DUPLEX_FULL 1 + + #define GMAC_SPEED_10M 0 + #define GMAC_SPEED_100M 1 + +/** + * \brief Return codes for GMAC APIs. + */ + typedef enum + { + GMAC_OK = 0, /** 0 Operation OK */ + GMAC_TIMEOUT = 1, /** 1 GMAC operation timeout */ + GMAC_TX_BUSY, /** 2 TX in progress */ + GMAC_RX_NULL, /** 3 No data received */ + GMAC_SIZE_TOO_SMALL, /** 4 Buffer size not enough */ + GMAC_PARAM, /** 5 Parameter error, TX packet invalid or RX size too small */ + GMAC_INVALID = 0xFF, /* Invalid */ + } gmac_status_t; + +/** + * \brief Media Independent Interface (MII) type. + */ + typedef enum + { + GMAC_PHY_MII = 0, /** MII mode */ + GMAC_PHY_RMII = 1, /** Reduced MII mode */ + GMAC_PHY_INVALID = 0xFF, /* Invalid mode*/ + } gmac_mii_mode_t; + +/** Receive buffer descriptor struct */ + COMPILER_PACK_SET( 8 ) + typedef struct gmac_rx_descriptor + { + union gmac_rx_addr + { + uint32_t val; + struct gmac_rx_addr_bm + { + uint32_t b_ownership : 1, /**< User clear, GMAC sets this to 1 once it has successfully written a frame to memory */ + b_wrap : 1, /**< Marks last descriptor in receive buffer */ + addr_dw : 30; /**< Address in number of DW */ + } bm; + } addr; /**< Address, Wrap & Ownership */ + union gmac_rx_status + { + uint32_t val; + struct gmac_rx_status_bm + { + uint32_t len : 13, /** 0..12 Length of frame including FCS */ + b_fcs : 1, /** 13 Receive buffer offset, bits 13:12 of frame length for jumbo frame */ + b_sof : 1, /** 14 Start of frame */ + b_eof : 1, /** 15 End of frame */ + b_cfi : 1, /** 16 Concatenation Format Indicator */ + vlan_priority : 3, /** 17..19 VLAN priority (if VLAN detected) */ + b_priority_detected : 1, /** 20 Priority tag detected */ + b_vlan_detected : 1, /** 21 VLAN tag detected */ + b_type_id_match : 2, /** 22..23 Type ID match */ + b_checksumoffload : 1, /** 24 Checksum offload specific function */ + b_addrmatch : 2, /** 25..26 Address register match */ + b_ext_addr_match : 1, /** 27 External address match found */ + reserved : 1, /** 28 */ + b_uni_hash_match : 1, /** 29 Unicast hash match */ + b_multi_hash_match : 1, /** 30 Multicast hash match */ + b_boardcast_detect : 1; /** 31 Global broadcast address detected */ + } bm; + } status; + } gmac_rx_descriptor_t; + +/** Transmit buffer descriptor struct */ + COMPILER_PACK_SET( 8 ) + typedef struct gmac_tx_descriptor + { + uint32_t addr; + union gmac_tx_status + { + uint32_t val; + struct gmac_tx_status_bm + { + uint32_t len : 14, /** 0..13 Length of buffer */ + reserved : 1, /** 14 */ + b_last_buffer : 1, /** 15 Last buffer (in the current frame) */ + b_no_crc : 1, /** 16 No CRC */ + reserved1 : 3, /** 17..19 */ + b_checksumoffload : 3, /** 20..22 Transmit checksum generation offload errors */ + reserved2 : 3, /** 23..25 */ + b_lco : 1, /** 26 Late collision, transmit error detected */ + b_exhausted : 1, /** 27 Buffer exhausted in mid frame */ + b_underrun : 1, /** 28 Transmit underrun */ + b_error : 1, /** 29 Retry limit exceeded, error detected */ + b_wrap : 1, /** 30 Marks last descriptor in TD list */ + b_used : 1; /** 31 User clear, GMAC sets this to 1 once a frame has been successfully transmitted */ + } bm; + } status; + } gmac_tx_descriptor_t; + + COMPILER_PACK_RESET() + +/** + * \brief Input parameters when initializing the gmac module mode. + */ + typedef struct gmac_options + { + /* Enable/Disable CopyAllFrame */ + uint8_t uc_copy_all_frame; + /* Enable/Disable NoBroadCast */ + uint8_t uc_no_boardcast; + /* MAC address */ + uint8_t uc_mac_addr[ GMAC_ADDR_LENGTH ]; + } gmac_options_t; + +/** RX callback */ + typedef void (* gmac_dev_tx_cb_t) ( uint32_t ul_status ); +/** Wakeup callback */ + typedef void (* gmac_dev_wakeup_cb_t) ( void ); + +/** + * GMAC driver structure. + */ + typedef struct gmac_device + { + /** Pointer to HW register base */ + Gmac * p_hw; + + /** + * Pointer to allocated TX buffer. + * Section 3.6 of AMBA 2.0 spec states that burst should not cross + * 1K Boundaries. + * Receive buffer manager writes are burst of 2 words => 3 lsb bits + * of the address shall be set to 0. + */ + uint8_t * p_tx_buffer; + /** Pointer to allocated RX buffer */ + uint8_t * p_rx_buffer; + /** Pointer to Rx TDs (must be 8-byte aligned) */ + gmac_rx_descriptor_t * p_rx_dscr; + /** Pointer to Tx TDs (must be 8-byte aligned) */ + gmac_tx_descriptor_t * p_tx_dscr; + /** Optional callback to be invoked once a frame has been received */ + gmac_dev_tx_cb_t func_rx_cb; + #if ( GMAC_USES_WAKEUP_CALLBACK ) + /** Optional callback to be invoked once several TDs have been released */ + gmac_dev_wakeup_cb_t func_wakeup_cb; + #endif + #if ( GMAC_USES_TX_CALLBACK != 0 ) + /** Optional callback list to be invoked once TD has been processed */ + gmac_dev_tx_cb_t * func_tx_cb_list; + #endif + /** RX TD list size */ + uint32_t ul_rx_list_size; + /** RX index for current processing TD */ + uint32_t ul_rx_idx; + /** TX TD list size */ + uint32_t ul_tx_list_size; + /** Circular buffer head pointer by upper layer (buffer to be sent) */ + int32_t l_tx_head; + /** Circular buffer tail pointer incremented by handlers (buffer sent) */ + int32_t l_tx_tail; + + /** Number of free TD before wakeup callback is invoked */ + uint32_t uc_wakeup_threshold; + } gmac_device_t; + +/** + * \brief Write network control value. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_ncr Network control value. + */ + static inline void gmac_network_control( Gmac * p_gmac, + uint32_t ul_ncr ) + { + p_gmac->GMAC_NCR = ul_ncr; + } + +/** + * \brief Get network control value. + * + * \param p_gmac Pointer to the GMAC instance. + */ + + static inline uint32_t gmac_get_network_control( Gmac * p_gmac ) + { + return p_gmac->GMAC_NCR; + } + +/** + * \brief Enable/Disable GMAC receive. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC receiver, else to enable it. + */ + static inline void gmac_enable_receive( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_RXEN; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_RXEN; + } + } + +/** + * \brief Enable/Disable GMAC transmit. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC transmit, else to enable it. + */ + static inline void gmac_enable_transmit( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXEN; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_TXEN; + } + } + +/** + * \brief Enable/Disable GMAC management. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC management, else to enable it. + */ + static inline void gmac_enable_management( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_MPE; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_MPE; + } + } + +/** + * \brief Clear all statistics registers. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_clear_statistics( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_CLRSTAT; + } + +/** + * \brief Increase all statistics registers. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_increase_statistics( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_INCSTAT; + } + +/** + * \brief Enable/Disable statistics registers writing. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the statistics registers writing, else to enable it. + */ + static inline void gmac_enable_statistics_write( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_WESTAT; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_WESTAT; + } + } + +/** + * \brief In half-duplex mode, forces collisions on all received frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the back pressure, else to enable it. + */ + static inline void gmac_enable_back_pressure( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_BP; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_BP; + } + } + +/** + * \brief Start transmission. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_start_transmission( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TSTART; + } + +/** + * \brief Halt transmission. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_halt_transmission( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_THALT; + } + +/** + * \brief Transmit pause frame. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_tx_pause_frame( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXPF; + } + +/** + * \brief Transmit zero quantum pause frame. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_tx_pause_zero_quantum_frame( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXZQPF; + } + +/** + * \brief Read snapshot. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_read_snapshot( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_RDS; + } + +/** + * \brief Store receivetime stamp to memory. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to normal operation, else to enable the store. + */ + static inline void gmac_store_rx_time_stamp( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_SRTSM; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_SRTSM; + } + } + +/** + * \brief Enable PFC priority-based pause reception. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 1 to set the reception, 0 to disable. + */ + static inline void gmac_enable_pfc_pause_frame( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_ENPBPR; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_ENPBPR; + } + } + +/** + * \brief Transmit PFC priority-based pause reception. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_transmit_pfc_pause_frame( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXPBPF; + } + +/** + * \brief Flush next packet. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_flush_next_packet( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_FNP; + } + +/** + * \brief Set up network configuration register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_cfg Network configuration value. + */ + static inline void gmac_set_configure( Gmac * p_gmac, + uint32_t ul_cfg ) + { + p_gmac->GMAC_NCFGR = ul_cfg; + } + +/** + * \brief Get network configuration. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Network configuration. + */ + static inline uint32_t gmac_get_configure( Gmac * p_gmac ) + { + return p_gmac->GMAC_NCFGR; + } + + +/* Get and set DMA Configuration Register */ + static inline void gmac_set_dma( Gmac * p_gmac, + uint32_t ul_cfg ) + { + p_gmac->GMAC_DCFGR = ul_cfg; + } + + static inline uint32_t gmac_get_dma( Gmac * p_gmac ) + { + return p_gmac->GMAC_DCFGR; + } + +/** + * \brief Set speed. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_speed 1 to indicate 100Mbps, 0 to 10Mbps. + */ + static inline void gmac_set_speed( Gmac * p_gmac, + uint8_t uc_speed ) + { + if( uc_speed ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_SPD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_SPD; + } + } + +/** + * \brief Enable/Disable Full-Duplex mode. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the Full-Duplex mode, else to enable it. + */ + static inline void gmac_enable_full_duplex( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_FD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_FD; + } + } + +/** + * \brief Enable/Disable Copy(Receive) All Valid Frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable copying all valid frames, else to enable it. + */ + static inline void gmac_enable_copy_all( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_CAF; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_CAF; + } + } + +/** + * \brief Enable/Disable jumbo frames (up to 10240 bytes). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the jumbo frames, else to enable it. + */ + static inline void gmac_enable_jumbo_frames( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_JFRAME; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_JFRAME; + } + } + +/** + * \brief Disable/Enable broadcast receiving. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 1 to disable the broadcast, else to enable it. + */ + static inline void gmac_disable_broadcast( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_NBC; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_NBC; + } + } + +/** + * \brief Enable/Disable multicast hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the multicast hash, else to enable it. + */ + static inline void gmac_enable_multicast_hash( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_UNIHEN; + } + } + +/** + * \brief Enable/Disable big frames (over 1518, up to 1536). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable big frames else to enable it. + */ + static inline void gmac_enable_big_frame( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_MAXFS; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_MAXFS; + } + } + +/** + * \brief Set MDC clock divider. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_mck GMAC MCK. + * + * \return GMAC_OK if successfully. + */ + static inline uint8_t gmac_set_mdc_clock( Gmac * p_gmac, + uint32_t ul_mck ) + { + uint32_t ul_clk; + + if( ul_mck > GMAC_MCK_SPEED_240MHZ ) + { + return GMAC_INVALID; + } + else if( ul_mck > GMAC_MCK_SPEED_160MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_96; + } + else if( ul_mck > GMAC_MCK_SPEED_120MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_64; + } + else if( ul_mck > GMAC_MCK_SPEED_80MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_48; + } + else if( ul_mck > GMAC_MCK_SPEED_40MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_32; + } + else if( ul_mck > GMAC_MCK_SPEED_20MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_16; + } + else + { + ul_clk = GMAC_NCFGR_CLK_MCK_8; + } + + p_gmac->GMAC_NCFGR = ( p_gmac->GMAC_NCFGR & ~GMAC_NCFGR_CLK_Msk ) | ul_clk; + return GMAC_OK; + } + +/** + * \brief Enable/Disable retry test. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the GMAC receiver, else to enable it. + */ + static inline void gmac_enable_retry_test( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RTY; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RTY; + } + } + +/** + * \brief Enable/Disable pause (when a valid pause frame is received). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable pause frame, else to enable it. + */ + static inline void gmac_enable_pause_frame( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_PEN; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_PEN; + } + } + +/** + * \brief Set receive buffer offset to 0 ~ 3. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_set_rx_buffer_offset( Gmac * p_gmac, + uint8_t uc_offset ) + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RXBUFO_Msk; + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RXBUFO( uc_offset ); + } + +/** + * \brief Enable/Disable receive length field checking. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable receive length field checking, else to enable it. + */ + static inline void gmac_enable_rx_length_check( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_LFERD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_LFERD; + } + } + +/** + * \brief Enable/Disable discarding FCS field of received frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable discarding FCS field of received frames, else to enable it. + */ + static inline void gmac_enable_discard_fcs( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RFCS; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RFCS; + } + } + + +/** + * \brief Enable/Disable frames to be received in half-duplex mode + * while transmitting. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the received in half-duplex mode, else to enable it. + */ + static inline void gmac_enable_efrhd( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_EFRHD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_EFRHD; + } + } + +/** + * \brief Enable/Disable ignore RX FCS. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable ignore RX FCS, else to enable it. + */ + static inline void gmac_enable_ignore_rx_fcs( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_IRXFCS; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_IRXFCS; + } + } + +/** + * \brief Get Network Status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Network status. + */ + static inline uint32_t gmac_get_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_NSR; + } + +/** + * \brief Get MDIO IN pin status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return MDIO IN pin status. + */ + static inline uint8_t gmac_get_MDIO( Gmac * p_gmac ) + { + return( ( p_gmac->GMAC_NSR & GMAC_NSR_MDIO ) > 0 ); + } + +/** + * \brief Check if PHY is idle. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return 1 if PHY is idle. + */ + static inline uint8_t gmac_is_phy_idle( Gmac * p_gmac ) + { + return( ( p_gmac->GMAC_NSR & GMAC_NSR_IDLE ) > 0 ); + } + +/** + * \brief Return transmit status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Transmit status. + */ + static inline uint32_t gmac_get_tx_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_TSR; + } + +/** + * \brief Clear transmit status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_status Transmit status. + */ + static inline void gmac_clear_tx_status( Gmac * p_gmac, + uint32_t ul_status ) + { + p_gmac->GMAC_TSR = ul_status; + } + +/** + * \brief Return receive status. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline uint32_t gmac_get_rx_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_RSR; + } + +/** + * \brief Clear receive status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_status Receive status. + */ + static inline void gmac_clear_rx_status( Gmac * p_gmac, + uint32_t ul_status ) + { + p_gmac->GMAC_RSR = ul_status; + } + +/** + * \brief Set Rx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Rx queue address. + */ + static inline void gmac_set_rx_queue( Gmac * p_gmac, + uint32_t ul_addr ) + { + p_gmac->GMAC_RBQB = GMAC_RBQB_ADDR_Msk & ul_addr; + } + +/** + * \brief Get Rx Queue Address. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Rx queue address. + */ + static inline uint32_t gmac_get_rx_queue( Gmac * p_gmac ) + { + return p_gmac->GMAC_RBQB; + } + +/** + * \brief Set Tx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Tx queue address. + */ + static inline void gmac_set_tx_queue( Gmac * p_gmac, + uint32_t ul_addr ) + { + p_gmac->GMAC_TBQB = GMAC_TBQB_ADDR_Msk & ul_addr; + } + +/** + * \brief Get Tx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Rx queue address. + */ + static inline uint32_t gmac_get_tx_queue( Gmac * p_gmac ) + { + return p_gmac->GMAC_TBQB; + } + +/** + * \brief Enable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be enabled. + */ + static inline void gmac_enable_interrupt( Gmac * p_gmac, + uint32_t ul_source ) + { + p_gmac->GMAC_IER = ul_source; + } + +/** + * \brief Disable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be disabled. + */ + static inline void gmac_disable_interrupt( Gmac * p_gmac, + uint32_t ul_source ) + { + p_gmac->GMAC_IDR = ul_source; + } + +/** + * \brief Return interrupt status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Interrupt status. + */ + static inline uint32_t gmac_get_interrupt_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_ISR; + } + +/** + * \brief Return interrupt mask. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Interrupt mask. + */ + static inline uint32_t gmac_get_interrupt_mask( Gmac * p_gmac ) + { + return p_gmac->GMAC_IMR; + } + +/** + * \brief Execute PHY maintenance command. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * \param uc_reg_addr Register address. + * \param uc_rw 1 to Read, 0 to write. + * \param us_data Data to be performed, write only. + */ + static inline void gmac_maintain_phy( Gmac * p_gmac, + uint8_t uc_phy_addr, + uint8_t uc_reg_addr, + uint8_t uc_rw, + uint16_t us_data ) + { + /* Wait until bus idle */ + while( ( p_gmac->GMAC_NSR & GMAC_NSR_IDLE ) == 0 ) + { + } + + /* Write maintain register */ + p_gmac->GMAC_MAN = GMAC_MAN_WTN( GMAC_MAN_CODE_VALUE ) + | GMAC_MAN_CLTTO + | GMAC_MAN_PHYA( uc_phy_addr ) + | GMAC_MAN_REGA( uc_reg_addr ) + | GMAC_MAN_OP( ( uc_rw ? GMAC_MAN_RW_TYPE : GMAC_MAN_READ_ONLY ) ) + | GMAC_MAN_DATA( us_data ); + } + +/** + * \brief Get PHY maintenance data returned. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Get PHY data. + */ + static inline uint16_t gmac_get_phy_data( Gmac * p_gmac ) + { + /* Wait until bus idle */ + while( ( p_gmac->GMAC_NSR & GMAC_NSR_IDLE ) == 0 ) + { + } + + /* Return data */ + return ( uint16_t ) ( p_gmac->GMAC_MAN & GMAC_MAN_DATA_Msk ); + } + +/** + * \brief Set Hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_hash_top Hash top. + * \param ul_hash_bottom Hash bottom. + */ + static inline void gmac_set_hash( Gmac * p_gmac, + uint32_t ul_hash_top, + uint32_t ul_hash_bottom ) + { + p_gmac->GMAC_HRB = ul_hash_bottom; + p_gmac->GMAC_HRT = ul_hash_top; + } + +/** + * \brief Set 64 bits Hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ull_hash 64 bits hash value. + */ + static inline void gmac_set_hash64( Gmac * p_gmac, + uint64_t ull_hash ) + { + p_gmac->GMAC_HRB = ( uint32_t ) ull_hash; + p_gmac->GMAC_HRT = ( uint32_t ) ( ull_hash >> 32 ); + } + +/** + * \brief Set MAC Address. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param p_mac_addr GMAC address. + */ + static inline void gmac_set_address( Gmac * p_gmac, + uint8_t uc_index, + uint8_t * p_mac_addr ) + { + p_gmac->GMAC_SA[ uc_index ].GMAC_SAB = ( p_mac_addr[ 3 ] << 24 ) + | ( p_mac_addr[ 2 ] << 16 ) + | ( p_mac_addr[ 1 ] << 8 ) + | ( p_mac_addr[ 0 ] ); + p_gmac->GMAC_SA[ uc_index ].GMAC_SAT = ( p_mac_addr[ 5 ] << 8 ) + | ( p_mac_addr[ 4 ] ); + } + +/** + * \brief Set MAC Address via 2 dword. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param ul_mac_top GMAC top address. + * \param ul_mac_bottom GMAC bottom address. + */ + static inline void gmac_set_address32( Gmac * p_gmac, + uint8_t uc_index, + uint32_t ul_mac_top, + uint32_t ul_mac_bottom ) + { + p_gmac->GMAC_SA[ uc_index ].GMAC_SAB = ul_mac_bottom; + p_gmac->GMAC_SA[ uc_index ].GMAC_SAT = ul_mac_top; + } + +/** + * \brief Set MAC Address via int64. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param ull_mac 64-bit GMAC address. + */ + static inline void gmac_set_address64( Gmac * p_gmac, + uint8_t uc_index, + uint64_t ull_mac ) + { + p_gmac->GMAC_SA[ uc_index ].GMAC_SAB = ( uint32_t ) ull_mac; + p_gmac->GMAC_SA[ uc_index ].GMAC_SAT = ( uint32_t ) ( ull_mac >> 32 ); + } + +/** + * \brief Select media independent interface mode. + * + * \param p_gmac Pointer to the GMAC instance. + * \param mode Media independent interface mode. + */ + static inline void gmac_select_mii_mode( Gmac * p_gmac, + gmac_mii_mode_t mode ) + { + switch( mode ) + { + case GMAC_PHY_MII: + case GMAC_PHY_RMII: + p_gmac->GMAC_UR |= GMAC_UR_RMIIMII; + break; + + default: + p_gmac->GMAC_UR &= ~GMAC_UR_RMIIMII; + break; + } + } + + uint8_t gmac_phy_read( Gmac * p_gmac, + uint8_t uc_phy_address, + uint8_t uc_address, + uint32_t * p_value ); + uint8_t gmac_phy_write( Gmac * p_gmac, + uint8_t uc_phy_address, + uint8_t uc_address, + uint32_t ul_value ); + void gmac_dev_init( Gmac * p_gmac, + gmac_device_t * p_gmac_dev, + gmac_options_t * p_opt ); + uint32_t gmac_dev_read( gmac_device_t * p_gmac_dev, + uint8_t * p_frame, + uint32_t ul_frame_size, + uint32_t * p_rcv_size ); + uint32_t gmac_dev_write( gmac_device_t * p_gmac_dev, + void * p_buffer, + uint32_t ul_size, + gmac_dev_tx_cb_t func_tx_cb ); + uint32_t gmac_dev_get_tx_load( gmac_device_t * p_gmac_dev ); + void gmac_dev_set_rx_callback( gmac_device_t * p_gmac_dev, + gmac_dev_tx_cb_t func_rx_cb ); + uint8_t gmac_dev_set_tx_wakeup_callback( gmac_device_t * p_gmac_dev, + gmac_dev_wakeup_cb_t func_wakeup, + uint8_t uc_threshold ); + void gmac_dev_reset( gmac_device_t * p_gmac_dev ); + void gmac_handler( gmac_device_t * p_gmac_dev ); + +/*/ @cond 0 */ +/**INDENT-OFF**/ + #ifdef __cplusplus + } + #endif +/**INDENT-ON**/ +/*/ @endcond */ + +/** + * \page gmac_quickstart Quickstart guide for GMAC driver. + * + * This is the quickstart guide for the \ref gmac_group "Ethernet MAC", + * with step-by-step instructions on how to configure and use the driver in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section gmac_basic_use_case Basic use case + * In the basic use case, the GMAC driver are configured for: + * - PHY component KSZ8051MNL is used + * - GMAC uses MII mode + * - The number of receive buffer is 16 + * - The number of transfer buffer is 8 + * - MAC address is set to 00-04-25-1c-a0-02 + * - IP address is set to 192.168.0.2 + * - IP address is set to 192.168.0.2 + * - Gateway is set to 192.168.0.1 + * - Network mask is 255.255.255.0 + * - PHY operation max retry count is 1000000 + * - GMAC is configured to not support copy all frame and support broadcast + * - The data will be read from the ethernet + * + * \section gmac_basic_use_case_setup Setup steps + * + * \subsection gmac_basic_use_case_setup_prereq Prerequisites + * -# \ref sysclk_group "System Clock Management (sysclock)" + * -# \ref pmc_group "Power Management Controller (pmc)" + * -# \ref ksz8051mnl_ethernet_phy_group "PHY component (KSZ8051MNL)" + * + * \subsection gmac_basic_use_case_setup_code Example code + * Content of conf_eth.h + * \code + * #define GMAC_RX_BUFFERS 16 + * #define GMAC_TX_BUFFERS 8 + * #define MAC_PHY_RETRY_MAX 1000000 + * #define ETHERNET_CONF_ETHADDR0 0x00 + * #define ETHERNET_CONF_ETHADDR0 0x00 + * #define ETHERNET_CONF_ETHADDR1 0x04 + * #define ETHERNET_CONF_ETHADDR2 0x25 + * #define ETHERNET_CONF_ETHADDR3 0x1C + * #define ETHERNET_CONF_ETHADDR4 0xA0 + * #define ETHERNET_CONF_ETHADDR5 0x02 + * #define ETHERNET_CONF_IPADDR0 192 + * #define ETHERNET_CONF_IPADDR1 168 + * #define ETHERNET_CONF_IPADDR2 0 + * #define ETHERNET_CONF_IPADDR3 2 + * #define ETHERNET_CONF_GATEWAY_ADDR0 192 + * #define ETHERNET_CONF_GATEWAY_ADDR1 168 + * #define ETHERNET_CONF_GATEWAY_ADDR2 0 + * #define ETHERNET_CONF_GATEWAY_ADDR3 1 + * #define ETHERNET_CONF_NET_MASK0 255 + * #define ETHERNET_CONF_NET_MASK1 255 + * #define ETHERNET_CONF_NET_MASK2 255 + * #define ETHERNET_CONF_NET_MASK3 0 + * #define ETH_PHY_MODE ETH_PHY_MODE + * \endcode + * + * A specific gmac device and the receive data buffer must be defined; another ul_frm_size should be defined + * to trace the actual size of the data received. + * \code + * static gmac_device_t gs_gmac_dev; + * static volatile uint8_t gs_uc_eth_buffer[GMAC_FRAME_LENTGH_MAX]; + * + * uint32_t ul_frm_size; + * \endcode + * + * Add to application C-file: + * \code + * void gmac_init(void) + * { + * sysclk_init(); + * + * board_init(); + * + * pmc_enable_periph_clk(ID_GMAC); + * + * gmac_option.uc_copy_all_frame = 0; + * gmac_option.uc_no_boardcast = 0; + * memcpy(gmac_option.uc_mac_addr, gs_uc_mac_address, sizeof(gs_uc_mac_address)); + * gs_gmac_dev.p_hw = GMAC; + * + * gmac_dev_init(GMAC, &gs_gmac_dev, &gmac_option); + * + * NVIC_EnableIRQ(GMAC_IRQn); + * + * ethernet_phy_init(GMAC, BOARD_GMAC_PHY_ADDR, sysclk_get_cpu_hz()); + * + * ethernet_phy_auto_negotiate(GMAC, BOARD_GMAC_PHY_ADDR); + * + * ethernet_phy_set_link(GMAC, BOARD_GMAC_PHY_ADDR, 1); + * \endcode + * + * \subsection gmac_basic_use_case_setup_flow Workflow + * - Ensure that conf_eth.h is present and contains the + * following configuration symbol. This configuration file is used + * by the driver and should not be included by the application. + * -# Define the receiving buffer size used in the internal GMAC driver. + * The buffer size used for RX is GMAC_RX_BUFFERS * 128. + * If it was supposed receiving a large number of frame, the + * GMAC_RX_BUFFERS should be set higher. E.g., the application wants to accept + * a ping echo test of 2048, the GMAC_RX_BUFFERS should be set at least + * (2048/128)=16, and as there are additional frames coming, a preferred + * number is 24 depending on a normal Ethernet throughput. + * - \code + * #define GMAC_RX_BUFFERS 16 + * \endcode + * -# Define the transmitting buffer size used in the internal GMAC driver. + * The buffer size used for TX is GMAC_TX_BUFFERS * 1518. + * - \code + * #define GMAC_TX_BUFFERS 8 + * \endcode + * -# Define maximum retry time for a PHY read/write operation. + * - \code + * #define MAC_PHY_RETRY_MAX 1000000 + * \endcode + * -# Define the MAC address. 00:04:25:1C:A0:02 is the address reserved + * for ATMEL, application should always change this address to its' own. + * - \code + * #define ETHERNET_CONF_ETHADDR0 0x00 + * #define ETHERNET_CONF_ETHADDR1 0x04 + * #define ETHERNET_CONF_ETHADDR2 0x25 + * #define ETHERNET_CONF_ETHADDR3 0x1C + * #define ETHERNET_CONF_ETHADDR4 0xA0 + * #define ETHERNET_CONF_ETHADDR5 0x02 + * \endcode + * -# Define the IP address configuration used in the application. When DHCP + * is enabled, this configuration is not effected. + * - \code + * #define ETHERNET_CONF_IPADDR0 192 + * #define ETHERNET_CONF_IPADDR1 168 + * #define ETHERNET_CONF_IPADDR2 0 + * #define ETHERNET_CONF_IPADDR3 2 + * #define ETHERNET_CONF_GATEWAY_ADDR0 192 + * #define ETHERNET_CONF_GATEWAY_ADDR1 168 + * #define ETHERNET_CONF_GATEWAY_ADDR2 0 + * #define ETHERNET_CONF_GATEWAY_ADDR3 1 + * #define ETHERNET_CONF_NET_MASK0 255 + * #define ETHERNET_CONF_NET_MASK1 255 + * #define ETHERNET_CONF_NET_MASK2 255 + * #define ETHERNET_CONF_NET_MASK3 0 + * \endcode + * -# Configure the PHY maintainance interface. + * - \code + * #define ETH_PHY_MODE GMAC_PHY_MII + * \endcode + * -# Enable the system clock: + * - \code sysclk_init(); \endcode + * -# Enable PIO configurations for GMAC: + * - \code board_init(); \endcode + * -# Enable PMC clock for GMAC: + * - \code pmc_enable_periph_clk(ID_GMAC); \endcode + * -# Set the GMAC options; it's set to copy all frame and support broadcast: + * - \code + * gmac_option.uc_copy_all_frame = 0; + * gmac_option.uc_no_boardcast = 0; + * memcpy(gmac_option.uc_mac_addr, gs_uc_mac_address, sizeof(gs_uc_mac_address)); + * gs_gmac_dev.p_hw = GMAC; + * \endcode + * -# Initialize GMAC device with the filled option: + * - \code + * gmac_dev_init(GMAC, &gs_gmac_dev, &gmac_option); + * \endcode + * -# Enable the interrupt service for GMAC: + * - \code + * NVIC_EnableIRQ(GMAC_IRQn); + * \endcode + * -# Initialize the PHY component: + * - \code + * ethernet_phy_init(GMAC, BOARD_GMAC_PHY_ADDR, sysclk_get_cpu_hz()); + * \endcode + * -# The link will be established based on auto negotiation. + * - \code + * ethernet_phy_auto_negotiate(GMAC, BOARD_GMAC_PHY_ADDR); + * \endcode + * -# Establish the ethernet link; the network can be worked from now on: + * - \code + * ethernet_phy_set_link(GMAC, BOARD_GMAC_PHY_ADDR, 1); + * \endcode + * + * \section gmac_basic_use_case_usage Usage steps + * \subsection gmac_basic_use_case_usage_code Example code + * Add to, e.g., main loop in application C-file: + * \code + * gmac_dev_read(&gs_gmac_dev, (uint8_t *) gs_uc_eth_buffer, sizeof(gs_uc_eth_buffer), &ul_frm_size)); + * \endcode + * + * \subsection gmac_basic_use_case_usage_flow Workflow + * -# Start reading the data from the ethernet: + * - \code gmac_dev_read(&gs_gmac_dev, (uint8_t *) gs_uc_eth_buffer, sizeof(gs_uc_eth_buffer), &ul_frm_size)); \endcode + */ + + #define GMAC_STATS 0 + + #if ( GMAC_STATS != 0 ) + +/* Here below some code to study the types and + * frequencies of GMAC interrupts. */ + #define GMAC_IDX_RXUBR 0 + #define GMAC_IDX_TUR 1 + #define GMAC_IDX_RLEX 2 + #define GMAC_IDX_TFC 3 + #define GMAC_IDX_RCOMP 4 + #define GMAC_IDX_TCOMP 5 + #define GMAC_IDX_ROVR 6 + #define GMAC_IDX_HRESP 7 + #define GMAC_IDX_PFNZ 8 + #define GMAC_IDX_PTZ 9 + + struct SGmacStats + { + unsigned recvCount; + unsigned rovrCount; + unsigned bnaCount; + unsigned sendCount; + unsigned sovrCount; + unsigned incompCount; + unsigned truncCount; + + unsigned intStatus[ 10 ]; + }; + extern struct SGmacStats gmacStats; + + struct SIntPair + { + const char * name; + unsigned mask; + int index; + }; + + #define MK_PAIR( NAME ) # NAME, GMAC_IER_ ## NAME, GMAC_IDX_ ## NAME + static const struct SIntPair intPairs[] = { + { MK_PAIR( RXUBR ) }, /* Enable receive used bit read interrupt. */ + { MK_PAIR( TUR ) }, /* Enable transmit underrun interrupt. */ + { MK_PAIR( RLEX ) }, /* Enable retry limit exceeded interrupt. */ + { MK_PAIR( TFC ) }, /* Enable transmit buffers exhausted in mid-frame interrupt. */ + { MK_PAIR( RCOMP ) }, /* Receive complete */ + { MK_PAIR( TCOMP ) }, /* Enable transmit complete interrupt. */ + { MK_PAIR( ROVR ) }, /* Enable receive overrun interrupt. */ + { MK_PAIR( HRESP ) }, /* Enable Hresp not OK interrupt. */ + { MK_PAIR( PFNZ ) }, /* Enable pause frame received interrupt. */ + { MK_PAIR( PTZ ) } /* Enable pause time zero interrupt. */ + }; + + void gmac_show_irq_counts(); + + #endif /* if ( GMAC_STATS != 0 ) */ + +#endif /* GMAC_H_INCLUDED */ diff --git a/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/instance/gmac.h b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/instance/gmac.h new file mode 100644 index 0000000..b49ed8f --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ATSAM4E/instance/gmac.h @@ -0,0 +1,1493 @@ +/** + * \file + * + * \brief GMAC (Ethernet MAC) driver for SAM. + * + * Copyright (c) 2013 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 + * + */ + +#ifndef GMAC_H_INCLUDED + #define GMAC_H_INCLUDED + + #include "compiler.h" + #include "component/gmac.h" + +/*/ @cond 0 */ +/**INDENT-OFF**/ + #ifdef __cplusplus + extern "C" { + #endif +/**INDENT-ON**/ +/*/ @endcond */ + +/** The buffer addresses written into the descriptors must be aligned, so the + * last few bits are zero. These bits have special meaning for the GMAC + * peripheral and cannot be used as part of the address. */ + #define GMAC_RXD_ADDR_MASK 0xFFFFFFFC + #define GMAC_RXD_WRAP ( 1ul << 1 ) /**< Wrap bit */ + #define GMAC_RXD_OWNERSHIP ( 1ul << 0 ) /**< Ownership bit */ + + #define GMAC_RXD_BROADCAST ( 1ul << 31 ) /**< Broadcast detected */ + #define GMAC_RXD_MULTIHASH ( 1ul << 30 ) /**< Multicast hash match */ + #define GMAC_RXD_UNIHASH ( 1ul << 29 ) /**< Unicast hash match */ + #define GMAC_RXD_ADDR_FOUND ( 1ul << 27 ) /**< Specific address match found */ + #define GMAC_RXD_ADDR ( 3ul << 25 ) /**< Address match */ + #define GMAC_RXD_RXCOEN ( 1ul << 24 ) /**< RXCOEN related function */ + #define GMAC_RXD_TYPE ( 3ul << 22 ) /**< Type ID match */ + #define GMAC_RXD_VLAN ( 1ul << 21 ) /**< VLAN tag detected */ + #define GMAC_RXD_PRIORITY ( 1ul << 20 ) /**< Priority tag detected */ + #define GMAC_RXD_PRIORITY_MASK ( 3ul << 17 ) /**< VLAN priority */ + #define GMAC_RXD_CFI ( 1ul << 16 ) /**< Concatenation Format Indicator only if bit 21 is set */ + #define GMAC_RXD_EOF ( 1ul << 15 ) /**< End of frame */ + #define GMAC_RXD_SOF ( 1ul << 14 ) /**< Start of frame */ + #define GMAC_RXD_FCS ( 1ul << 13 ) /**< Frame check sequence */ + #define GMAC_RXD_OFFSET_MASK /**< Receive buffer offset */ + #define GMAC_RXD_LEN_MASK ( 0xFFF ) /**< Length of frame including FCS (if selected) */ + #define GMAC_RXD_LENJUMBO_MASK ( 0x3FFF ) /**< Jumbo frame length */ + + #define GMAC_TXD_USED ( 1ul << 31 ) /**< Frame is transmitted */ + #define GMAC_TXD_WRAP ( 1ul << 30 ) /**< Last descriptor */ + #define GMAC_TXD_ERROR ( 1ul << 29 ) /**< Retry limit exceeded, error */ + #define GMAC_TXD_UNDERRUN ( 1ul << 28 ) /**< Transmit underrun */ + #define GMAC_TXD_EXHAUSTED ( 1ul << 27 ) /**< Buffer exhausted */ + #define GMAC_TXD_LATE ( 1ul << 26 ) /**< Late collision,transmit error */ + #define GMAC_TXD_CHECKSUM_ERROR ( 7ul << 20 ) /**< Checksum error */ + #define GMAC_TXD_NOCRC ( 1ul << 16 ) /**< No CRC */ + #define GMAC_TXD_LAST ( 1ul << 15 ) /**< Last buffer in frame */ + #define GMAC_TXD_LEN_MASK ( 0x1FFF ) /**< Length of buffer */ + +/** The MAC can support frame lengths up to 1536 bytes */ + #define GMAC_FRAME_LENTGH_MAX 1536 + + #define GMAC_RX_UNITSIZE 128 /**< Fixed size for RX buffer */ + #define GMAC_TX_UNITSIZE 1518 /**< Size for ETH frame length */ + +/** GMAC clock speed */ + #define GMAC_MCK_SPEED_240MHZ ( 240 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_160MHZ ( 160 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_120MHZ ( 120 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_80MHZ ( 80 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_40MHZ ( 40 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_20MHZ ( 20 * 1000 * 1000 ) + +/** GMAC maintain code default value*/ + #define GMAC_MAN_CODE_VALUE ( 10 ) + +/** GMAC maintain start of frame default value*/ + #define GMAC_MAN_SOF_VALUE ( 1 ) + +/** GMAC maintain read/write*/ + #define GMAC_MAN_RW_TYPE ( 2 ) + +/** GMAC maintain read only*/ + #define GMAC_MAN_READ_ONLY ( 1 ) + +/** GMAC address length */ + #define GMAC_ADDR_LENGTH ( 6 ) + + + #define GMAC_DUPLEX_HALF 0 + #define GMAC_DUPLEX_FULL 1 + + #define GMAC_SPEED_10M 0 + #define GMAC_SPEED_100M 1 + +/** + * \brief Return codes for GMAC APIs. + */ + typedef enum + { + GMAC_OK = 0, /** 0 Operation OK */ + GMAC_TIMEOUT = 1, /** 1 GMAC operation timeout */ + GMAC_TX_BUSY, /** 2 TX in progress */ + GMAC_RX_NULL, /** 3 No data received */ + GMAC_SIZE_TOO_SMALL, /** 4 Buffer size not enough */ + GMAC_PARAM, /** 5 Parameter error, TX packet invalid or RX size too small */ + GMAC_INVALID = 0xFF, /* Invalid */ + } gmac_status_t; + +/** + * \brief Media Independent Interface (MII) type. + */ + typedef enum + { + GMAC_PHY_MII = 0, /** MII mode */ + GMAC_PHY_RMII = 1, /** Reduced MII mode */ + GMAC_PHY_INVALID = 0xFF, /* Invalid mode*/ + } gmac_mii_mode_t; + +/** Receive buffer descriptor struct */ + COMPILER_PACK_SET( 8 ) + typedef struct gmac_rx_descriptor + { + union gmac_rx_addr + { + uint32_t val; + struct gmac_rx_addr_bm + { + uint32_t b_ownership : 1, /**< User clear, GMAC sets this to 1 once it has successfully written a frame to memory */ + b_wrap : 1, /**< Marks last descriptor in receive buffer */ + addr_dw : 30; /**< Address in number of DW */ + } bm; + } addr; /**< Address, Wrap & Ownership */ + union gmac_rx_status + { + uint32_t val; + struct gmac_rx_status_bm + { + uint32_t len : 13, /** 0..12 Length of frame including FCS */ + b_fcs : 1, /** 13 Receive buffer offset, bits 13:12 of frame length for jumbo frame */ + b_sof : 1, /** 14 Start of frame */ + b_eof : 1, /** 15 End of frame */ + b_cfi : 1, /** 16 Concatenation Format Indicator */ + vlan_priority : 3, /** 17..19 VLAN priority (if VLAN detected) */ + b_priority_detected : 1, /** 20 Priority tag detected */ + b_vlan_detected : 1, /** 21 VLAN tag detected */ + b_type_id_match : 2, /** 22..23 Type ID match */ + b_checksumoffload : 1, /** 24 Checksum offload specific function */ + b_addrmatch : 2, /** 25..26 Address register match */ + b_ext_addr_match : 1, /** 27 External address match found */ + reserved : 1, /** 28 */ + b_uni_hash_match : 1, /** 29 Unicast hash match */ + b_multi_hash_match : 1, /** 30 Multicast hash match */ + b_boardcast_detect : 1; /** 31 Global broadcast address detected */ + } bm; + } status; + } gmac_rx_descriptor_t; + +/** Transmit buffer descriptor struct */ + COMPILER_PACK_SET( 8 ) + typedef struct gmac_tx_descriptor + { + uint32_t addr; + union gmac_tx_status + { + uint32_t val; + struct gmac_tx_status_bm + { + uint32_t len : 14, /** 0..13 Length of buffer */ + reserved : 1, /** 14 */ + b_last_buffer : 1, /** 15 Last buffer (in the current frame) */ + b_no_crc : 1, /** 16 No CRC */ + reserved1 : 3, /** 17..19 */ + b_checksumoffload : 3, /** 20..22 Transmit checksum generation offload errors */ + reserved2 : 3, /** 23..25 */ + b_lco : 1, /** 26 Late collision, transmit error detected */ + b_exhausted : 1, /** 27 Buffer exhausted in mid frame */ + b_underrun : 1, /** 28 Transmit underrun */ + b_error : 1, /** 29 Retry limit exceeded, error detected */ + b_wrap : 1, /** 30 Marks last descriptor in TD list */ + b_used : 1; /** 31 User clear, GMAC sets this to 1 once a frame has been successfully transmitted */ + } bm; + } status; + } gmac_tx_descriptor_t; + + COMPILER_PACK_RESET() + +/** + * \brief Input parameters when initializing the gmac module mode. + */ + typedef struct gmac_options + { + /* Enable/Disable CopyAllFrame */ + uint8_t uc_copy_all_frame; + /* Enable/Disable NoBroadCast */ + uint8_t uc_no_boardcast; + /* MAC address */ + uint8_t uc_mac_addr[ GMAC_ADDR_LENGTH ]; + } gmac_options_t; + +/** TX callback */ + typedef void (* gmac_dev_tx_cb_t) ( uint32_t ul_status, + uint8_t * puc_buffer ); +/** RX callback */ + typedef void (* gmac_dev_rx_cb_t) ( uint32_t ul_status ); +/** Wakeup callback */ + typedef void (* gmac_dev_wakeup_cb_t) ( void ); + +/** + * GMAC driver structure. + */ + typedef struct gmac_device + { + /** Pointer to HW register base */ + Gmac * p_hw; + + /** + * Pointer to allocated TX buffer. + * Section 3.6 of AMBA 2.0 spec states that burst should not cross + * 1K Boundaries. + * Receive buffer manager writes are burst of 2 words => 3 lsb bits + * of the address shall be set to 0. + */ + uint8_t * p_tx_buffer; + /** Pointer to allocated RX buffer */ + uint8_t * p_rx_buffer; + /** Pointer to Rx TDs (must be 8-byte aligned) */ + gmac_rx_descriptor_t * p_rx_dscr; + /** Pointer to Tx TDs (must be 8-byte aligned) */ + gmac_tx_descriptor_t * p_tx_dscr; + /** Optional callback to be invoked once a frame has been received */ + gmac_dev_rx_cb_t func_rx_cb; + #if ( GMAC_USES_WAKEUP_CALLBACK ) + /** Optional callback to be invoked once several TDs have been released */ + gmac_dev_wakeup_cb_t func_wakeup_cb; + #endif + #if ( GMAC_USES_TX_CALLBACK != 0 ) + /** Optional callback list to be invoked once TD has been processed */ + gmac_dev_tx_cb_t * func_tx_cb_list; + #endif + /** RX TD list size */ + uint32_t ul_rx_list_size; + /** RX index for current processing TD */ + uint32_t ul_rx_idx; + /** TX TD list size */ + uint32_t ul_tx_list_size; + /** Circular buffer head pointer by upper layer (buffer to be sent) */ + int32_t l_tx_head; + /** Circular buffer tail pointer incremented by handlers (buffer sent) */ + int32_t l_tx_tail; + + /** Number of free TD before wakeup callback is invoked */ + uint32_t uc_wakeup_threshold; + } gmac_device_t; + +/** + * \brief Write network control value. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_ncr Network control value. + */ + static inline void gmac_network_control( Gmac * p_gmac, + uint32_t ul_ncr ) + { + p_gmac->GMAC_NCR = ul_ncr; + } + +/** + * \brief Get network control value. + * + * \param p_gmac Pointer to the GMAC instance. + */ + + static inline uint32_t gmac_get_network_control( Gmac * p_gmac ) + { + return p_gmac->GMAC_NCR; + } + +/** + * \brief Enable/Disable GMAC receive. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC receiver, else to enable it. + */ + static inline void gmac_enable_receive( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_RXEN; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_RXEN; + } + } + +/** + * \brief Enable/Disable GMAC transmit. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC transmit, else to enable it. + */ + static inline void gmac_enable_transmit( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXEN; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_TXEN; + } + } + +/** + * \brief Enable/Disable GMAC management. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC management, else to enable it. + */ + static inline void gmac_enable_management( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_MPE; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_MPE; + } + } + +/** + * \brief Clear all statistics registers. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_clear_statistics( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_CLRSTAT; + } + +/** + * \brief Increase all statistics registers. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_increase_statistics( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_INCSTAT; + } + +/** + * \brief Enable/Disable statistics registers writing. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the statistics registers writing, else to enable it. + */ + static inline void gmac_enable_statistics_write( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_WESTAT; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_WESTAT; + } + } + +/** + * \brief In half-duplex mode, forces collisions on all received frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the back pressure, else to enable it. + */ + static inline void gmac_enable_back_pressure( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_BP; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_BP; + } + } + +/** + * \brief Start transmission. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_start_transmission( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TSTART; + } + +/** + * \brief Halt transmission. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_halt_transmission( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_THALT; + } + +/** + * \brief Transmit pause frame. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_tx_pause_frame( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXPF; + } + +/** + * \brief Transmit zero quantum pause frame. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_tx_pause_zero_quantum_frame( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXZQPF; + } + +/** + * \brief Read snapshot. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_read_snapshot( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_RDS; + } + +/** + * \brief Store receivetime stamp to memory. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to normal operation, else to enable the store. + */ + static inline void gmac_store_rx_time_stamp( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_SRTSM; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_SRTSM; + } + } + +/** + * \brief Enable PFC priority-based pause reception. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 1 to set the reception, 0 to disable. + */ + static inline void gmac_enable_pfc_pause_frame( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_ENPBPR; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_ENPBPR; + } + } + +/** + * \brief Transmit PFC priority-based pause reception. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_transmit_pfc_pause_frame( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXPBPF; + } + +/** + * \brief Flush next packet. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_flush_next_packet( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_FNP; + } + +/** + * \brief Set up network configuration register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_cfg Network configuration value. + */ + static inline void gmac_set_configure( Gmac * p_gmac, + uint32_t ul_cfg ) + { + p_gmac->GMAC_NCFGR = ul_cfg; + } + +/** + * \brief Get network configuration. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Network configuration. + */ + static inline uint32_t gmac_get_configure( Gmac * p_gmac ) + { + return p_gmac->GMAC_NCFGR; + } + + +/* Get and set DMA Configuration Register */ + static inline void gmac_set_dma( Gmac * p_gmac, + uint32_t ul_cfg ) + { + p_gmac->GMAC_DCFGR = ul_cfg; + } + + static inline uint32_t gmac_get_dma( Gmac * p_gmac ) + { + return p_gmac->GMAC_DCFGR; + } + +/** + * \brief Set speed. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_speed 1 to indicate 100Mbps, 0 to 10Mbps. + */ + static inline void gmac_set_speed( Gmac * p_gmac, + uint8_t uc_speed ) + { + if( uc_speed ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_SPD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_SPD; + } + } + +/** + * \brief Enable/Disable Full-Duplex mode. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the Full-Duplex mode, else to enable it. + */ + static inline void gmac_enable_full_duplex( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_FD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_FD; + } + } + +/** + * \brief Enable/Disable Copy(Receive) All Valid Frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable copying all valid frames, else to enable it. + */ + static inline void gmac_enable_copy_all( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_CAF; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_CAF; + } + } + +/** + * \brief Enable/Disable jumbo frames (up to 10240 bytes). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the jumbo frames, else to enable it. + */ + static inline void gmac_enable_jumbo_frames( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_JFRAME; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_JFRAME; + } + } + +/** + * \brief Disable/Enable broadcast receiving. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 1 to disable the broadcast, else to enable it. + */ + static inline void gmac_disable_broadcast( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_NBC; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_NBC; + } + } + +/** + * \brief Enable/Disable multicast hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the multicast hash, else to enable it. + */ + static inline void gmac_enable_multicast_hash( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_UNIHEN; + } + } + +/** + * \brief Enable/Disable big frames (over 1518, up to 1536). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable big frames else to enable it. + */ + static inline void gmac_enable_big_frame( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_MAXFS; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_MAXFS; + } + } + +/** + * \brief Set MDC clock divider. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_mck GMAC MCK. + * + * \return GMAC_OK if successfully. + */ + static inline uint8_t gmac_set_mdc_clock( Gmac * p_gmac, + uint32_t ul_mck ) + { + uint32_t ul_clk; + + if( ul_mck > GMAC_MCK_SPEED_240MHZ ) + { + return GMAC_INVALID; + } + else if( ul_mck > GMAC_MCK_SPEED_160MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_96; + } + else if( ul_mck > GMAC_MCK_SPEED_120MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_64; + } + else if( ul_mck > GMAC_MCK_SPEED_80MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_48; + } + else if( ul_mck > GMAC_MCK_SPEED_40MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_32; + } + else if( ul_mck > GMAC_MCK_SPEED_20MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_16; + } + else + { + ul_clk = GMAC_NCFGR_CLK_MCK_8; + } + + p_gmac->GMAC_NCFGR = ( p_gmac->GMAC_NCFGR & ~GMAC_NCFGR_CLK_Msk ) | ul_clk; + return GMAC_OK; + } + +/** + * \brief Enable/Disable retry test. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the GMAC receiver, else to enable it. + */ + static inline void gmac_enable_retry_test( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RTY; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RTY; + } + } + +/** + * \brief Enable/Disable pause (when a valid pause frame is received). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable pause frame, else to enable it. + */ + static inline void gmac_enable_pause_frame( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_PEN; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_PEN; + } + } + +/** + * \brief Set receive buffer offset to 0 ~ 3. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_set_rx_buffer_offset( Gmac * p_gmac, + uint8_t uc_offset ) + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RXBUFO_Msk; + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RXBUFO( uc_offset ); + } + +/** + * \brief Enable/Disable receive length field checking. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable receive length field checking, else to enable it. + */ + static inline void gmac_enable_rx_length_check( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_LFERD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_LFERD; + } + } + +/** + * \brief Enable/Disable discarding FCS field of received frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable discarding FCS field of received frames, else to enable it. + */ + static inline void gmac_enable_discard_fcs( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RFCS; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RFCS; + } + } + + +/** + * \brief Enable/Disable frames to be received in half-duplex mode + * while transmitting. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the received in half-duplex mode, else to enable it. + */ + static inline void gmac_enable_efrhd( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_EFRHD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_EFRHD; + } + } + +/** + * \brief Enable/Disable ignore RX FCS. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable ignore RX FCS, else to enable it. + */ + static inline void gmac_enable_ignore_rx_fcs( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_IRXFCS; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_IRXFCS; + } + } + +/** + * \brief Get Network Status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Network status. + */ + static inline uint32_t gmac_get_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_NSR; + } + +/** + * \brief Get MDIO IN pin status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return MDIO IN pin status. + */ + static inline uint8_t gmac_get_MDIO( Gmac * p_gmac ) + { + return( ( p_gmac->GMAC_NSR & GMAC_NSR_MDIO ) > 0 ); + } + +/** + * \brief Check if PHY is idle. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return 1 if PHY is idle. + */ + static inline uint8_t gmac_is_phy_idle( Gmac * p_gmac ) + { + return( ( p_gmac->GMAC_NSR & GMAC_NSR_IDLE ) > 0 ); + } + +/** + * \brief Return transmit status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Transmit status. + */ + static inline uint32_t gmac_get_tx_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_TSR; + } + +/** + * \brief Clear transmit status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_status Transmit status. + */ + static inline void gmac_clear_tx_status( Gmac * p_gmac, + uint32_t ul_status ) + { + p_gmac->GMAC_TSR = ul_status; + } + +/** + * \brief Return receive status. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline uint32_t gmac_get_rx_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_RSR; + } + +/** + * \brief Clear receive status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_status Receive status. + */ + static inline void gmac_clear_rx_status( Gmac * p_gmac, + uint32_t ul_status ) + { + p_gmac->GMAC_RSR = ul_status; + } + +/** + * \brief Set Rx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Rx queue address. + */ + static inline void gmac_set_rx_queue( Gmac * p_gmac, + uint32_t ul_addr ) + { + p_gmac->GMAC_RBQB = GMAC_RBQB_ADDR_Msk & ul_addr; + } + +/** + * \brief Get Rx Queue Address. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Rx queue address. + */ + static inline uint32_t gmac_get_rx_queue( Gmac * p_gmac ) + { + return p_gmac->GMAC_RBQB; + } + +/** + * \brief Set Tx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Tx queue address. + */ + static inline void gmac_set_tx_queue( Gmac * p_gmac, + uint32_t ul_addr ) + { + p_gmac->GMAC_TBQB = GMAC_TBQB_ADDR_Msk & ul_addr; + } + +/** + * \brief Get Tx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Rx queue address. + */ + static inline uint32_t gmac_get_tx_queue( Gmac * p_gmac ) + { + return p_gmac->GMAC_TBQB; + } + +/** + * \brief Enable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be enabled. + */ + static inline void gmac_enable_interrupt( Gmac * p_gmac, + uint32_t ul_source ) + { + p_gmac->GMAC_IER = ul_source; + } + +/** + * \brief Disable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be disabled. + */ + static inline void gmac_disable_interrupt( Gmac * p_gmac, + uint32_t ul_source ) + { + p_gmac->GMAC_IDR = ul_source; + } + +/** + * \brief Return interrupt status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Interrupt status. + */ + static inline uint32_t gmac_get_interrupt_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_ISR; + } + +/** + * \brief Return interrupt mask. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Interrupt mask. + */ + static inline uint32_t gmac_get_interrupt_mask( Gmac * p_gmac ) + { + return p_gmac->GMAC_IMR; + } + +/** + * \brief Execute PHY maintenance command. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * \param uc_reg_addr Register address. + * \param uc_rw 1 to Read, 0 to write. + * \param us_data Data to be performed, write only. + */ + static inline void gmac_maintain_phy( Gmac * p_gmac, + uint8_t uc_phy_addr, + uint8_t uc_reg_addr, + uint8_t uc_rw, + uint16_t us_data ) + { + /* Wait until bus idle */ + while( ( p_gmac->GMAC_NSR & GMAC_NSR_IDLE ) == 0 ) + { + } + + /* Write maintain register */ + p_gmac->GMAC_MAN = GMAC_MAN_WTN( GMAC_MAN_CODE_VALUE ) + | GMAC_MAN_CLTTO + | GMAC_MAN_PHYA( uc_phy_addr ) + | GMAC_MAN_REGA( uc_reg_addr ) + | GMAC_MAN_OP( ( uc_rw ? GMAC_MAN_RW_TYPE : GMAC_MAN_READ_ONLY ) ) + | GMAC_MAN_DATA( us_data ); + } + +/** + * \brief Get PHY maintenance data returned. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Get PHY data. + */ + static inline uint16_t gmac_get_phy_data( Gmac * p_gmac ) + { + /* Wait until bus idle */ + while( ( p_gmac->GMAC_NSR & GMAC_NSR_IDLE ) == 0 ) + { + } + + /* Return data */ + return ( uint16_t ) ( p_gmac->GMAC_MAN & GMAC_MAN_DATA_Msk ); + } + +/** + * \brief Set Hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_hash_top Hash top. + * \param ul_hash_bottom Hash bottom. + */ + static inline void gmac_set_hash( Gmac * p_gmac, + uint32_t ul_hash_top, + uint32_t ul_hash_bottom ) + { + p_gmac->GMAC_HRB = ul_hash_bottom; + p_gmac->GMAC_HRT = ul_hash_top; + } + +/** + * \brief Set 64 bits Hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ull_hash 64 bits hash value. + */ + static inline void gmac_set_hash64( Gmac * p_gmac, + uint64_t ull_hash ) + { + p_gmac->GMAC_HRB = ( uint32_t ) ull_hash; + p_gmac->GMAC_HRT = ( uint32_t ) ( ull_hash >> 32 ); + } + +/** + * \brief Set MAC Address. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param p_mac_addr GMAC address. + */ + static inline void gmac_set_address( Gmac * p_gmac, + uint8_t uc_index, + uint8_t * p_mac_addr ) + { + p_gmac->GMAC_SA[ uc_index ].GMAC_SAB = ( p_mac_addr[ 3 ] << 24 ) + | ( p_mac_addr[ 2 ] << 16 ) + | ( p_mac_addr[ 1 ] << 8 ) + | ( p_mac_addr[ 0 ] ); + p_gmac->GMAC_SA[ uc_index ].GMAC_SAT = ( p_mac_addr[ 5 ] << 8 ) + | ( p_mac_addr[ 4 ] ); + } + +/** + * \brief Set MAC Address via 2 dword. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param ul_mac_top GMAC top address. + * \param ul_mac_bottom GMAC bottom address. + */ + static inline void gmac_set_address32( Gmac * p_gmac, + uint8_t uc_index, + uint32_t ul_mac_top, + uint32_t ul_mac_bottom ) + { + p_gmac->GMAC_SA[ uc_index ].GMAC_SAB = ul_mac_bottom; + p_gmac->GMAC_SA[ uc_index ].GMAC_SAT = ul_mac_top; + } + +/** + * \brief Set MAC Address via int64. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param ull_mac 64-bit GMAC address. + */ + static inline void gmac_set_address64( Gmac * p_gmac, + uint8_t uc_index, + uint64_t ull_mac ) + { + p_gmac->GMAC_SA[ uc_index ].GMAC_SAB = ( uint32_t ) ull_mac; + p_gmac->GMAC_SA[ uc_index ].GMAC_SAT = ( uint32_t ) ( ull_mac >> 32 ); + } + +/** + * \brief Select media independent interface mode. + * + * \param p_gmac Pointer to the GMAC instance. + * \param mode Media independent interface mode. + */ + static inline void gmac_select_mii_mode( Gmac * p_gmac, + gmac_mii_mode_t mode ) + { + switch( mode ) + { + case GMAC_PHY_MII: + case GMAC_PHY_RMII: + p_gmac->GMAC_UR |= GMAC_UR_RMIIMII; + break; + + default: + p_gmac->GMAC_UR &= ~GMAC_UR_RMIIMII; + break; + } + } + + uint8_t gmac_phy_read( Gmac * p_gmac, + uint8_t uc_phy_address, + uint8_t uc_address, + uint32_t * p_value ); + uint8_t gmac_phy_write( Gmac * p_gmac, + uint8_t uc_phy_address, + uint8_t uc_address, + uint32_t ul_value ); + void gmac_dev_init( Gmac * p_gmac, + gmac_device_t * p_gmac_dev, + gmac_options_t * p_opt ); + uint32_t gmac_dev_read( gmac_device_t * p_gmac_dev, + uint8_t * p_frame, + uint32_t ul_frame_size, + uint32_t * p_rcv_size ); + uint32_t gmac_dev_write( gmac_device_t * p_gmac_dev, + void * p_buffer, + uint32_t ul_size, + gmac_dev_tx_cb_t func_tx_cb ); + uint32_t gmac_dev_get_tx_load( gmac_device_t * p_gmac_dev ); + void gmac_dev_set_rx_callback( gmac_device_t * p_gmac_dev, + gmac_dev_rx_cb_t func_rx_cb ); + uint8_t gmac_dev_set_tx_wakeup_callback( gmac_device_t * p_gmac_dev, + gmac_dev_wakeup_cb_t func_wakeup, + uint8_t uc_threshold ); + void gmac_dev_reset( gmac_device_t * p_gmac_dev ); + void gmac_handler( gmac_device_t * p_gmac_dev ); + +/*/ @cond 0 */ +/**INDENT-OFF**/ + #ifdef __cplusplus + } + #endif +/**INDENT-ON**/ +/*/ @endcond */ + +/** + * \page gmac_quickstart Quickstart guide for GMAC driver. + * + * This is the quickstart guide for the \ref gmac_group "Ethernet MAC", + * with step-by-step instructions on how to configure and use the driver in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section gmac_basic_use_case Basic use case + * In the basic use case, the GMAC driver are configured for: + * - PHY component KSZ8051MNL is used + * - GMAC uses MII mode + * - The number of receive buffer is 16 + * - The number of transfer buffer is 8 + * - MAC address is set to 00-04-25-1c-a0-02 + * - IP address is set to 192.168.0.2 + * - IP address is set to 192.168.0.2 + * - Gateway is set to 192.168.0.1 + * - Network mask is 255.255.255.0 + * - PHY operation max retry count is 1000000 + * - GMAC is configured to not support copy all frame and support broadcast + * - The data will be read from the ethernet + * + * \section gmac_basic_use_case_setup Setup steps + * + * \subsection gmac_basic_use_case_setup_prereq Prerequisites + * -# \ref sysclk_group "System Clock Management (sysclock)" + * -# \ref pmc_group "Power Management Controller (pmc)" + * -# \ref ksz8051mnl_ethernet_phy_group "PHY component (KSZ8051MNL)" + * + * \subsection gmac_basic_use_case_setup_code Example code + * Content of conf_eth.h + * \code + * #define GMAC_RX_BUFFERS 16 + * #define GMAC_TX_BUFFERS 8 + * #define MAC_PHY_RETRY_MAX 1000000 + * #define ETHERNET_CONF_ETHADDR0 0x00 + * #define ETHERNET_CONF_ETHADDR0 0x00 + * #define ETHERNET_CONF_ETHADDR1 0x04 + * #define ETHERNET_CONF_ETHADDR2 0x25 + * #define ETHERNET_CONF_ETHADDR3 0x1C + * #define ETHERNET_CONF_ETHADDR4 0xA0 + * #define ETHERNET_CONF_ETHADDR5 0x02 + * #define ETHERNET_CONF_IPADDR0 192 + * #define ETHERNET_CONF_IPADDR1 168 + * #define ETHERNET_CONF_IPADDR2 0 + * #define ETHERNET_CONF_IPADDR3 2 + * #define ETHERNET_CONF_GATEWAY_ADDR0 192 + * #define ETHERNET_CONF_GATEWAY_ADDR1 168 + * #define ETHERNET_CONF_GATEWAY_ADDR2 0 + * #define ETHERNET_CONF_GATEWAY_ADDR3 1 + * #define ETHERNET_CONF_NET_MASK0 255 + * #define ETHERNET_CONF_NET_MASK1 255 + * #define ETHERNET_CONF_NET_MASK2 255 + * #define ETHERNET_CONF_NET_MASK3 0 + * #define ETH_PHY_MODE ETH_PHY_MODE + * \endcode + * + * A specific gmac device and the receive data buffer must be defined; another ul_frm_size should be defined + * to trace the actual size of the data received. + * \code + * static gmac_device_t gs_gmac_dev; + * static volatile uint8_t gs_uc_eth_buffer[GMAC_FRAME_LENTGH_MAX]; + * + * uint32_t ul_frm_size; + * \endcode + * + * Add to application C-file: + * \code + * void gmac_init(void) + * { + * sysclk_init(); + * + * board_init(); + * + * pmc_enable_periph_clk(ID_GMAC); + * + * gmac_option.uc_copy_all_frame = 0; + * gmac_option.uc_no_boardcast = 0; + * memcpy(gmac_option.uc_mac_addr, gs_uc_mac_address, sizeof(gs_uc_mac_address)); + * gs_gmac_dev.p_hw = GMAC; + * + * gmac_dev_init(GMAC, &gs_gmac_dev, &gmac_option); + * + * NVIC_EnableIRQ(GMAC_IRQn); + * + * ethernet_phy_init(GMAC, BOARD_GMAC_PHY_ADDR, sysclk_get_cpu_hz()); + * + * ethernet_phy_auto_negotiate(GMAC, BOARD_GMAC_PHY_ADDR); + * + * ethernet_phy_set_link(GMAC, BOARD_GMAC_PHY_ADDR, 1); + * \endcode + * + * \subsection gmac_basic_use_case_setup_flow Workflow + * - Ensure that conf_eth.h is present and contains the + * following configuration symbol. This configuration file is used + * by the driver and should not be included by the application. + * -# Define the receiving buffer size used in the internal GMAC driver. + * The buffer size used for RX is GMAC_RX_BUFFERS * 128. + * If it was supposed receiving a large number of frame, the + * GMAC_RX_BUFFERS should be set higher. E.g., the application wants to accept + * a ping echo test of 2048, the GMAC_RX_BUFFERS should be set at least + * (2048/128)=16, and as there are additional frames coming, a preferred + * number is 24 depending on a normal Ethernet throughput. + * - \code + * #define GMAC_RX_BUFFERS 16 + * \endcode + * -# Define the transmitting buffer size used in the internal GMAC driver. + * The buffer size used for TX is GMAC_TX_BUFFERS * 1518. + * - \code + * #define GMAC_TX_BUFFERS 8 + * \endcode + * -# Define maximum retry time for a PHY read/write operation. + * - \code + * #define MAC_PHY_RETRY_MAX 1000000 + * \endcode + * -# Define the MAC address. 00:04:25:1C:A0:02 is the address reserved + * for ATMEL, application should always change this address to its' own. + * - \code + * #define ETHERNET_CONF_ETHADDR0 0x00 + * #define ETHERNET_CONF_ETHADDR1 0x04 + * #define ETHERNET_CONF_ETHADDR2 0x25 + * #define ETHERNET_CONF_ETHADDR3 0x1C + * #define ETHERNET_CONF_ETHADDR4 0xA0 + * #define ETHERNET_CONF_ETHADDR5 0x02 + * \endcode + * -# Define the IP address configration used in the application. When DHCP + * is enabled, this configuration is not effected. + * - \code + * #define ETHERNET_CONF_IPADDR0 192 + * #define ETHERNET_CONF_IPADDR1 168 + * #define ETHERNET_CONF_IPADDR2 0 + * #define ETHERNET_CONF_IPADDR3 2 + * #define ETHERNET_CONF_GATEWAY_ADDR0 192 + * #define ETHERNET_CONF_GATEWAY_ADDR1 168 + * #define ETHERNET_CONF_GATEWAY_ADDR2 0 + * #define ETHERNET_CONF_GATEWAY_ADDR3 1 + * #define ETHERNET_CONF_NET_MASK0 255 + * #define ETHERNET_CONF_NET_MASK1 255 + * #define ETHERNET_CONF_NET_MASK2 255 + * #define ETHERNET_CONF_NET_MASK3 0 + * \endcode + * -# Configure the PHY maintenance interface. + * - \code + * #define ETH_PHY_MODE GMAC_PHY_MII + * \endcode + * -# Enable the system clock: + * - \code sysclk_init(); \endcode + * -# Enable PIO configurations for GMAC: + * - \code board_init(); \endcode + * -# Enable PMC clock for GMAC: + * - \code pmc_enable_periph_clk(ID_GMAC); \endcode + * -# Set the GMAC options; it's set to copy all frame and support broadcast: + * - \code + * gmac_option.uc_copy_all_frame = 0; + * gmac_option.uc_no_boardcast = 0; + * memcpy(gmac_option.uc_mac_addr, gs_uc_mac_address, sizeof(gs_uc_mac_address)); + * gs_gmac_dev.p_hw = GMAC; + * \endcode + * -# Initialize GMAC device with the filled option: + * - \code + * gmac_dev_init(GMAC, &gs_gmac_dev, &gmac_option); + * \endcode + * -# Enable the interrupt service for GMAC: + * - \code + * NVIC_EnableIRQ(GMAC_IRQn); + * \endcode + * -# Initialize the PHY component: + * - \code + * ethernet_phy_init(GMAC, BOARD_GMAC_PHY_ADDR, sysclk_get_cpu_hz()); + * \endcode + * -# The link will be established based on auto negotiation. + * - \code + * ethernet_phy_auto_negotiate(GMAC, BOARD_GMAC_PHY_ADDR); + * \endcode + * -# Establish the ethernet link; the network can be worked from now on: + * - \code + * ethernet_phy_set_link(GMAC, BOARD_GMAC_PHY_ADDR, 1); + * \endcode + * + * \section gmac_basic_use_case_usage Usage steps + * \subsection gmac_basic_use_case_usage_code Example code + * Add to, e.g., main loop in application C-file: + * \code + * gmac_dev_read(&gs_gmac_dev, (uint8_t *) gs_uc_eth_buffer, sizeof(gs_uc_eth_buffer), &ul_frm_size)); + * \endcode + * + * \subsection gmac_basic_use_case_usage_flow Workflow + * -# Start reading the data from the ethernet: + * - \code gmac_dev_read(&gs_gmac_dev, (uint8_t *) gs_uc_eth_buffer, sizeof(gs_uc_eth_buffer), &ul_frm_size)); \endcode + */ + + #define GMAC_STATS 0 + + #if ( GMAC_STATS != 0 ) + +/* Here below some code to study the types and + * frequencies of GMAC interrupts. */ + #define GMAC_IDX_RXUBR 0 + #define GMAC_IDX_TUR 1 + #define GMAC_IDX_RLEX 2 + #define GMAC_IDX_TFC 3 + #define GMAC_IDX_RCOMP 4 + #define GMAC_IDX_TCOMP 5 + #define GMAC_IDX_ROVR 6 + #define GMAC_IDX_HRESP 7 + #define GMAC_IDX_PFNZ 8 + #define GMAC_IDX_PTZ 9 + + struct SGmacStats + { + unsigned recvCount; + unsigned rovrCount; + unsigned bnaCount; + unsigned sendCount; + unsigned sovrCount; + unsigned incompCount; + unsigned truncCount; + + unsigned intStatus[ 10 ]; + }; + extern struct SGmacStats gmacStats; + + struct SIntPair + { + const char * name; + unsigned mask; + int index; + }; + + #define MK_PAIR( NAME ) # NAME, GMAC_IER_ ## NAME, GMAC_IDX_ ## NAME + static const struct SIntPair intPairs[] = { + { MK_PAIR( RXUBR ) }, /* Enable receive used bit read interrupt. */ + { MK_PAIR( TUR ) }, /* Enable transmit underrun interrupt. */ + { MK_PAIR( RLEX ) }, /* Enable retry limit exceeded interrupt. */ + { MK_PAIR( TFC ) }, /* Enable transmit buffers exhausted in mid-frame interrupt. */ + { MK_PAIR( RCOMP ) }, /* Receive complete */ + { MK_PAIR( TCOMP ) }, /* Enable transmit complete interrupt. */ + { MK_PAIR( ROVR ) }, /* Enable receive overrun interrupt. */ + { MK_PAIR( HRESP ) }, /* Enable Hresp not OK interrupt. */ + { MK_PAIR( PFNZ ) }, /* Enable pause frame received interrupt. */ + { MK_PAIR( PTZ ) } /* Enable pause time zero interrupt. */ + }; + + void gmac_show_irq_counts(); + + #endif /* if ( GMAC_STATS != 0 ) */ + +#endif /* GMAC_H_INCLUDED */ diff --git a/FreeRTOS/source/portable/NetworkInterface/ATSAME5x/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/ATSAME5x/NetworkInterface.c new file mode 100644 index 0000000..5e96e6d --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ATSAME5x/NetworkInterface.c @@ -0,0 +1,574 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + + +/* This driver is made to work with Atmel START's ASF4 GMAC driver. + * The START generated GMAC initialization code should be commented out, + * since this driver will take care of initializing the GMAC peripheral itself. + * + * Optimal performance is obtained with: + * - CRC offloading enabled for both RX and TX + * - "Copy all frames" set to zero / off + */ + +/* Atmel ASF includes */ +#include "hal_mac_async.h" +#include "hpl_gmac_config.h" +/* Include MAC initialization function here: */ +#include "driver_init.h" + +/* FreeRTOS includes */ +#include "FreeRTOS.h" +#include "task.h" + +/* FreeRTOS+TCP includes */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "phyHandling.h" + + + +/***********************************************/ +/* Configuration variables */ +/***********************************************/ + +/* Check for optimal performance parameters */ +#if ( CONF_GMAC_NCFGR_RXCOEN == 0 ) + #warning This driver works best with RX CRC offloading enabled. +#endif + +#if ( CONF_GMAC_DCFGR_TXCOEN == 0 ) + #warning This driver works best with TX CRC offloading enabled. +#endif + +#if ( CONF_GMAC_NCFGR_CAF != 0 ) + #warning This driver includes GMAC hardware frame filtering for better performance. +#endif + + +/* Make sure someone takes care of the CRC calculation */ +#if ( ( CONF_GMAC_NCFGR_RXCOEN == 0 ) && ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) ) + #error Receive CRC offloading should be enabled. +#endif +#if ( ( CONF_GMAC_DCFGR_TXCOEN == 0 ) && ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) ) + #error Transmit CRC offloading should be enabled. +#endif + +/* Setup LLMNR specific multicast address. */ +#if ( defined( ipconfigUSE_LLMNR ) && ( ipconfigUSE_LLMNR == 1 ) ) + static const uint8_t ucLLMNR_MAC_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; +#endif + +/* Receive task refresh time */ +#define RECEIVE_BLOCK_TIME_MS 100 + +/***********************************************/ +/* FreeRTOS variables */ +/***********************************************/ + +/* Copied from FreeRTOS_IP.c. Used for ICMP CRC calculation */ +#define ipCORRECT_CRC 0xffffU + +/* Also copied from FreeRTOS_IP.c */ + +/** @brief If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet + * driver will filter incoming packets and only pass the stack those packets it + * considers need processing. In this case ipCONSIDER_FRAME_FOR_PROCESSING() can + * be #-defined away. If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 0 + * then the Ethernet driver will pass all received packets to the stack, and the + * stack must do the filtering itself. In this case ipCONSIDER_FRAME_FOR_PROCESSING + * needs to call eConsiderFrameForProcessing. + */ +#if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#endif + +/* Ethernet buffers for BufferAllocation_1.c scheme. + * Set ipUSE_STATIC_ALLOCATION to 1 if using BufferAllocation_1.c, + * otherwise to 0, to save RAM. From Iperf testing, there is no point in using + * static allocation with a non zero-copy driver. + */ +#define ipUSE_STATIC_ALLOCATION 0 +#if ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 ) ) + +/* 1536 bytes is more than needed, 1524 would be enough. + * But 1536 is a multiple of 32, which gives a great alignment for cached memories. */ + #define NETWORK_BUFFER_SIZE 1536 + static uint8_t ucBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ NETWORK_BUFFER_SIZE ]; +#endif /* ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 )) */ + + +/* Holds the handle of the task used as a deferred interrupt processor. The + * handle is used so direct notifications can be sent to the task for all EMAC/DMA + * related interrupts. */ +TaskHandle_t xEMACTaskHandle = NULL; + +/* The PING response queue */ +#if ( defined( ipconfigSUPPORT_OUTGOING_PINGS ) && ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) ) + QueueHandle_t xPingReplyQueue = NULL; +#endif + +/* GMAC interrupt callbacks. */ +void xRxCallback( void ); +static void prvEMACDeferredInterruptHandlerTask( void * pvParameters ); + +/***********************************************/ +/* GMAC variables */ +/***********************************************/ + +/* The Ethernet MAC instance created by ASF4 */ +extern struct mac_async_descriptor ETH_MAC; + +static void prvGMACInit( void ); + +/* Enable/Disable MDC and MDIO ports for PHY register management. */ +static inline void prvGMACEnablePHYManagementPort( bool enable ); + +/* GMAC registers configuration functions. */ +static inline void prvGMACEnable100Mbps( bool enable ); +static inline void prvGMACEnableFullDuplex( bool enable ); + + +/***********************************************/ +/* PHY variables */ +/***********************************************/ + +/* All PHY handling code has now been separated from the NetworkInterface.c, + * see "../Common/phyHandling.c". */ +static EthernetPhy_t xPhyObject; + +/* PHY link preferences. */ +/* Set both speed and Duplex to AUTO, or give them BOTH manual values. */ +const PhyProperties_t xPHYProperties = +{ + .ucSpeed = PHY_SPEED_AUTO, + .ucDuplex = PHY_DUPLEX_AUTO, + .ucMDI_X = PHY_MDIX_AUTO, +}; + +static void prvPHYLinkReset( void ); +static void prvPHYInit( void ); +static inline bool bPHYGetLinkStatus( void ); + +/* PHY read and write functions. */ +static BaseType_t xPHYRead( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t * pulValue ); +static BaseType_t xPHYWrite( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t pulValue ); + + +/*********************************************************************/ +/* FreeRTOS+TCP functions */ +/*********************************************************************/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + /* + * Perform the hardware specific network initialization here. Typically + * that will involve using the Ethernet driver library to initialize the + * Ethernet (or other network) hardware, initialize DMA descriptors, and + * perform a PHY auto-negotiation to obtain a network link. */ + + if( xEMACTaskHandle == NULL ) + { + /* Initialize MAC and PHY */ + prvGMACInit(); + prvPHYInit(); + + /* (Re)set PHY link */ + prvPHYLinkReset(); + + /* Initialize PING capability */ + #if ( defined( ipconfigSUPPORT_OUTGOING_PINGS ) && ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) ) + xPingReplyQueue = xQueueCreate( ipconfigPING_QUEUE_SIZE, sizeof( uint16_t ) ); + #endif + + /* Create event handler task */ + xTaskCreate( prvEMACDeferredInterruptHandlerTask, /* Function that implements the task. */ + "EMACInt", /* Text name for the task. */ + 256, /* Stack size in words, not bytes. */ + ( void * ) 1, /* Parameter passed into the task. */ + configMAX_PRIORITIES - 1, /* Priority at which the task is created. */ + &xEMACTaskHandle ); /* Used to pass out the created task's handle. */ + + configASSERT( xEMACTaskHandle ); + } + + return bPHYGetLinkStatus(); +} + + +static void prvEMACDeferredInterruptHandlerTask( void * pvParameters ) +{ + NetworkBufferDescriptor_t * pxBufferDescriptor; + size_t xBytesReceived = 0, xBytesRead = 0; + + uint16_t xICMPChecksumResult = ipCORRECT_CRC; + const IPPacket_t * pxIPPacket; + + + /* Used to indicate that xSendEventStructToIPTask() is being called because + * of an Ethernet receive event. */ + IPStackEvent_t xRxEvent; + + for( ; ; ) + { + /* Wait for the Ethernet MAC interrupt to indicate that another packet + * has been received. The task notification is used in a similar way to a + * counting semaphore to count Rx events, but is a lot more efficient than + * a semaphore. */ + ulTaskNotifyTake( pdFALSE, pdMS_TO_TICKS( RECEIVE_BLOCK_TIME_MS ) ); + + /* See how much data was received. Here it is assumed ReceiveSize() is + * a peripheral driver function that returns the number of bytes in the + * received Ethernet frame. */ + xBytesReceived = mac_async_read_len( Ð_MAC ); + + if( xBytesReceived > 0 ) + { + /* Allocate a network buffer descriptor that points to a buffer + * large enough to hold the received frame. As this is the simple + * rather than efficient example the received data will just be copied + * into this buffer. */ + pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 ); + + if( pxBufferDescriptor != NULL ) + { + /* pxBufferDescriptor->pucEthernetBuffer now points to an Ethernet + * buffer large enough to hold the received data. Copy the + * received data into pcNetworkBuffer->pucEthernetBuffer. Here it + * is assumed ReceiveData() is a peripheral driver function that + * copies the received data into a buffer passed in as the function's + * parameter. Remember! While is is a simple robust technique - + * it is not efficient. An example that uses a zero copy technique + * is provided further down this page. */ + xBytesRead = mac_async_read( Ð_MAC, pxBufferDescriptor->pucEthernetBuffer, xBytesReceived ); + pxBufferDescriptor->xDataLength = xBytesRead; + + + #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) + { + /* the Atmel SAM GMAC peripheral does not support hardware CRC offloading for ICMP packets. + * It must therefore be implemented in software. */ + pxIPPacket = ipCAST_CONST_PTR_TO_CONST_TYPE_PTR( IPPacket_t, pxBufferDescriptor->pucEthernetBuffer ); + + if( pxIPPacket->xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) + { + xICMPChecksumResult = usGenerateProtocolChecksum( pxBufferDescriptor->pucEthernetBuffer, pxBufferDescriptor->xDataLength, pdFALSE ); + } + else + { + xICMPChecksumResult = ipCORRECT_CRC; /* Reset the result value in case this is not an ICMP packet. */ + } + } + #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) */ + + /* See if the data contained in the received Ethernet frame needs + * to be processed. NOTE! It is preferable to do this in + * the interrupt service routine itself, which would remove the need + * to unblock this task for packets that don't need processing. */ + if( ( ipCONSIDER_FRAME_FOR_PROCESSING( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer ) && + ( xICMPChecksumResult == ipCORRECT_CRC ) ) + { + /* The event about to be sent to the TCP/IP is an Rx event. */ + xRxEvent.eEventType = eNetworkRxEvent; + + /* pvData is used to point to the network buffer descriptor that + * now references the received data. */ + xRxEvent.pvData = ( void * ) pxBufferDescriptor; + + /* Send the data to the TCP/IP stack. */ + if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) + { + /* The buffer could not be sent to the IP task so the buffer + * must be released. */ + vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); + + /* Make a call to the standard trace macro to log the + * occurrence. */ + iptraceETHERNET_RX_EVENT_LOST(); + } + else + { + /* The message was successfully sent to the TCP/IP stack. + * Call the standard trace macro to log the occurrence. */ + iptraceNETWORK_INTERFACE_RECEIVE(); + } + } + else + { + /* The Ethernet frame can be dropped, but the Ethernet buffer + * must be released. */ + vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); + } + } + else + { + /* The event was lost because a network buffer was not available. + * Call the standard trace macro to log the occurrence. */ + iptraceETHERNET_RX_EVENT_LOST(); + } + } + + prvGMACEnablePHYManagementPort( true ); + + if( xPhyCheckLinkStatus( &xPhyObject, xBytesReceived ) ) + { + prvPHYLinkReset(); + } + + prvGMACEnablePHYManagementPort( false ); + } +} + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t xReleaseAfterSend ) +{ + /* Simple network interfaces (as opposed to more efficient zero copy network + * interfaces) just use Ethernet peripheral driver library functions to copy + * data from the FreeRTOS+TCP buffer into the peripheral driver's own buffer. + * This example assumes SendData() is a peripheral driver library function that + * takes a pointer to the start of the data to be sent and the length of the + * data to be sent as two separate parameters. The start of the data is located + * by pxDescriptor->pucEthernetBuffer. The length of the data is located + * by pxDescriptor->xDataLength. */ + + if( bPHYGetLinkStatus() ) + { + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) + { + /* the Atmel SAM GMAC peripheral does not support hardware CRC offloading for ICMP packets. + * It must therefore be implemented in software. */ + const IPPacket_t * pxIPPacket = ipCAST_CONST_PTR_TO_CONST_TYPE_PTR( IPPacket_t, pxDescriptor->pucEthernetBuffer ); + + if( pxIPPacket->xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) + { + ( void ) usGenerateProtocolChecksum( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, pdTRUE ); + } + } + #endif + + mac_async_write( Ð_MAC, pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ); + + /* Call the standard trace macro to log the send event. */ + iptraceNETWORK_INTERFACE_TRANSMIT(); + } + + if( xReleaseAfterSend != pdFALSE ) + { + /* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet + * buffer. The Ethernet buffer is therefore no longer needed, and must be + * freed for re-use. */ + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return pdTRUE; +} + +void xRxCallback( void ) +{ + vTaskNotifyGiveFromISR( xEMACTaskHandle, 0 ); +} + +#if ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 ) ) + +/* Next provide the vNetworkInterfaceAllocateRAMToBuffers() function, which + * simply fills in the pucEthernetBuffer member of each descriptor. */ + void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) + { + BaseType_t x; + + for( x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ ) + { + /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the + * beginning of the allocated buffer. */ + pxNetworkBuffers[ x ].pucEthernetBuffer = &( ucBuffers[ x ][ ipBUFFER_PADDING ] ); + + /* The following line is also required, but will not be required in + * future versions. */ + *( ( uint32_t * ) &ucBuffers[ x ][ 0 ] ) = ( uint32_t ) &( pxNetworkBuffers[ x ] ); + } + } +#endif /* ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 )) */ + + +/*********************************************************************/ +/* GMAC functions */ +/*********************************************************************/ + +/* Initializes the GMAC peripheral. This function is based on ASF4 GMAC initialization + * and uses the Atmel START- generated code, typically located in "driver_init.h". + * Make sure the initialization function is not called twice, e.g. comment out the call in "driver_init.c". + * It is compatible with modifications made in Atmel START afterwards because the + * configuration is saved in "hpl_gmac_config.h". */ +static void prvGMACInit() +{ + /* Call MAC initialization function here: */ + vGMACInit(); + prvGMACEnablePHYManagementPort( false ); + mac_async_disable_irq( Ð_MAC ); + + /* Set GMAC Filtering for own MAC address */ + struct mac_async_filter mac_filter; + memcpy( mac_filter.mac, ipLOCAL_MAC_ADDRESS, ipMAC_ADDRESS_LENGTH_BYTES ); + mac_filter.tid_enable = false; + mac_async_set_filter( Ð_MAC, 0, &mac_filter ); + + /* Set GMAC filtering for LLMNR, if defined. */ + #if ( defined( ipconfigUSE_LLMNR ) && ( ipconfigUSE_LLMNR == 1 ) ) + { + memcpy( mac_filter.mac, ucLLMNR_MAC_address, ipMAC_ADDRESS_LENGTH_BYTES ); + /* LLMNR requires responders to listen to both TCP and UDP protocols. */ + mac_filter.tid_enable = false; + mac_async_set_filter( Ð_MAC, 1, &mac_filter ); + } + #endif + + /* Set GMAC interrupt priority to be compatible with FreeRTOS API */ + NVIC_SetPriority( GMAC_IRQn, configMAX_SYSCALL_INTERRUPT_PRIORITY >> ( 8 - configPRIO_BITS ) ); + + /* Register callback(s). Currently only RX callback is implemented, but TX callback can be added the same way. */ + mac_async_register_callback( Ð_MAC, MAC_ASYNC_RECEIVE_CB, ( FUNC_PTR ) xRxCallback ); + + /* Start the GMAC. */ + mac_async_enable( Ð_MAC ); + mac_async_enable_irq( Ð_MAC ); +} + +static inline void prvGMACEnablePHYManagementPort( bool enable ) +{ + if( enable ) + { + ( ( Gmac * ) ETH_MAC.dev.hw )->NCR.reg |= GMAC_NCR_MPE; + } + else + { + ( ( Gmac * ) ETH_MAC.dev.hw )->NCR.reg &= ~GMAC_NCR_MPE; + } +} + +static inline void prvGMACEnable100Mbps( bool enable ) +{ + if( enable ) + { + ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_SPD; + } + else + { + ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_SPD; + } +} + +static inline void prvGMACEnableFullDuplex( bool enable ) +{ + if( enable ) + { + ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_FD; + } + else + { + ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_FD; + } +} + + +/*********************************************************************/ +/* PHY functions */ +/*********************************************************************/ + +/* Initializes the PHY hardware. Based on ASF4 generated code. */ +static void prvPHYInit() +{ + prvGMACEnablePHYManagementPort( true ); + + vPhyInitialise( &xPhyObject, &xPHYRead, &xPHYWrite ); + xPhyDiscover( &xPhyObject ); + xPhyConfigure( &xPhyObject, &xPHYProperties ); + + prvGMACEnablePHYManagementPort( false ); +} + +/* Start a new link negotiation on the PHY and wait until link is up. */ +static void prvPHYLinkReset() +{ + /* Restart an auto-negotiation */ + prvGMACEnablePHYManagementPort( true ); + + if( ( xPHYProperties.ucDuplex == PHY_DUPLEX_AUTO ) && ( xPHYProperties.ucSpeed == PHY_SPEED_AUTO ) && ( xPHYProperties.ucMDI_X == PHY_MDIX_AUTO ) ) + { + /* Auto-negotiation */ + xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) ); + + /* Update the MAC with the auto-negotiation result parameters. */ + prvGMACEnableFullDuplex( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL ); + prvGMACEnable100Mbps( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_100 ); + } + else + { + /* Fixed values */ + xPhyObject.xPhyPreferences.ucDuplex = xPHYProperties.ucDuplex; + xPhyObject.xPhyPreferences.ucSpeed = xPHYProperties.ucSpeed; + xPhyObject.xPhyPreferences.ucMDI_X = xPHYProperties.ucMDI_X; + xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) ); + + /* Update the MAC with the auto-negotiation result parameters. */ + prvGMACEnableFullDuplex( xPHYProperties.ucDuplex == PHY_DUPLEX_FULL ); + prvGMACEnable100Mbps( xPHYProperties.ucSpeed == PHY_SPEED_100 ); + } + + prvGMACEnablePHYManagementPort( false ); +} + +static BaseType_t xPHYRead( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t * pulValue ) +{ + prvGMACEnablePHYManagementPort( true ); + BaseType_t readStatus = mac_async_read_phy_reg( Ð_MAC, xAddress, xRegister, ( ( uint16_t * ) pulValue ) ); + prvGMACEnablePHYManagementPort( false ); + return readStatus; +} + +static BaseType_t xPHYWrite( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t pulValue ) +{ + prvGMACEnablePHYManagementPort( true ); + BaseType_t writeStatus = mac_async_write_phy_reg( Ð_MAC, xAddress, xRegister, pulValue ); + prvGMACEnablePHYManagementPort( false ); + return writeStatus; +} + +static inline bool bPHYGetLinkStatus( void ) +{ + return( xPhyObject.ulLinkStatusMask != 0 ); +} diff --git a/FreeRTOS/source/portable/NetworkInterface/Common/phyHandling.c b/FreeRTOS/source/portable/NetworkInterface/Common/phyHandling.c new file mode 100644 index 0000000..ec6ca34 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Common/phyHandling.c @@ -0,0 +1,827 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @brief + * 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 +#include +#include + +/* 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" + +#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 LinkStatus in the PHY is still high after 15 seconds of not + * receiving packets. */ + #define ipconfigPHY_LS_HIGH_CHECK_TIME_MS 15000U +#endif + +#ifndef ipconfigPHY_LS_LOW_CHECK_TIME_MS + /* Check if the LinkStatus in the PHY is still low every second. */ + #define ipconfigPHY_LS_LOW_CHECK_TIME_MS 1000U +#endif + +/* As the following 3 macro's are OK in most situations, and so they're not + * included in 'FreeRTOSIPConfigDefaults.h'. + * Users can change their values in the project's 'FreeRTOSIPConfig.h'. */ +#ifndef phyPHY_MAX_RESET_TIME_MS + #define phyPHY_MAX_RESET_TIME_MS 1000U +#endif + +#ifndef phyPHY_MAX_NEGOTIATE_TIME_MS + #define phyPHY_MAX_NEGOTIATE_TIME_MS 3000U +#endif + +#ifndef phySHORT_DELAY_MS + #define phySHORT_DELAY_MS 50U +#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_ISOLATE 0x0400U /* 1 = Isolates 0 = Normal operation. */ +#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 /* Supports IEEE 802.3u: Fast Ethernet at 100 Mbit/s */ +#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 | \ + phyADVERTISE_CSMA ) + +/* Send a reset command 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 = pdFALSE; + + 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_KSZ8081MNXIA: + + case PHY_ID_KSZ8863: + default: + /* Most PHY's have a 1F_PHYSPCS */ + xResult = pdTRUE; + break; + + case PHY_ID_DP83848I: + case PHY_ID_DP83TC811S: + case PHY_ID_TM4C129X: + case PHY_ID_MV88E6071: + /* Has no 0x1F register "PHY Special Control Status". */ + break; + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xHas_19_PHYCR( uint32_t ulPhyID ) +{ + BaseType_t xResult = pdFALSE; + + switch( ulPhyID ) + { + case PHY_ID_LAN8742A: + case PHY_ID_DP83848I: + case PHY_ID_TM4C129X: + xResult = pdTRUE; + break; + + case PHY_ID_MV88E6071: /* Marvell 88E6071 */ + default: + /* Most PHY's do not have a 19_PHYCR */ + 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 = 0U; + + 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 & 0xFFF0U ); + + 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 ] ) ); + } + + return pxPhyObject->xPortCount; +} +/*-----------------------------------------------------------*/ + +/* Send a reset command 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 of PHY ports that are ready. */ + ulDoneMask = 0U; + + /* 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( phyPHY_MAX_RESET_TIME_MS ); + 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 |= ( 1U << xPhyIndex ); + } + } + + if( ulDoneMask == ulPhyMask ) + { + break; + } + + if( xTaskCheckForTimeOut( &xTimer, &xRemainingTime ) != pdFALSE ) + { + FreeRTOS_printf( ( "xPhyReset: phyBMCR_RESET timed out ( done 0x%02lX )\n", ulDoneMask ) ); + break; + } + + /* Block for a while */ + vTaskDelay( pdMS_TO_TICKS( phySHORT_DELAY_MS ) ); + } + + /* Clear the reset bits. */ + for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ ) + { + if( ( ulDoneMask & ( 1U << xPhyIndex ) ) == 0U ) + { + BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ]; + + /* The reset operation timed out, clear the bit manually. */ + pxPhyObject->fnPhyRead( xPhyAddress, phyREG_00_BMCR, &ulConfig ); + pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, ulConfig & ~phyBMCR_RESET ); + } + } + + vTaskDelay( pdMS_TO_TICKS( phySHORT_DELAY_MS ) ); + + 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( ( "xPhyConfigure: 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_ALL; + /* Reset auto-negotiation capability. */ + } + else + { + /* Always select protocol 802.3u. */ + 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 command 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 ) || ( pxPhyProperties->ucSpeed == ( uint8_t ) PHY_SPEED_AUTO ) ) + { + 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 ) || ( pxPhyProperties->ucDuplex == ( uint8_t ) PHY_DUPLEX_AUTO ) ) + { + 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 ) ); + } + + /* Keep these values for later use. */ + pxPhyObject->ulBCRValue = ulConfig & ~phyBMCR_ISOLATE; + pxPhyObject->ulACRValue = ulAdvertise; + + return 0; +} +/*-----------------------------------------------------------*/ + +/* xPhyFixedValue(): this function is called in case auto-negotiation is disabled. + * The caller has set the values in 'xPhyPreferences' (ucDuplex and ucSpeed). + * The PHY register phyREG_00_BMCR will be set for every connected PHY that matches + * with ulPhyMask. */ +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; +} +/*-----------------------------------------------------------*/ + +/* xPhyStartAutoNegotiation() is the alternative xPhyFixedValue(): + * It sets the BMCR_AN_RESTART bit and waits for the auto-negotiation completion + * ( phyBMSR_AN_COMPLETE ). */ +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 < ( uint32_t ) 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 ); + } + } + + xRemainingTime = ( TickType_t ) pdMS_TO_TICKS( phyPHY_MAX_NEGOTIATE_TIME_MS ); + vTaskSetTimeOutState( &xTimer ); + ulDoneMask = 0; + + /* Wait until the auto-negotiation will be completed */ + for( ; ; ) + { + ulBitMask = ( uint32_t ) 1U; + + for( xPhyIndex = 0; xPhyIndex < ( uint32_t ) 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( ( "xPhyStartAutoNegotiation: phyBMSR_AN_COMPLETE timed out ( done 0x%02lX )\n", ulDoneMask ) ); + break; + } + + vTaskDelay( pdMS_TO_TICKS( phySHORT_DELAY_MS ) ); + } + + if( ulDoneMask != ( uint32_t ) 0U ) + { + ulBitMask = ( uint32_t ) 1U; + pxPhyObject->ulLinkStatusMask &= ~( ulDoneMask ); + + for( xPhyIndex = 0; xPhyIndex < ( uint32_t ) 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( ulPhyID == PHY_ID_KSZ8081MNXIA ) + { + uint32_t ulControlStatus; + + pxPhyObject->fnPhyRead( xPhyAddress, 0x1E, &ulControlStatus ); + + switch( ulControlStatus & 0x07 ) + { + case 0x01: + case 0x05: +/* [001] = 10BASE-T half-duplex */ +/* [101] = 10BASE-T full-duplex */ + /* 10 Mbps. */ + ulRegValue |= phyPHYSTS_SPEED_STATUS; + break; + + case 0x02: + case 0x06: +/* [010] = 100BASE-TX half-duplex */ +/* [110] = 100BASE-TX full-duplex */ + break; + } + + switch( ulControlStatus & 0x07 ) + { + case 0x05: + case 0x06: +/* [101] = 10BASE-T full-duplex */ +/* [110] = 100BASE-TX full-duplex */ + /* Full duplex. */ + ulRegValue |= phyPHYSTS_DUPLEX_STATUS; + break; + + case 0x01: + case 0x02: +/* [001] = 10BASE-T half-duplex */ +/* [010] = 100BASE-TX half-duplex */ + break; + } + } + else if( ulPhyID == PHY_ID_KSZ8795 ) + { + /* KSZ8795 has a different mapping for the Port Operation Mode Indication field + * in the phyREG_1F_PHYSPCS than other similar PHYs: + * 010 = 10BASE-T half-duplex + * 101 = 10BASE-T full-duplex + * 011 = 100BASE-TX half-duplex + * 110 = 100BASE-TX full-duplex + */ + uint32_t ulControlStatus = 0u; + uint32_t ulPortOperationMode = 0u; + pxPhyObject->fnPhyRead( xPhyAddress, phyREG_1F_PHYSPCS, &ulControlStatus ); + ulPortOperationMode = ( ulControlStatus >> 8u ) & 0x07u; + + ulRegValue = 0; + + /* Detect 10baseT operation */ + if( ( 0x02u == ulPortOperationMode ) || ( 0x05u == ulPortOperationMode ) ) + { + ulRegValue |= phyPHYSTS_SPEED_STATUS; + } + + /* Detect full duplex operation */ + if( ( 0x05u == ulPortOperationMode ) || ( 0x06u == ulPortOperationMode ) ) + { + ulRegValue |= phyPHYSTS_DUPLEX_STATUS; + } + } + else 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" ) ); + + 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 ); + + for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 ) + { + if( ( pxPhyObject->ulLinkStatusMask & ulBitMask ) == 0UL ) + { + pxPhyObject->ulLinkStatusMask |= ulBitMask; + FreeRTOS_printf( ( "xPhyCheckLinkStatus: PHY LS now %02lX\n", pxPhyObject->ulLinkStatusMask ) ); + xNeedCheck = pdTRUE; + } + } + } + else if( xTaskCheckForTimeOut( &( pxPhyObject->xLinkStatusTimer ), &( pxPhyObject->xLinkStatusRemaining ) ) != pdFALSE ) + { + /* Frequent checking the PHY Link Status can affect for the performance of Ethernet controller. + * As long as packets are received, no polling is needed. + * Otherwise, polling will be done when the 'xLinkStatusTimer' expires. */ + 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 ) ); + xNeedCheck = pdTRUE; + } + } + } + + vTaskSetTimeOutState( &( pxPhyObject->xLinkStatusTimer ) ); + + if( ( pxPhyObject->ulLinkStatusMask & ( ulBitMask >> 1 ) ) != 0 ) + { + /* The link status is high, so don't poll the PHY too often. */ + pxPhyObject->xLinkStatusRemaining = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS ); + } + else + { + /* The link status is low, polling may be done more frequently. */ + pxPhyObject->xLinkStatusRemaining = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS ); + } + } + + return xNeedCheck; +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c new file mode 100644 index 0000000..aabfa7b --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c @@ -0,0 +1,950 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +/* Some files from the Atmel Software Framework */ +/* gmac_SAM.[ch] is a combination of the gmac.[ch] for both SAM4E and SAME70. */ +#include "gmac_SAM.h" +#include +#include "phyHandling.h" + +/* This file is included to see if 'CONF_BOARD_ENABLE_CACHE' is defined. */ +#include "conf_board.h" + + +/* Interrupt events to process. Currently only the Rx event is processed + * although code for other events is included to allow for possible future + * expansion. */ +#define EMAC_IF_RX_EVENT 1UL +#define EMAC_IF_TX_EVENT 2UL +#define EMAC_IF_ERR_EVENT 4UL +#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) + +/* 1536 bytes is more than needed, 1524 would be enough. + * But 1536 is a multiple of 32, which gives a great alignment for + * cached memories. */ + +#define NETWORK_BUFFER_SIZE 1536 + +#ifndef EMAC_MAX_BLOCK_TIME_MS + +/* The task 'prvEMACHandlerTask()' will wake-up every 100 ms, to see + * if something has to be done, mostly checking if the PHY has a + * change in Link Status. */ + #define EMAC_MAX_BLOCK_TIME_MS 100ul +#endif + +#if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) + #error This driver works optimal if ipconfigZERO_COPY_RX_DRIVER is defined as 1 +#endif + +#if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + #error This driver works optimal if ipconfigZERO_COPY_TX_DRIVER is defined as 1 +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to 4x + * the size of the stack used by the idle task - but allow this to be overridden in + * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 4 * configMINIMAL_STACK_SIZE ) +#endif + +#ifndef niEMAC_HANDLER_TASK_PRIORITY + #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1 +#endif + +#if ( __DCACHE_PRESENT != 0 ) && defined( CONF_BOARD_ENABLE_CACHE ) + #include "core_cm7.h" + #warning This driver assumes the presence of DCACHE + #define NETWORK_BUFFERS_CACHED 1 + #define CACHE_LINE_SIZE 32 + #define NETWORK_BUFFER_HEADER_SIZE ( ipconfigPACKET_FILLER_SIZE + 8 ) + + static void cache_clean_invalidate() + { + /* If you application crashes here, make sure that SCB_EnableDCache() has been called. */ + SCB_CleanInvalidateDCache(); + } + /*-----------------------------------------------------------*/ + + static void cache_clean_invalidate_by_addr( uint32_t addr, + uint32_t size ) + { + /* SAME70 does not have clean/invalidate per area. */ + /* SCB_CleanInvalidateDCache_by_Addr( ( uint32_t * )addr, size); */ + SCB_CleanInvalidateDCache(); + } + /*-----------------------------------------------------------*/ + + static void cache_invalidate_by_addr( addr, + size ) \ + { + /* SAME70 does not have clean/invalidate per area. */ + /* SCB_InvalidateDCache_by_Addr( ( uint32_t * )addr, size); */ + SCB_InvalidateDCache(); + } + /*-----------------------------------------------------------*/ + +#else /* if ( __DCACHE_PRESENT != 0 ) && defined( CONF_BOARD_ENABLE_CACHE ) */ + #warning Sure there is no caching? + #define cache_clean_invalidate() do {} while( 0 ) + #define cache_clean_invalidate_by_addr( addr, size ) do {} while( 0 ) + #define cache_invalidate_by_addr( addr, size ) do {} while( 0 ) +#endif /* if ( __DCACHE_PRESENT != 0 ) && defined( CONF_BOARD_ENABLE_CACHE ) */ + +/*-----------------------------------------------------------*/ + +/* + * Update settings in GMAC for speed and duplex. + */ +static void prvEthernetUpdateConfig( BaseType_t xForce ); + +/* + * Access functions to the PHY's: read() and write() to be used by + * phyHandling.c. + */ +static BaseType_t xPHY_Read( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t * pulValue ); +static BaseType_t xPHY_Write( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t ulValue ); + +#if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 ) + void vGMACGenerateChecksum( uint8_t * apBuffer, + size_t uxLength ); +#endif + +/* + * Called from the ASF GMAC driver. + */ +void xRxCallback( uint32_t ulStatus ); +void xTxCallback( uint32_t ulStatus, + uint8_t * puc_buffer ); + +/* + * A deferred interrupt handler task that processes GMAC interrupts. + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/* + * Initialise the ASF GMAC driver. + */ +static BaseType_t prvGMACInit( void ); + +/* + * Try to obtain an Rx packet from the hardware. + */ +static uint32_t prvEMACRxPoll( void ); + +/* + * Handle transmission errors. + */ +static void hand_tx_errors( void ); + +/*-----------------------------------------------------------*/ + +/* Bit map of outstanding ETH interrupt events for processing. Currently only + * the Rx interrupt is handled, although code is included for other events to + * enable future expansion. */ +static volatile uint32_t ulISREvents; + +/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ +static volatile BaseType_t xGMACSwitchRequired; + +/* LLMNR multicast address. */ +static const uint8_t llmnr_mac_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; + +/* The GMAC object as defined by the ASF drivers. */ +static gmac_device_t gs_gmac_dev; + +/* Holds the handle of the task used as a deferred interrupt processor. The + * handle is used so direct notifications can be sent to the task for all EMAC/DMA + * related interrupts. */ +TaskHandle_t xEMACTaskHandle = NULL; + +static QueueHandle_t xTxBufferQueue; +int tx_release_count[ 4 ]; + +/* xTXDescriptorSemaphore is a counting semaphore with + * a maximum count of GMAC_TX_BUFFERS, which is the number of + * DMA TX descriptors. */ +static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; + +/* For local use only: describe the PHY's properties: */ +const PhyProperties_t xPHYProperties = +{ + #if ( ipconfigETHERNET_AN_ENABLE != 0 ) + .ucSpeed = PHY_SPEED_AUTO, + .ucDuplex = PHY_DUPLEX_AUTO, + #else + #if ( ipconfigETHERNET_USE_100MB != 0 ) + .ucSpeed = PHY_SPEED_100, + #else + .ucSpeed = PHY_SPEED_10, + #endif + + #if ( ipconfigETHERNET_USE_FULL_DUPLEX != 0 ) + .ucDuplex = PHY_DUPLEX_FULL, + #else + .ucDuplex = PHY_DUPLEX_HALF, + #endif + #endif /* if ( ipconfigETHERNET_AN_ENABLE != 0 ) */ + + #if ( ipconfigETHERNET_AN_ENABLE != 0 ) && ( ipconfigETHERNET_AUTO_CROSS_ENABLE != 0 ) + .ucMDI_X = PHY_MDIX_AUTO, + #elif ( ipconfigETHERNET_CROSSED_LINK != 0 ) + .ucMDI_X = PHY_MDIX_CROSSED, + #else + .ucMDI_X = PHY_MDIX_DIRECT, + #endif +}; + +/* All PHY handling code has now been separated from the NetworkInterface.c, + * see "../Common/phyHandling.c". */ +static EthernetPhy_t xPhyObject; + +/*-----------------------------------------------------------*/ + +/* + * GMAC interrupt handler. + */ +void GMAC_Handler( void ) +{ + xGMACSwitchRequired = pdFALSE; + + /* gmac_handler() may call xRxCallback() which may change + * the value of xGMACSwitchRequired. */ + gmac_handler( &gs_gmac_dev ); + + if( xGMACSwitchRequired != pdFALSE ) + { + portEND_SWITCHING_ISR( xGMACSwitchRequired ); + } +} +/*-----------------------------------------------------------*/ + +void xRxCallback( uint32_t ulStatus ) +{ + if( ( ( ulStatus & GMAC_RSR_REC ) != 0 ) && ( xEMACTaskHandle != NULL ) ) + { + /* let the prvEMACHandlerTask know that there was an RX event. */ + ulISREvents |= EMAC_IF_RX_EVENT; + /* Only an RX interrupt can wakeup prvEMACHandlerTask. */ + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); + } +} +/*-----------------------------------------------------------*/ + +void returnTxBuffer( uint8_t * puc_buffer ) +{ + /* Called from a non-ISR context. */ + if( xTxBufferQueue != NULL ) + { + xQueueSend( xTxBufferQueue, &puc_buffer, 0 ); + xTaskNotifyGive( xEMACTaskHandle ); + ulISREvents |= EMAC_IF_TX_EVENT; + } +} + +void xTxCallback( uint32_t ulStatus, + uint8_t * puc_buffer ) +{ + if( ( xTxBufferQueue != NULL ) && ( xEMACTaskHandle != NULL ) ) + { + /* let the prvEMACHandlerTask know that there was an TX event. */ + ulISREvents |= EMAC_IF_TX_EVENT; + /* Wakeup prvEMACHandlerTask. */ + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); + xQueueSendFromISR( xTxBufferQueue, &puc_buffer, ( BaseType_t * ) &xGMACSwitchRequired ); + tx_release_count[ 2 ]++; + } +} +/*-----------------------------------------------------------*/ + + +/* + * The two standard defines 'GMAC_MAN_RW_TYPE' and 'GMAC_MAN_READ_ONLY' + * are incorrect. + * Therefore, use the following: + */ + +#define GMAC_MAINTENANCE_READ_ACCESS ( 2 ) +#define GMAC_MAINTENANCE_WRITE_ACCESS ( 1 ) + +static BaseType_t xPHY_Read( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t * pulValue ) +{ + BaseType_t xReturn; + UBaseType_t uxWasEnabled; + + /* Wait until bus idle */ + while( ( GMAC->GMAC_NSR & GMAC_NSR_IDLE ) == 0 ) + { + } + + /* Write maintain register */ + + /* + * OP: Operation: 10 is read. 01 is write. + */ + uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u; + + if( uxWasEnabled == 0u ) + { + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR |= GMAC_NCR_MPE; + } + + GMAC->GMAC_MAN = GMAC_MAN_WTN( GMAC_MAN_CODE_VALUE ) + | GMAC_MAN_CLTTO + | GMAC_MAN_PHYA( xAddress ) + | GMAC_MAN_REGA( xRegister ) + | GMAC_MAN_OP( GMAC_MAINTENANCE_READ_ACCESS ) + | GMAC_MAN_DATA( ( uint16_t ) 0u ); + + if( gmac_wait_phy( GMAC, MAC_PHY_RETRY_MAX ) == GMAC_TIMEOUT ) + { + *pulValue = ( uint32_t ) 0xffffu; + xReturn = -1; + } + else + { + /* Wait until bus idle */ + while( ( GMAC->GMAC_NSR & GMAC_NSR_IDLE ) == 0 ) + { + } + + /* Return data */ + *pulValue = ( uint32_t ) ( GMAC->GMAC_MAN & GMAC_MAN_DATA_Msk ); + + xReturn = 0; + } + + if( uxWasEnabled == 0u ) + { + /* Disable further GMAC maintenance. */ + GMAC->GMAC_NCR &= ~GMAC_NCR_MPE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xPHY_Write( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t ulValue ) +{ + BaseType_t xReturn; + UBaseType_t uxWasEnabled; + + /* Wait until bus idle */ + while( ( GMAC->GMAC_NSR & GMAC_NSR_IDLE ) == 0 ) + { + } + + /* Write maintain register */ + uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u; + + if( uxWasEnabled == 0u ) + { + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR |= GMAC_NCR_MPE; + } + + GMAC->GMAC_MAN = GMAC_MAN_WTN( GMAC_MAN_CODE_VALUE ) + | GMAC_MAN_CLTTO + | GMAC_MAN_PHYA( xAddress ) + | GMAC_MAN_REGA( xRegister ) + | GMAC_MAN_OP( GMAC_MAINTENANCE_WRITE_ACCESS ) + | GMAC_MAN_DATA( ( uint16_t ) ulValue ); + + if( gmac_wait_phy( GMAC, MAC_PHY_RETRY_MAX ) == GMAC_TIMEOUT ) + { + xReturn = -1; + } + else + { + xReturn = 0; + } + + if( uxWasEnabled == 0u ) + { + /* Disable further GMAC maintenance. */ + GMAC->GMAC_NCR &= ~GMAC_NCR_MPE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + const TickType_t x5_Seconds = 5000UL; + + if( xEMACTaskHandle == NULL ) + { + prvGMACInit(); + + cache_clean_invalidate(); + + /* The handler task is created at the highest possible priority to + * ensure the interrupt handler can return directly to it. */ + xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle ); + configASSERT( xEMACTaskHandle ); + } + + if( xTxBufferQueue == NULL ) + { + xTxBufferQueue = xQueueCreate( GMAC_TX_BUFFERS, sizeof( void * ) ); + configASSERT( xTxBufferQueue ); + } + + if( xTXDescriptorSemaphore == NULL ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) GMAC_TX_BUFFERS, ( UBaseType_t ) GMAC_TX_BUFFERS ); + configASSERT( xTXDescriptorSemaphore ); + } + + /* When returning non-zero, the stack will become active and + * start DHCP (in configured) */ + return xGetPhyLinkStatus(); +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ + BaseType_t xReturn; + + if( xPhyObject.ulLinkStatusMask != 0 ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** The GMAC TX errors to handle */ +#define GMAC_TX_ERRORS ( GMAC_TSR_TFC | GMAC_TSR_HRESP ) + +static void hand_tx_errors( void ) +{ +/* Handle GMAC underrun or AHB errors. */ + if( gmac_get_tx_status( GMAC ) & GMAC_TX_ERRORS ) + { + gmac_enable_transmit( GMAC, false ); + + /* Reinit TX descriptors. */ +/* gmac_tx_init(ps_gmac_dev); */ + gmac_reset_tx_mem( &gs_gmac_dev ); + /* Clear error status. */ + gmac_clear_tx_status( GMAC, GMAC_TX_ERRORS ); + + gmac_enable_transmit( GMAC, true ); + } +} + +volatile IPPacket_t * pxSendPacket; + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t bReleaseAfterSend ) +{ +/* Do not wait too long for a free TX DMA buffer. */ + const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u ); + uint32_t ulTransmitSize; + + ulTransmitSize = pxDescriptor->xDataLength; + + pxSendPacket = ( IPPacket_t * ) pxDescriptor->pucEthernetBuffer; + + if( ulTransmitSize > NETWORK_BUFFER_SIZE ) + { + ulTransmitSize = NETWORK_BUFFER_SIZE; + } + + /* A do{}while(0) loop is introduced to allow the use of multiple break + * statement. */ + do + { + if( xPhyObject.ulLinkStatusMask == 0ul ) + { + /* Do not attempt to send packets as long as the Link Status is low. */ + break; + } + + if( xTXDescriptorSemaphore == NULL ) + { + /* Semaphore has not been created yet? */ + break; + } + + hand_tx_errors(); + + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + /* Time-out waiting for a free TX descriptor. */ + tx_release_count[ 3 ]++; + break; + } + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Confirm that the pxDescriptor may be kept by the driver. */ + configASSERT( bReleaseAfterSend != pdFALSE ); + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + #if ( NETWORK_BUFFERS_CACHED != 0 ) + { + uint32_t xlength = CACHE_LINE_SIZE * ( ( ulTransmitSize + NETWORK_BUFFER_HEADER_SIZE + CACHE_LINE_SIZE - 1 ) / CACHE_LINE_SIZE ); + uint32_t xAddress = ( uint32_t ) ( pxDescriptor->pucEthernetBuffer - NETWORK_BUFFER_HEADER_SIZE ); + cache_clean_invalidate_by_addr( xAddress, xlength ); + } + #endif + + gmac_dev_write( &gs_gmac_dev, ( void * ) pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ); + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Confirm that the pxDescriptor may be kept by the driver. */ + bReleaseAfterSend = pdFALSE; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + /* Not interested in a call-back after TX. */ + iptraceNETWORK_INTERFACE_TRANSMIT(); + } while( ipFALSE_BOOL ); + + if( bReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvGMACInit( void ) +{ + uint32_t ncfgr; + + gmac_options_t gmac_option; + + gmac_enable_management( GMAC, true ); + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR |= GMAC_NCR_MPE; + + memset( &gmac_option, '\0', sizeof( gmac_option ) ); + gmac_option.uc_copy_all_frame = 0; + gmac_option.uc_no_boardcast = 0; + memcpy( gmac_option.uc_mac_addr, ipLOCAL_MAC_ADDRESS, sizeof( gmac_option.uc_mac_addr ) ); + + gs_gmac_dev.p_hw = GMAC; + gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option ); + + NVIC_SetPriority( GMAC_IRQn, configMAC_INTERRUPT_PRIORITY ); + NVIC_EnableIRQ( GMAC_IRQn ); + + { + /* Set MDC clock divider. */ + gmac_set_mdc_clock( GMAC, sysclk_get_cpu_hz() ); + + vPhyInitialise( &xPhyObject, xPHY_Read, xPHY_Write ); + xPhyDiscover( &xPhyObject ); + xPhyConfigure( &xPhyObject, &xPHYProperties ); + + /* For a reset / reconfigure of the EMAC. */ + prvEthernetUpdateConfig( pdTRUE ); + + /* Select Media Independent Interface type */ + #if ( SAME70 != 0 ) + { + /* Selecting RMII mode. */ + GMAC->GMAC_UR &= ~GMAC_UR_RMII; + } + #else + { + gmac_select_mii_mode( GMAC, ETH_PHY_MODE ); + } + #endif + + gmac_enable_transmit( GMAC, true ); + gmac_enable_receive( GMAC, true ); + } + + gmac_enable_management( GMAC, true ); + + gmac_set_address( GMAC, 1, ( uint8_t * ) llmnr_mac_address ); + + gmac_enable_management( GMAC, false ); + /* Disable further GMAC maintenance. */ + GMAC->GMAC_NCR &= ~GMAC_NCR_MPE; + + return 1; +} +/*-----------------------------------------------------------*/ + +static void prvEthernetUpdateConfig( BaseType_t xForce ) +{ + FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS mask %02lX Force %d\n", + xPhyObject.ulLinkStatusMask, + ( int ) xForce ) ); + + if( ( xForce != pdFALSE ) || ( xPhyObject.ulLinkStatusMask != 0 ) ) + { + #if ( ipconfigETHERNET_AN_ENABLE != 0 ) + { + UBaseType_t uxWasEnabled; + + /* Restart the auto-negotiation. */ + uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u; + + if( uxWasEnabled == 0u ) + { + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR |= GMAC_NCR_MPE; + } + + xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) ); + + /* Configure the MAC with the Duplex Mode fixed by the + * auto-negotiation process. */ + if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL ) + { + gmac_enable_full_duplex( GMAC, pdTRUE ); + } + else + { + gmac_enable_full_duplex( GMAC, pdFALSE ); + } + + /* Configure the MAC with the speed fixed by the + * auto-negotiation process. */ + if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 ) + { + gmac_set_speed( GMAC, pdFALSE ); + } + else + { + gmac_set_speed( GMAC, pdTRUE ); + } + + if( uxWasEnabled == 0u ) + { + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR &= ~GMAC_NCR_MPE; + } + } + #else /* if ( ipconfigETHERNET_AN_ENABLE != 0 ) */ + { + if( xPHYProperties.ucDuplex == PHY_DUPLEX_FULL ) + { + xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_FULL; + gmac_enable_full_duplex( GMAC, pdTRUE ); + } + else + { + xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_HALF; + gmac_enable_full_duplex( GMAC, pdFALSE ); + } + + if( xPHYProperties.ucSpeed == PHY_SPEED_100 ) + { + xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_100; + gmac_set_speed( GMAC, pdTRUE ); + } + else + { + xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_10; + gmac_set_speed( GMAC, pdFALSE ); + } + + xPhyObject.xPhyPreferences.ucMDI_X = PHY_MDIX_AUTO; + + /* Use predefined (fixed) configuration. */ + xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) ); + } + #endif /* if ( ipconfigETHERNET_AN_ENABLE != 0 ) */ + } +} +/*-----------------------------------------------------------*/ + +void vGMACGenerateChecksum( uint8_t * pucBuffer, + size_t uxLength ) +{ + ProtocolPacket_t * xProtPacket = ( ProtocolPacket_t * ) pucBuffer; + + if( xProtPacket->xTCPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) + { + IPHeader_t * pxIPHeader = &( xProtPacket->xTCPPacket.xIPHeader ); + + /* Calculate the IP header checksum. */ + pxIPHeader->usHeaderChecksum = 0x00; + pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); + pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); + + /* Calculate the TCP checksum for an outgoing packet. */ + usGenerateProtocolChecksum( pucBuffer, uxLength, pdTRUE ); + } +} +/*-----------------------------------------------------------*/ + +static uint32_t prvEMACRxPoll( void ) +{ + unsigned char * pucUseBuffer; + uint32_t ulReceiveCount, ulResult, ulReturnValue = 0; + static NetworkBufferDescriptor_t * pxNextNetworkBufferDescriptor = NULL; + const UBaseType_t xMinDescriptorsToLeave = 2UL; + const TickType_t xBlockTime = pdMS_TO_TICKS( 100UL ); + static IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + uint8_t * pucDMABuffer = NULL; + + for( ; ; ) + { + /* If pxNextNetworkBufferDescriptor was not left pointing at a valid + * descriptor then allocate one now. */ + if( ( pxNextNetworkBufferDescriptor == NULL ) && ( uxGetNumberOfFreeNetworkBuffers() > xMinDescriptorsToLeave ) ) + { + pxNextNetworkBufferDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xBlockTime ); + } + + if( pxNextNetworkBufferDescriptor != NULL ) + { + /* Point pucUseBuffer to the buffer pointed to by the descriptor. */ + pucUseBuffer = ( unsigned char * ) ( pxNextNetworkBufferDescriptor->pucEthernetBuffer - ipconfigPACKET_FILLER_SIZE ); + } + else + { + /* As long as pxNextNetworkBufferDescriptor is NULL, the incoming + * messages will be flushed and ignored. */ + pucUseBuffer = NULL; + } + + /* Read the next packet from the hardware into pucUseBuffer. */ + ulResult = gmac_dev_read( &gs_gmac_dev, pucUseBuffer, ipTOTAL_ETHERNET_FRAME_SIZE, &ulReceiveCount, &pucDMABuffer ); + + if( ( ulResult != GMAC_OK ) || ( ulReceiveCount == 0 ) ) + { + /* No data from the hardware. */ + break; + } + + if( pxNextNetworkBufferDescriptor == NULL ) + { + /* Data was read from the hardware, but no descriptor was available + * for it, so it will be dropped. */ + iptraceETHERNET_RX_EVENT_LOST(); + continue; + } + + iptraceNETWORK_INTERFACE_RECEIVE(); + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + pxNextNetworkBufferDescriptor = pxPacketBuffer_to_NetworkBuffer( pucDMABuffer ); + + if( pxNextNetworkBufferDescriptor == NULL ) + { + /* Strange: can not translate from a DMA buffer to a Network Buffer. */ + break; + } + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + pxNextNetworkBufferDescriptor->xDataLength = ( size_t ) ulReceiveCount; + xRxEvent.pvData = ( void * ) pxNextNetworkBufferDescriptor; + + /* Send the descriptor to the IP task for processing. */ + if( xSendEventStructToIPTask( &xRxEvent, xBlockTime ) != pdTRUE ) + { + /* The buffer could not be sent to the stack so must be released + * again. */ + vReleaseNetworkBufferAndDescriptor( pxNextNetworkBufferDescriptor ); + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) ); + } + + /* Now the buffer has either been passed to the IP-task, + * or it has been released in the code above. */ + pxNextNetworkBufferDescriptor = NULL; + ulReturnValue++; + } + + return ulReturnValue; +} +/*-----------------------------------------------------------*/ + +extern uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * NETWORK_BUFFER_SIZE ]; +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + uint8_t * ucRAMBuffer = ucNetworkPackets; + uint32_t ulIndex; + + for( ulIndex = 0; ulIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ulIndex++ ) + { + pxNetworkBuffers[ ulIndex ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING; + *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ulIndex ] ) ); + ucRAMBuffer += NETWORK_BUFFER_SIZE; + } + + cache_clean_invalidate(); +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void * pvParameters ) +{ + UBaseType_t uxCount; + UBaseType_t uxLowestSemCount = 0; + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + NetworkBufferDescriptor_t * pxBuffer; + #endif + uint8_t * pucBuffer; + BaseType_t xResult = 0; + uint32_t xStatus; + const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS ); + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + configASSERT( xEMACTaskHandle ); + + for( ; ; ) + { + xResult = 0; + + #if ( ipconfigHAS_PRINTF != 0 ) + { + /* Call a function that monitors resources: the amount of free network + * buffers and the amount of free space on the heap. See FreeRTOS_IP.c + * for more detailed comments. */ + vPrintResourceStats(); + + if( xTXDescriptorSemaphore != NULL ) + { + UBaseType_t uxCurrentSemCount = uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + if( uxLowestSemCount > uxCurrentSemCount ) + { + uxLowestSemCount = uxCurrentSemCount; + FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) ); + } + } + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + + if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 ) + { + /* No events to process now, wait for the next. */ + ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); + } + + if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 ) + { + ulISREvents &= ~EMAC_IF_RX_EVENT; + + /* Wait for the EMAC interrupt to indicate that another packet has been + * received. */ + xResult = prvEMACRxPoll(); + } + + if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) + { + /* Future extension: code to release TX buffers if zero-copy is used. */ + ulISREvents &= ~EMAC_IF_TX_EVENT; + + while( xQueueReceive( xTxBufferQueue, &pucBuffer, 0 ) != pdFALSE ) + { + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + pxBuffer = pxPacketBuffer_to_NetworkBuffer( pucBuffer ); + + if( pxBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + tx_release_count[ 0 ]++; + } + else + { + tx_release_count[ 1 ]++; + } + } + #else /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + { + tx_release_count[ 0 ]++; + } + #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + uxCount = uxQueueMessagesWaiting( ( QueueHandle_t ) xTXDescriptorSemaphore ); + + if( uxCount < GMAC_TX_BUFFERS ) + { + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + } + } + } + + if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 ) + { + /* Future extension: logging about errors that occurred. */ + ulISREvents &= ~EMAC_IF_ERR_EVENT; + } + + gmac_enable_management( GMAC, true ); + + if( xPhyCheckLinkStatus( &xPhyObject, xResult ) != 0 ) + { + /* Something has changed to a Link Status, need re-check. */ + prvEthernetUpdateConfig( pdFALSE ); + } + + gmac_enable_management( GMAC, false ); + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/DriverSAM/gmac_SAM.c b/FreeRTOS/source/portable/NetworkInterface/DriverSAM/gmac_SAM.c new file mode 100644 index 0000000..7b2d445 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/DriverSAM/gmac_SAM.c @@ -0,0 +1,997 @@ +/** + * \file + * + * \brief GMAC (Ethernet MAC) driver for SAM. + * + * Copyright (c) 2015-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +/* + * Support and FAQ: visit Atmel Support + */ + + +/* Standard includes. */ +#include +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +#include "FreeRTOSIPConfig.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "compiler.h" +#include "gmac_SAM.h" + +#if ( SAME70 != 0 ) + /* This file is included to see if 'CONF_BOARD_ENABLE_CACHE' is defined. */ + #include "conf_board.h" + #include "core_cm7.h" +#endif + +/*/ @cond 0 */ +/**INDENT-OFF**/ +#ifdef __cplusplus + extern "C" { +#endif +/**INDENT-ON**/ +/*/ @endcond */ + +#ifndef ARRAY_SIZE + #define ARRAY_SIZE( x ) ( int ) ( sizeof( x ) / sizeof( x )[ 0 ] ) +#endif + +#if ( GMAC_RX_BUFFERS <= 1 ) + #error Configuration error +#endif + +#if ( GMAC_TX_BUFFERS <= 1 ) + #error Configuration error +#endif + +/** + * \defgroup gmac_group Ethernet Media Access Controller + * + * See \ref gmac_quickstart. + * + * Driver for the GMAC (Ethernet Media Access Controller). + * This file contains basic functions for the GMAC, with support for all modes, settings + * and clock speeds. + * + * \section dependencies Dependencies + * This driver does not depend on other modules. + * + * @{ + */ + +#define NETWORK_BUFFER_SIZE 1536 + +__attribute__( ( aligned( 32 ) ) ) +__attribute__( ( section( ".first_data" ) ) ) +uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * NETWORK_BUFFER_SIZE ]; + +/** TX descriptor lists */ +__attribute__( ( section( ".first_data" ) ) ) +COMPILER_ALIGNED( 8 ) +static gmac_tx_descriptor_t gs_tx_desc[ GMAC_TX_BUFFERS ]; + +#if ( SAME70 != 0 ) + __attribute__( ( section( ".first_data" ) ) ) + COMPILER_ALIGNED( 8 ) + static gmac_tx_descriptor_t gs_tx_desc_null; +#endif + +/** RX descriptors lists */ +__attribute__( ( section( ".first_data" ) ) ) +COMPILER_ALIGNED( 8 ) +static gmac_rx_descriptor_t gs_rx_desc[ GMAC_RX_BUFFERS ]; + +#if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + +/** Send Buffer. Section 3.6 of AMBA 2.0 spec states that burst should not cross the + * 1K Boundaries. Receive buffer manager write operations are burst of 2 words => 3 lsb bits + * of the address shall be set to 0. + */ + __attribute__( ( section( ".first_data" ) ) ) + COMPILER_ALIGNED( 8 ) + static uint8_t gs_uc_tx_buffer[ GMAC_TX_BUFFERS * GMAC_TX_UNITSIZE ]; +#endif /* ipconfigZERO_COPY_TX_DRIVER */ + +#if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) + /** Receive Buffer */ + __attribute__( ( section( ".first_data" ) ) ) + COMPILER_ALIGNED( 8 ) + static uint8_t gs_uc_rx_buffer[ GMAC_RX_BUFFERS * GMAC_RX_UNITSIZE ]; +#endif /* ipconfigZERO_COPY_RX_DRIVER */ + +/** Return count in buffer */ +#define CIRC_CNT( head, tail, size ) ( ( ( head ) - ( tail ) ) % ( size ) ) + +/* + * Return space available, from 0 to size-1. + * Always leave one free char as a completely full buffer that has (head == tail), + * which is the same as empty. + */ +#define CIRC_SPACE( head, tail, size ) CIRC_CNT( ( tail ), ( ( head ) + 1 ), ( size ) ) + +/** Circular buffer is empty ? */ +#define CIRC_EMPTY( head, tail ) ( ( head ) == ( tail ) ) +/** Clear circular buffer */ +#define CIRC_CLEAR( head, tail ) do { ( head ) = 0; ( tail ) = 0; } while( 0 ) + +/* Two call-back functions that should be defined in NetworkInterface.c */ +extern void xRxCallback( uint32_t ulStatus ); +extern void xTxCallback( uint32_t ulStatus, + uint8_t * puc_buffer ); +extern void returnTxBuffer( uint8_t * puc_buffer ); + + +/** Increment head or tail */ +static __inline void circ_inc32( int32_t * lHeadOrTail, + uint32_t ulSize ) +{ + ( *lHeadOrTail )++; + + if( ( *lHeadOrTail ) >= ( int32_t ) ulSize ) + { + ( *lHeadOrTail ) = 0; + } +} + +/** + * \brief Wait PHY operation to be completed. + * + * \param p_gmac HW controller address. + * \param ul_retry The retry times, 0 to wait forever until completeness. + * + * Return GMAC_OK if the operation is completed successfully. + */ +uint8_t gmac_wait_phy( Gmac * p_gmac, + const uint32_t ul_retry ) +{ + volatile uint32_t ul_retry_count = 0; + const uint32_t xPHYPollDelay = pdMS_TO_TICKS( 1ul ); + + while( !gmac_is_phy_idle( p_gmac ) ) + { + if( ul_retry == 0 ) + { + continue; + } + + ul_retry_count++; + + if( ul_retry_count >= ul_retry ) + { + return GMAC_TIMEOUT; + } + + /* Block the task to allow other tasks to execute while the PHY + * is not connected. */ + vTaskDelay( xPHYPollDelay ); + } + + return GMAC_OK; +} + +/** + * \brief Disable transfer, reset registers and descriptor lists. + * + * \param p_dev Pointer to GMAC driver instance. + * + */ +void gmac_reset_tx_mem( gmac_device_t * p_dev ) +{ + Gmac * p_hw = p_dev->p_hw; + + uint32_t ul_index; + uint32_t ul_address; + + /* Disable TX */ + gmac_enable_transmit( p_hw, 0 ); + + { + for( ul_index = 0; ul_index < ARRAY_SIZE( gs_tx_desc ); ul_index++ ) + { + uint32_t ulAddr = gs_tx_desc[ ul_index ].addr; + + if( ulAddr ) + { + returnTxBuffer( ( uint8_t * ) ulAddr ); + } + } + } + /* Set up the TX descriptors */ + CIRC_CLEAR( p_dev->l_tx_head, p_dev->l_tx_tail ); + + for( ul_index = 0; ul_index < GMAC_TX_BUFFERS; ul_index++ ) + { + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + ul_address = ( uint32_t ) 0u; + } + #else + { + ul_address = ( uint32_t ) ( &( gs_uc_tx_buffer[ ul_index * GMAC_TX_UNITSIZE ] ) ); + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + gs_tx_desc[ ul_index ].addr = ul_address; + gs_tx_desc[ ul_index ].status.val = GMAC_TXD_USED; + } + + /* Set the WRAP bit in the last descriptor. */ + gs_tx_desc[ GMAC_TX_BUFFERS - 1 ].status.val = GMAC_TXD_USED | GMAC_TXD_WRAP; + + /* Set transmit buffer queue */ + gmac_set_tx_queue( p_hw, ( uint32_t ) gs_tx_desc ); + #if ( SAME70 != 0 ) + { + gmac_set_tx_priority_queue( p_hw, ( uint32_t ) &gs_tx_desc_null, GMAC_QUE_1 ); + gmac_set_tx_priority_queue( p_hw, ( uint32_t ) &gs_tx_desc_null, GMAC_QUE_2 ); + /* Note that SAME70 REV B had 6 priority queues. */ + gmac_set_tx_priority_queue( p_hw, ( uint32_t ) &gs_tx_desc_null, GMAC_QUE_3 ); + gmac_set_tx_priority_queue( p_hw, ( uint32_t ) &gs_tx_desc_null, GMAC_QUE_4 ); + gmac_set_tx_priority_queue( p_hw, ( uint32_t ) &gs_tx_desc_null, GMAC_QUE_5 ); + } + #endif +} + +/** + * \brief Disable receiver, reset registers and descriptor list. + * + * \param p_dev Pointer to GMAC Driver instance. + */ +static void gmac_reset_rx_mem( gmac_device_t * p_dev ) +{ + Gmac * p_hw = p_dev->p_hw; + + uint32_t ul_index; + uint32_t ul_address; + + /* Disable RX */ + gmac_enable_receive( p_hw, 0 ); + + /* Set up the RX descriptors */ + p_dev->ul_rx_idx = 0; + + for( ul_index = 0; ul_index < GMAC_RX_BUFFERS; ul_index++ ) + { + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + NetworkBufferDescriptor_t * pxNextNetworkBufferDescriptor; + + pxNextNetworkBufferDescriptor = pxGetNetworkBufferWithDescriptor( GMAC_RX_UNITSIZE, 0ul ); + configASSERT( pxNextNetworkBufferDescriptor != NULL ); + ul_address = ( uint32_t ) ( pxNextNetworkBufferDescriptor->pucEthernetBuffer ); + } + #else + { + ul_address = ( uint32_t ) ( &( gs_uc_rx_buffer[ ul_index * GMAC_RX_UNITSIZE ] ) ); + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + gs_rx_desc[ ul_index ].addr.val = ul_address & GMAC_RXD_ADDR_MASK; + gs_rx_desc[ ul_index ].status.val = 0; + } + + /* Set the WRAP bit in the last descriptor. */ + gs_rx_desc[ GMAC_RX_BUFFERS - 1 ].addr.bm.b_wrap = 1; + + /* Set receive buffer queue */ + gmac_set_rx_queue( p_hw, ( uint32_t ) gs_rx_desc ); +} + + +/** + * \brief Initialize the allocated buffer lists for GMAC driver to transfer data. + * Must be invoked after gmac_dev_init() but before RX/TX starts. + * + * \note If input address is not 8-byte aligned, the address is automatically + * adjusted and the list size is reduced by one. + * + * \param p_gmac Pointer to GMAC instance. + * \param p_gmac_dev Pointer to GMAC device instance. + * \param p_dev_mm Pointer to the GMAC memory management control block. + * + * \return GMAC_OK or GMAC_PARAM. + */ +static uint8_t gmac_init_mem( Gmac * p_gmac, + gmac_device_t * p_gmac_dev ) +{ + /* Assign TX buffers */ + #if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + if( ( ( ( uint32_t ) gs_uc_tx_buffer ) & 0x7 ) || + ( ( uint32_t ) p_dev_mm->p_tx_dscr & 0x7 ) ) + { + p_dev_mm->ul_tx_size--; + } + + p_gmac_dev->p_tx_buffer = + ( uint8_t * ) ( ( ( uint32_t ) gs_uc_tx_buffer ) & 0xFFFFFFF8 ); + #endif + + /* Reset TX & RX Memory */ + gmac_reset_rx_mem( p_gmac_dev ); + gmac_reset_tx_mem( p_gmac_dev ); + + /* Enable Rx and Tx, plus the statistics register */ + gmac_enable_transmit( p_gmac, true ); + gmac_enable_receive( p_gmac, true ); + gmac_enable_statistics_write( p_gmac, true ); + + /* Set up the interrupts for transmission and errors */ + gmac_enable_interrupt( p_gmac, + GMAC_IER_RLEX | /* Enable retry limit exceeded interrupt. */ + GMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */ + GMAC_IER_ROVR | /* Enable receive overrun interrupt. */ + GMAC_IER_TCOMP | /* Enable transmit complete interrupt. */ + GMAC_IER_TUR | /* Enable transmit underrun interrupt. */ + GMAC_IER_TFC | /* Enable transmit buffers exhausted in mid-frame interrupt. */ + GMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */ + GMAC_IER_PFNZ | /* Enable pause frame received interrupt. */ + GMAC_IER_PTZ | /* Enable pause time zero interrupt. */ + GMAC_IER_RCOMP ); /* Enable receive complete interrupt. */ + + return GMAC_OK; +} + +/** + * \brief Initialize the GMAC driver. + * + * \param p_gmac Pointer to the GMAC instance. + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param p_opt GMAC configure options. + */ +void gmac_dev_init( Gmac * p_gmac, + gmac_device_t * p_gmac_dev, + gmac_options_t * p_opt ) +{ + /* Disable TX & RX and more */ + gmac_network_control( p_gmac, 0 ); + gmac_disable_interrupt( p_gmac, ~0u ); + + gmac_clear_statistics( p_gmac ); + + /* Clear all status bits in the receive status register. */ + gmac_clear_rx_status( p_gmac, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA + | GMAC_RSR_HNO ); + + #ifndef GMAC_TSR_UND + /* GMAC_TSR_UND is only defined by SAM4E. */ + #define GMAC_TSR_UND 0ul + #endif + /* Clear all status bits in the transmit status register */ + gmac_clear_tx_status( p_gmac, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE + | GMAC_TSR_TFC | GMAC_TSR_TXCOMP | GMAC_TSR_UND ); + + /* Clear interrupts */ + gmac_get_interrupt_status( p_gmac ); + #if !defined( ETHERNET_CONF_DATA_OFFSET ) + + /* Receive Buffer Offset + * Indicates the number of bytes by which the received data + * is offset from the start of the receive buffer + * which can be handy for alignment reasons */ + /* Note: FreeRTOS+TCP wants to have this offset set to 2 bytes */ + #error ETHERNET_CONF_DATA_OFFSET not defined, assuming 0 + #endif + + /* Enable the copy of data into the buffers + * ignore broadcasts, and not copy FCS. */ + + gmac_set_config( p_gmac, + ( gmac_get_config( p_gmac ) & ~GMAC_NCFGR_RXBUFO_Msk ) | + GMAC_NCFGR_RFCS | /* Remove FCS, frame check sequence (last 4 bytes) */ + GMAC_NCFGR_PEN | /* Pause Enable */ + GMAC_NCFGR_RXBUFO( ETHERNET_CONF_DATA_OFFSET ) | /* Set Ethernet Offset */ + GMAC_RXD_RXCOEN ); /* RXCOEN related function */ + + /* + * GMAC_DCFGR_TXCOEN: (GMAC_DCFGR) Transmitter Checksum Generation Offload Enable. + * Note: SAM4E/SAME70 do have RX checksum offloading + * but TX checksum offloading has NOT been implemented, + * at least on a SAM4E. + * http://community.atmel.com/forum/sam4e-gmac-transmit-checksum-offload-enablesolved + */ + + { + uint32_t ulValue = gmac_get_dma( p_gmac ); + + /* Let the GMAC set TX checksum's. */ + ulValue |= GMAC_DCFGR_TXCOEN; + #if ( SAME70 != 0 ) + { + /* Transmitter Packet Buffer Memory Size Select: + * Use full configured addressable space (4 Kbytes). */ + ulValue |= GMAC_DCFGR_TXPBMS; + } + #endif + + /* Clear the DMA Receive Buffer Size (DRBS) field: */ + ulValue &= ~( GMAC_DCFGR_DRBS_Msk ); + /* And set it: */ + ulValue |= ( GMAC_RX_UNITSIZE / 64 ) << GMAC_DCFGR_DRBS_Pos; + + gmac_set_dma( p_gmac, ulValue ); + } + + /* Enable/Disable Copy(Receive) All Valid Frames. */ + gmac_enable_copy_all( p_gmac, p_opt->uc_copy_all_frame ); + + /* Disable/Enable broadcast receiving */ + gmac_disable_broadcast( p_gmac, p_opt->uc_no_boardcast ); + + + /* Initialize memory */ + gmac_init_mem( p_gmac, p_gmac_dev ); + + /* Set Mac Address */ + gmac_set_address( p_gmac, 0, p_opt->uc_mac_addr ); +} + +/** + * \brief Frames can be read from the GMAC in multiple sections. + * + * Returns > 0 if a complete frame is available + * It also it cleans up incomplete older frames + */ + +static uint32_t gmac_dev_poll( gmac_device_t * p_gmac_dev ) +{ + uint32_t ulReturn = 0; + int32_t ulIndex = p_gmac_dev->ul_rx_idx; + gmac_rx_descriptor_t * pxHead = &gs_rx_desc[ ulIndex ]; + +/* #warning Just for debugging */ +/* if((pxHead->addr.val & GMAC_RXD_OWNERSHIP) != 0) */ +/* { */ +/* NVIC_DisableIRQ( GMAC_IRQn ); */ +/* } */ + + #if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) + { + /* Discard any incomplete frames */ + while( ( pxHead->addr.val & GMAC_RXD_OWNERSHIP ) && + ( pxHead->status.val & GMAC_RXD_SOF ) == 0 ) + { + pxHead->addr.val &= ~( GMAC_RXD_OWNERSHIP ); + circ_inc32( &ulIndex, GMAC_RX_BUFFERS ); + pxHead = &gs_rx_desc[ ulIndex ]; + p_gmac_dev->ul_rx_idx = ulIndex; + #if ( GMAC_STATS != 0 ) + { + gmacStats.incompCount++; + } + #endif + } + } + #endif /* ipconfigZERO_COPY_RX_DRIVER == 0 */ + + while( ( pxHead->addr.val & GMAC_RXD_OWNERSHIP ) != 0 ) + { + #if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) + { + if( ( pxHead->status.val & GMAC_RXD_EOF ) != 0 ) + { + /* Here a complete frame has been seen with SOF and EOF */ + ulReturn = pxHead->status.bm.b_len; + break; + } + + circ_inc32( &ulIndex, GMAC_RX_BUFFERS ); + pxHead = &gs_rx_desc[ ulIndex ]; + + if( ( pxHead->addr.val & GMAC_RXD_OWNERSHIP ) == 0 ) + { + /* CPU is not the owner (yet) */ + break; + } + + if( ( pxHead->status.val & GMAC_RXD_SOF ) != 0 ) + { + /* Strange, we found a new Start Of Frame + * discard previous segments */ + int32_t ulPrev = p_gmac_dev->ul_rx_idx; + pxHead = &gs_rx_desc[ ulPrev ]; + + do + { + pxHead->addr.val &= ~( GMAC_RXD_OWNERSHIP ); + circ_inc32( &ulPrev, GMAC_RX_BUFFERS ); + pxHead = &gs_rx_desc[ ulPrev ]; + #if ( GMAC_STATS != 0 ) + { + gmacStats.truncCount++; + } + #endif + } while( ulPrev != ulIndex ); + + p_gmac_dev->ul_rx_idx = ulIndex; + } + } + #else /* ipconfigZERO_COPY_RX_DRIVER */ + { + if( ( pxHead->status.val & ( GMAC_RXD_SOF | GMAC_RXD_EOF ) ) == ( GMAC_RXD_SOF | GMAC_RXD_EOF ) ) + { + /* Here a complete frame in a single segment. */ + ulReturn = pxHead->status.bm.b_len; + break; + } + + /* Return the buffer to DMA. */ + pxHead->addr.bm.b_ownership = 0; + + /* Let ulIndex/pxHead point to the next buffer. */ + circ_inc32( &ulIndex, GMAC_RX_BUFFERS ); + pxHead = &gs_rx_desc[ ulIndex ]; + /* And remember this index. */ + p_gmac_dev->ul_rx_idx = ulIndex; + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + } + + return ulReturn; +} + +/** + * \brief Frames can be read from the GMAC in multiple sections. + * Read ul_frame_size bytes from the GMAC receive buffers to pcTo. + * p_rcv_size is the size of the entire frame. Generally gmac_read + * will be repeatedly called until the sum of all the ul_frame_size equals + * the value of p_rcv_size. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param p_frame Address of the frame buffer. + * \param ul_frame_size Length of the frame. + * \param p_rcv_size Received frame size. + * + * \return GMAC_OK if receiving frame successfully, otherwise failed. + */ +uint32_t gmac_dev_read( gmac_device_t * p_gmac_dev, + uint8_t * p_frame, + uint32_t ul_frame_size, + uint32_t * p_rcv_size, + uint8_t ** pp_recv_frame ) +{ + int32_t nextIdx; /* A copy of the Rx-index 'ul_rx_idx' */ + int32_t bytesLeft = gmac_dev_poll( p_gmac_dev ); + gmac_rx_descriptor_t * pxHead; + + if( bytesLeft == 0 ) + { + return GMAC_RX_NO_DATA; + } + + /* gmac_dev_poll has confirmed that there is a complete frame at + * the current position 'ul_rx_idx' + */ + nextIdx = p_gmac_dev->ul_rx_idx; + + /* Read +2 bytes because buffers are aligned at -2 bytes */ + bytesLeft = min( bytesLeft + 2, ( int32_t ) ul_frame_size ); + + #if ( __DCACHE_PRESENT != 0 ) && defined( CONF_BOARD_ENABLE_CACHE ) + SCB_InvalidateDCache(); + #endif + + #if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) + { + /* The frame will be copied in 1 or 2 memcpy's */ + if( ( p_frame != NULL ) && ( bytesLeft != 0 ) ) + { + const uint8_t * source; + int32_t left; + int32_t toCopy; + + source = gs_uc_rx_buffer + nextIdx * GMAC_RX_UNITSIZE; + left = bytesLeft; + toCopy = ( GMAC_RX_BUFFERS - nextIdx ) * GMAC_RX_UNITSIZE; + + if( toCopy > left ) + { + toCopy = left; + } + + memcpy( p_frame, source, toCopy ); + left -= toCopy; + + if( left != 0ul ) + { + memcpy( p_frame + toCopy, ( void * ) gs_uc_rx_buffer, left ); + } + } + } + #else /* ipconfigZERO_COPY_RX_DRIVER */ + { + if( p_frame != NULL ) + { + /* Return a pointer to the earlier DMA buffer. */ + *( pp_recv_frame ) = ( uint8_t * ) + ( ( ( gs_rx_desc[ nextIdx ].addr.val ) & ~( 0x03ul ) ) + 2 ); + /* Set the new DMA-buffer. */ + gs_rx_desc[ nextIdx ].addr.bm.addr_dw = ( ( uint32_t ) p_frame ) / 4; + } + else + { + /* The driver could not allocate a buffer to receive a packet. + * Leave the current DMA buffer in place. */ + } + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + do + { + pxHead = &gs_rx_desc[ nextIdx ]; + pxHead->addr.val &= ~( GMAC_RXD_OWNERSHIP ); + circ_inc32( &nextIdx, GMAC_RX_BUFFERS ); + } while( ( pxHead->status.val & GMAC_RXD_EOF ) == 0 ); + + p_gmac_dev->ul_rx_idx = nextIdx; + + *p_rcv_size = bytesLeft; + +/* #warning Just for debugging */ +/* NVIC_EnableIRQ( GMAC_IRQn ); */ + + return GMAC_OK; +} + +extern void vGMACGenerateChecksum( uint8_t * apBuffer, + size_t uxLength ); + +/** + * \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the + * GMAC Tx buffers, and then indicates to the GMAC that the buffer is ready. + * If lEndOfFrame is true then the data being copied is the end of the frame + * and the frame can be transmitted. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param p_buffer Pointer to the data buffer. + * \param ul_size Length of the frame. + * + * \return Length sent. + */ +uint32_t gmac_dev_write( gmac_device_t * p_gmac_dev, + void * p_buffer, + uint32_t ul_size ) +{ + volatile gmac_tx_descriptor_t * p_tx_td; + + Gmac * p_hw = p_gmac_dev->p_hw; + + + /* Check parameter */ + if( ul_size > GMAC_TX_UNITSIZE ) + { + return GMAC_PARAM; + } + + /* Pointers to the current transmit descriptor */ + p_tx_td = &gs_tx_desc[ p_gmac_dev->l_tx_head ]; + + /* If no free TxTd, buffer can't be sent, schedule the wakeup callback */ + if( ( p_tx_td->status.val & GMAC_TXD_USED ) == 0 ) + { + return GMAC_TX_BUSY; + } + + /* Set up/copy data to transmission buffer */ + if( p_buffer && ul_size ) + { + /* Driver manages the ring buffer */ + + /* Calculating the checksum here is faster than calculating it from the GMAC buffer + * because within p_buffer, it is well aligned */ + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Zero-copy... */ + p_tx_td->addr = ( uint32_t ) p_buffer; + } + #else + { + /* Or memcopy... */ + memcpy( ( void * ) p_tx_td->addr, p_buffer, ul_size ); + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + vGMACGenerateChecksum( ( uint8_t * ) p_tx_td->addr, ( size_t ) ul_size ); + } + + /*#warning Trying out */ + gmac_start_transmission( p_hw ); + + /* Update transmit descriptor status */ + + /* The buffer size defined is the length of ethernet frame, + * so it's always the last buffer of the frame. */ + if( p_gmac_dev->l_tx_head == ( int32_t ) ( GMAC_TX_BUFFERS - 1 ) ) + { + /* No need to 'and' with GMAC_TXD_LEN_MASK because ul_size has been checked + * GMAC_TXD_USED will now be cleared. */ + p_tx_td->status.val = + ul_size | GMAC_TXD_LAST | GMAC_TXD_WRAP; + } + else + { + /* GMAC_TXD_USED will now be cleared. */ + p_tx_td->status.val = + ul_size | GMAC_TXD_LAST; + } + + circ_inc32( &p_gmac_dev->l_tx_head, GMAC_TX_BUFFERS ); + + /* Now start to transmit if it is still not done */ + gmac_start_transmission( p_hw ); + + return GMAC_OK; +} + +/** + * \brief Get current load of transmit. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * + * \return Current load of transmit. + */ +uint32_t gmac_dev_get_tx_load( gmac_device_t * p_gmac_dev ) +{ + uint16_t us_head = p_gmac_dev->l_tx_head; + uint16_t us_tail = p_gmac_dev->l_tx_tail; + + return CIRC_CNT( us_head, us_tail, GMAC_TX_BUFFERS ); +} + +/** + * \brief Register/Clear TX wakeup callback. + * + * When gmac_dev_write() returns GMAC_TX_BUSY (all transmit descriptor busy), the application + * task calls gmac_dev_set_tx_wakeup_callback() to register func_wakeup() callback and + * enters suspend state. The callback is in charge to resume the task once + * several transmit descriptors have been released. The next time gmac_dev_write() will be called, + * it shall be successful. + * + * This function is usually invoked with NULL callback from the TX wakeup + * callback itself, to unregister. Once the callback has resumed the + * application task, there is no need to invoke the callback again. + * + * \param p_gmac_dev Pointer to GMAC device instance. + * \param func_wakeup Pointer to wakeup callback function. + * \param uc_threshold Number of free transmit descriptor before wakeup callback invoked. + * + * \return GMAC_OK, GMAC_PARAM on parameter error. + */ +#if ( GMAC_USES_WAKEUP_CALLBACK ) + uint8_t gmac_dev_set_tx_wakeup_callback( gmac_device_t * p_gmac_dev, + gmac_dev_wakeup_cb_t func_wakeup_cb, + uint8_t uc_threshold ) + { + if( func_wakeup_cb == NULL ) + { + p_gmac_dev->func_wakeup_cb = NULL; + } + else + { + if( uc_threshold <= GMAC_TX_BUFFERS ) + { + p_gmac_dev->func_wakeup_cb = func_wakeup_cb; + p_gmac_dev->ul_wakeup_threshold = ( uint32_t ) uc_threshold; + } + else + { + return GMAC_PARAM; + } + } + + return GMAC_OK; + } +#endif /* GMAC_USES_WAKEUP_CALLBACK */ + +/** + * \brief Reset TX & RX queue & statistics. + * + * \param p_gmac_dev Pointer to GMAC device instance. + */ +void gmac_dev_reset( gmac_device_t * p_gmac_dev ) +{ + Gmac * p_hw = p_gmac_dev->p_hw; + + gmac_reset_rx_mem( p_gmac_dev ); + gmac_reset_tx_mem( p_gmac_dev ); + gmac_network_control( p_hw, GMAC_NCR_TXEN | GMAC_NCR_RXEN + | GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT ); +} + +void gmac_dev_halt( Gmac * p_gmac ); + +void gmac_dev_halt( Gmac * p_gmac ) +{ + gmac_network_control( p_gmac, GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT ); + gmac_disable_interrupt( p_gmac, ~0u ); +} + + +/** + * \brief GMAC Interrupt handler. + * + * \param p_gmac_dev Pointer to GMAC device instance. + */ + +#if ( GMAC_STATS != 0 ) + extern int logPrintf( const char * pcFormat, + ... ); + + void gmac_show_irq_counts() + { + int index; + + for( index = 0; index < ARRAY_SIZE( intPairs ); index++ ) + { + if( gmacStats.intStatus[ intPairs[ index ].index ] ) + { + logPrintf( "%s : %6u\n", intPairs[ index ].name, gmacStats.intStatus[ intPairs[ index ].index ] ); + } + } + } +#endif /* if ( GMAC_STATS != 0 ) */ + +void gmac_handler( gmac_device_t * p_gmac_dev ) +{ + Gmac * p_hw = p_gmac_dev->p_hw; + + gmac_tx_descriptor_t * p_tx_td; + uint32_t ul_tx_status_flag; + + #if ( GMAC_STATS != 0 ) + int index; + #endif + + uint32_t ul_isr = gmac_get_interrupt_status( p_hw ); + uint32_t ul_rsr = gmac_get_rx_status( p_hw ); + uint32_t ul_tsr = gmac_get_tx_status( p_hw ); + + #if ( GMAC_STATS != 0 ) + { + for( index = 0; index < ARRAY_SIZE( intPairs ); index++ ) + { + if( ul_isr & intPairs[ index ].mask ) + { + gmacStats.intStatus[ intPairs[ index ].index ]++; + } + } + } + #endif /* GMAC_STATS != 0 */ + + /* RX packet */ + if( ( ul_isr & GMAC_ISR_RCOMP ) || ( ul_rsr & ( GMAC_RSR_REC | GMAC_RSR_RXOVR | GMAC_RSR_BNA ) ) ) + { + /* Clear status */ + gmac_clear_rx_status( p_hw, ul_rsr ); + + if( ul_isr & GMAC_ISR_RCOMP ) + { + ul_rsr |= GMAC_RSR_REC; + } + + /* Invoke callbacks which can be useful to wake up a task */ + xRxCallback( ul_rsr ); + } + + /* TX packet */ + if( ( ul_isr & GMAC_ISR_TCOMP ) || ( ul_tsr & ( GMAC_TSR_TXCOMP | GMAC_TSR_COL | GMAC_TSR_RLE | GMAC_TSR_UND ) ) ) + { + ul_tx_status_flag = GMAC_TSR_TXCOMP; + /* A frame transmitted */ + + /* Check RLE */ + if( ul_tsr & GMAC_TSR_RLE ) + { + /* Status RLE & Number of discarded buffers */ + ul_tx_status_flag = GMAC_TSR_RLE | CIRC_CNT( p_gmac_dev->l_tx_head, + p_gmac_dev->l_tx_tail, GMAC_TX_BUFFERS ); + gmac_reset_tx_mem( p_gmac_dev ); + gmac_enable_transmit( p_hw, 1 ); + } + + /* Clear status */ + gmac_clear_tx_status( p_hw, ul_tsr ); + + if( !CIRC_EMPTY( p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail ) ) + { + /* Check the buffers */ + do + { + p_tx_td = &gs_tx_desc[ p_gmac_dev->l_tx_tail ]; + + /* Any error? Exit if buffer has not been sent yet */ + if( ( p_tx_td->status.val & GMAC_TXD_USED ) == 0 ) + { + break; + } + + /* Notify upper layer that a packet has been sent */ + xTxCallback( ul_tx_status_flag, ( void * ) p_tx_td->addr ); /* Function call prvTxCallback */ + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + p_tx_td->addr = 0ul; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + circ_inc32( &p_gmac_dev->l_tx_tail, GMAC_TX_BUFFERS ); + } while( CIRC_CNT( p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail, + GMAC_TX_BUFFERS ) ); + } + + if( ul_tsr & GMAC_TSR_RLE ) + { + /* Notify upper layer RLE */ + xTxCallback( ul_tx_status_flag, NULL ); + } + + #if ( GMAC_USES_WAKEUP_CALLBACK ) + + /* If a wakeup has been scheduled, notify upper layer that it can + * send other packets, and the sending will be successful. */ + if( ( CIRC_SPACE( p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail, + GMAC_TX_BUFFERS ) >= p_gmac_dev->ul_wakeup_threshold ) && + p_gmac_dev->func_wakeup_cb ) + { + p_gmac_dev->func_wakeup_cb(); + } + #endif + } +} + +/*@} */ + +/*/ @cond 0 */ +/**INDENT-OFF**/ +#ifdef __cplusplus + } +#endif +/**INDENT-ON**/ +/*/ @endcond */ diff --git a/FreeRTOS/source/portable/NetworkInterface/DriverSAM/gmac_SAM.h b/FreeRTOS/source/portable/NetworkInterface/DriverSAM/gmac_SAM.h new file mode 100644 index 0000000..212e25e --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/DriverSAM/gmac_SAM.h @@ -0,0 +1,1604 @@ +/** + * \file + * + * \brief GMAC (Ethernet MAC) driver for SAM. + * + * Copyright (c) 2013-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +/* + * Support and FAQ: visit Atmel Support + */ + +#ifndef GMAC_H_INCLUDED + #define GMAC_H_INCLUDED + + #include "compiler.h" + +/*/ @cond 0 */ +/**INDENT-OFF**/ + #ifdef __cplusplus + extern "C" { + #endif +/**INDENT-ON**/ +/*/ @endcond */ + +/** The buffer addresses written into the descriptors must be aligned, so the + * last few bits are zero. These bits have special meaning for the GMAC + * peripheral and cannot be used as part of the address. */ + #define GMAC_RXD_ADDR_MASK 0xFFFFFFFC + #define GMAC_RXD_WRAP ( 1ul << 1 ) /**< Wrap bit */ + #define GMAC_RXD_OWNERSHIP ( 1ul << 0 ) /**< Ownership bit */ + + #define GMAC_RXD_BROADCAST ( 1ul << 31 ) /**< Broadcast detected */ + #define GMAC_RXD_MULTIHASH ( 1ul << 30 ) /**< Multicast hash match */ + #define GMAC_RXD_UNIHASH ( 1ul << 29 ) /**< Unicast hash match */ + #define GMAC_RXD_ADDR_FOUND ( 1ul << 27 ) /**< Specific address match found */ + #define GMAC_RXD_ADDR ( 3ul << 25 ) /**< Address match */ + #define GMAC_RXD_RXCOEN ( 1ul << 24 ) /**< RXCOEN related function */ + #define GMAC_RXD_TYPE ( 3ul << 22 ) /**< Type ID match */ + #define GMAC_RXD_VLAN ( 1ul << 21 ) /**< VLAN tag detected */ + #define GMAC_RXD_PRIORITY ( 1ul << 20 ) /**< Priority tag detected */ + #define GMAC_RXD_PRIORITY_MASK ( 3ul << 17 ) /**< VLAN priority */ + #define GMAC_RXD_CFI ( 1ul << 16 ) /**< Concatenation Format Indicator only if bit 21 is set */ + #define GMAC_RXD_EOF ( 1ul << 15 ) /**< End of frame */ + #define GMAC_RXD_SOF ( 1ul << 14 ) /**< Start of frame */ + #define GMAC_RXD_FCS ( 1ul << 13 ) /**< Frame check sequence */ + #define GMAC_RXD_OFFSET_MASK /**< Receive buffer offset */ + #define GMAC_RXD_LEN_MASK ( 0xFFF ) /**< Length of frame including FCS (if selected) */ + #define GMAC_RXD_LENJUMBO_MASK ( 0x3FFF ) /**< Jumbo frame length */ + + #define GMAC_TXD_USED ( 1ul << 31 ) /**< Frame is transmitted */ + #define GMAC_TXD_WRAP ( 1ul << 30 ) /**< Last descriptor */ + #define GMAC_TXD_ERROR ( 1ul << 29 ) /**< Retry limit exceeded, error */ + #define GMAC_TXD_UNDERRUN ( 1ul << 28 ) /**< Transmit underrun */ + #define GMAC_TXD_EXHAUSTED ( 1ul << 27 ) /**< Buffer exhausted */ + #define GMAC_TXD_LATE ( 1ul << 26 ) /**< Late collision,transmit error */ + #define GMAC_TXD_CHECKSUM_ERROR ( 7ul << 20 ) /**< Checksum error */ + #define GMAC_TXD_NOCRC ( 1ul << 16 ) /**< No CRC */ + #define GMAC_TXD_LAST ( 1ul << 15 ) /**< Last buffer in frame */ + #define GMAC_TXD_LEN_MASK ( 0x1FFF ) /**< Length of buffer */ + +/** The MAC can support frame lengths up to 1536 bytes */ + #define GMAC_FRAME_LENTGH_MAX 1536 + +/*#define GMAC_RX_UNITSIZE 128 / **< Fixed size for RX buffer * / */ + #define GMAC_RX_UNITSIZE 1536 /**< Fixed size for RX buffer */ + +/*#define GMAC_TX_UNITSIZE 1518 / **< Size for ETH frame length * / */ + #define GMAC_TX_UNITSIZE 1536 /**< Size for ETH frame length */ + +/** GMAC clock speed */ + #define GMAC_MCK_SPEED_240MHZ ( 240 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_160MHZ ( 160 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_120MHZ ( 120 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_80MHZ ( 80 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_40MHZ ( 40 * 1000 * 1000 ) + #define GMAC_MCK_SPEED_20MHZ ( 20 * 1000 * 1000 ) + +/** GMAC maintain code default value*/ + #define GMAC_MAN_CODE_VALUE ( 10 ) + +/** GMAC maintain start of frame default value*/ + #define GMAC_MAN_SOF_VALUE ( 1 ) + +/** GMAC maintain read/write*/ + #define GMAC_MAN_RW_TYPE ( 2 ) + +/** GMAC maintain read only*/ + #define GMAC_MAN_READ_ONLY ( 1 ) + +/** GMAC address length */ + #define GMAC_ADDR_LENGTH ( 6 ) + + + #define GMAC_DUPLEX_HALF 0 + #define GMAC_DUPLEX_FULL 1 + + #define GMAC_SPEED_10M 0 + #define GMAC_SPEED_100M 1 + +/** + * \brief Return codes for GMAC APIs. + */ + typedef enum + { + GMAC_OK = 0, /** 0 Operation OK */ + GMAC_TIMEOUT = 1, /** 1 GMAC operation timeout */ + GMAC_TX_BUSY, /** 2 TX in progress */ + GMAC_RX_NO_DATA, /** 3 No data received */ + GMAC_SIZE_TOO_SMALL, /** 4 Buffer size not enough */ + GMAC_PARAM, /** 5 Parameter error, TX packet invalid or RX size too small */ + GMAC_RX_ERROR, /** 6 RX error */ + GMAC_INVALID = 0xFF, /* Invalid */ + } gmac_status_t; + +/** + * \brief Media Independent Interface (MII) type. + */ + typedef enum + { + GMAC_PHY_MII = 0, /** MII mode */ + GMAC_PHY_RMII = 1, /** Reduced MII mode */ + GMAC_PHY_INVALID = 0xFF, /* Invalid mode*/ + } gmac_mii_mode_t; + +/* This is the list of GMAC priority queue */ + typedef enum + { + GMAC_QUE_0 = 0, + #if !( SAM4E ) + GMAC_QUE_1 = 1, + GMAC_QUE_2 = 2, + /* Only SAM E70 Rev-B. */ + GMAC_QUE_3 = 3, + GMAC_QUE_4 = 4, + GMAC_QUE_5 = 5, + #endif + #if !defined( __DOXYGEN__ ) + GMAC_QUE_N, + #endif + } gmac_quelist_t; + +/** Receive buffer descriptor struct */ + COMPILER_PACK_SET( 8 ) + typedef struct gmac_rx_descriptor + { + union gmac_rx_addr + { + uint32_t val; + struct gmac_rx_addr_bm + { + uint32_t b_ownership : 1, /**< User clear, GMAC sets this to 1 once it has successfully written a frame to memory */ + b_wrap : 1, /**< Marks last descriptor in receive buffer */ + addr_dw : 30; /**< Address in number of DW */ + } bm; + } addr; /**< Address, Wrap & Ownership */ + union gmac_rx_status + { + uint32_t val; + struct gmac_rx_status_bm + { + uint32_t b_len : 13, /** 0..12 Length of frame including FCS */ + b_fcs : 1, /** 13 Receive buffer offset, bits 13:12 of frame length for jumbo frame */ + b_sof : 1, /** 14 Start of frame */ + b_eof : 1, /** 15 End of frame */ + b_cfi : 1, /** 16 Concatenation Format Indicator */ + b_vlan_priority : 3, /** 17..19 VLAN priority (if VLAN detected) */ + b_priority_detected : 1, /** 20 Priority tag detected */ + b_vlan_detected : 1, /** 21 VLAN tag detected */ + b_type_id_match : 2, /** 22..23 Type ID match */ + b_checksumoffload : 1, /** 24 Checksum offload specific function */ + b_addrmatch : 2, /** 25..26 Address register match */ + b_ext_addr_match : 1, /** 27 External address match found */ + reserved : 1, /** 28 */ + b_uni_hash_match : 1, /** 29 Unicast hash match */ + b_multi_hash_match : 1, /** 30 Multicast hash match */ + b_boardcast_detect : 1; /** 31 Global broadcast address detected */ + } bm; + } status; + } gmac_rx_descriptor_t; + +/** Transmit buffer descriptor struct */ + COMPILER_PACK_SET( 8 ) + typedef struct gmac_tx_descriptor + { + uint32_t addr; + union gmac_tx_status + { + uint32_t val; + struct gmac_tx_status_bm + { + uint32_t b_len : 14, /** 0..13 Length of buffer */ + reserved : 1, /** 14 */ + b_last_buffer : 1, /** 15 Last buffer (in the current frame) */ + b_no_crc : 1, /** 16 No CRC */ + reserved1 : 3, /** 17..19 */ + b_checksumoffload : 3, /** 20..22 Transmit checksum generation offload errors */ + reserved2 : 3, /** 23..25 */ + b_lco : 1, /** 26 Late collision, transmit error detected */ + b_exhausted : 1, /** 27 Buffer exhausted in mid frame */ + b_underrun : 1, /** 28 Transmit underrun */ + b_error : 1, /** 29 Retry limit exceeded, error detected */ + b_wrap : 1, /** 30 Marks last descriptor in TD list */ + b_used : 1; /** 31 User clear, GMAC sets this to 1 once a frame has been successfully transmitted */ + } bm; + } status; + } gmac_tx_descriptor_t; + + COMPILER_PACK_RESET() + +/** + * \brief Input parameters when initializing the gmac module mode. + */ + typedef struct gmac_options + { + /* Enable/Disable CopyAllFrame */ + uint8_t uc_copy_all_frame; + /* Enable/Disable NoBroadCast */ + uint8_t uc_no_boardcast; + /* MAC address */ + uint8_t uc_mac_addr[ GMAC_ADDR_LENGTH ]; + } gmac_options_t; + +/** Wakeup callback */ + typedef void (* gmac_dev_wakeup_cb_t) ( void ); + +/** + * GMAC driver structure. + */ + typedef struct gmac_device + { + /** Pointer to HW register base */ + Gmac * p_hw; + + /** + * Pointer to allocated TX buffer. + * Section 3.6 of AMBA 2.0 spec states that burst should not cross + * 1K Boundaries. + * Receive buffer manager writes are burst of 2 words => 3 lsb bits + * of the address shall be set to 0. + */ + #if ( GMAC_USES_WAKEUP_CALLBACK != 0 ) + /** Optional callback to be invoked once several TDs have been released */ + gmac_dev_wakeup_cb_t func_wakeup_cb; + #endif + /** RX index for current processing TD */ + uint32_t ul_rx_idx; + /** Circular buffer head pointer by upper layer (buffer to be sent) */ + int32_t l_tx_head; + /** Circular buffer tail pointer incremented by handlers (buffer sent) */ + int32_t l_tx_tail; + + /** Number of free TD before wakeup callback is invoked */ + uint32_t ul_wakeup_threshold; + } gmac_device_t; + + uint8_t gmac_wait_phy( Gmac * p_gmac, + const uint32_t ul_retry ); + +/** + * \brief Write network control value. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_ncr Network control value. + */ + static inline void gmac_network_control( Gmac * p_gmac, + uint32_t ul_ncr ) + { + p_gmac->GMAC_NCR = ul_ncr; + } + +/** + * \brief Get network control value. + * + * \param p_gmac Pointer to the GMAC instance. + */ + + static inline uint32_t gmac_get_network_control( Gmac * p_gmac ) + { + return p_gmac->GMAC_NCR; + } + +/** + * \brief Enable/Disable GMAC receive. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC receiver, else to enable it. + */ + static inline void gmac_enable_receive( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_RXEN; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_RXEN; + } + } + +/** + * \brief Enable/Disable GMAC transmit. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC transmit, else to enable it. + */ + static inline void gmac_enable_transmit( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXEN; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_TXEN; + } + } + +/** + * \brief Enable/Disable GMAC management. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC management, else to enable it. + */ + static inline void gmac_enable_management( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_MPE; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_MPE; + } + } + +/** + * \brief Clear all statistics registers. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_clear_statistics( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_CLRSTAT; + } + +/** + * \brief Increase all statistics registers. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_increase_statistics( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_INCSTAT; + } + +/** + * \brief Enable/Disable statistics registers writing. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the statistics registers writing, else to enable it. + */ + static inline void gmac_enable_statistics_write( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_WESTAT; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_WESTAT; + } + } + +/** + * \brief In half-duplex mode, forces collisions on all received frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the back pressure, else to enable it. + */ + static inline void gmac_enable_back_pressure( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_BP; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_BP; + } + } + +/** + * \brief Start transmission. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_start_transmission( Gmac * p_gmac ) + { + __DSB(); + p_gmac->GMAC_NCR |= GMAC_NCR_TSTART; + } + +/** + * \brief Halt transmission. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_halt_transmission( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_THALT; + } + +/** + * \brief Transmit pause frame. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_tx_pause_frame( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXPF; + } + +/** + * \brief Transmit zero quantum pause frame. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_tx_pause_zero_quantum_frame( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXZQPF; + } + +/** + * \brief Store receivetime stamp to memory. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to normal operation, else to enable the store. + */ + static inline void gmac_store_rx_time_stamp( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_SRTSM; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_SRTSM; + } + } + +/** + * \brief Enable PFC priority-based pause reception. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 1 to set the reception, 0 to disable. + */ + static inline void gmac_enable_pfc_pause_frame( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_ENPBPR; + } + else + { + p_gmac->GMAC_NCR &= ~GMAC_NCR_ENPBPR; + } + } + +/** + * \brief Transmit PFC priority-based pause reception. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_transmit_pfc_pause_frame( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_TXPBPF; + } + +/** + * \brief Flush next packet. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_flush_next_packet( Gmac * p_gmac ) + { + p_gmac->GMAC_NCR |= GMAC_NCR_FNP; + } + +/** + * \brief Set up network configuration register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_cfg Network configuration value. + */ + static inline void gmac_set_config( Gmac * p_gmac, + uint32_t ul_cfg ) + { + p_gmac->GMAC_NCFGR = ul_cfg; + } + +/* Get and set DMA Configuration Register */ + static inline void gmac_set_dma( Gmac * p_gmac, + uint32_t ul_cfg ) + { + p_gmac->GMAC_DCFGR = ul_cfg; + } + + static inline uint32_t gmac_get_dma( Gmac * p_gmac ) + { + return p_gmac->GMAC_DCFGR; + } + +/** + * \brief Get network configuration. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Network configuration. + */ + static inline uint32_t gmac_get_config( Gmac * p_gmac ) + { + return p_gmac->GMAC_NCFGR; + } + +/** + * \brief Set speed. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_speed 1 to indicate 100Mbps, 0 to 10Mbps. + */ + static inline void gmac_set_speed( Gmac * p_gmac, + uint8_t uc_speed ) + { + if( uc_speed ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_SPD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_SPD; + } + } + +/** + * \brief Enable/Disable Full-Duplex mode. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the Full-Duplex mode, else to enable it. + */ + static inline void gmac_enable_full_duplex( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_FD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_FD; + } + } + +/** + * \brief Enable/Disable Copy(Receive) All Valid Frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable copying all valid frames, else to enable it. + */ + static inline void gmac_enable_copy_all( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_CAF; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_CAF; + } + } + +/** + * \brief Enable/Disable jumbo frames (up to 10240 bytes). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the jumbo frames, else to enable it. + */ + static inline void gmac_enable_jumbo_frames( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_JFRAME; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_JFRAME; + } + } + +/** + * \brief Disable/Enable broadcast receiving. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 1 to disable the broadcast, else to enable it. + */ + static inline void gmac_disable_broadcast( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_NBC; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_NBC; + } + } + +/** + * \brief Enable/Disable multicast hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the multicast hash, else to enable it. + */ + static inline void gmac_enable_multicast_hash( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_UNIHEN; + } + } + +/** + * \brief Enable/Disable big frames (over 1518, up to 1536). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable big frames else to enable it. + */ + static inline void gmac_enable_big_frame( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_MAXFS; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_MAXFS; + } + } + +/** + * \brief Set MDC clock divider. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_mck GMAC MCK. + * + * \return GMAC_OK if successfully. + */ + static inline uint8_t gmac_set_mdc_clock( Gmac * p_gmac, + uint32_t ul_mck ) + { + uint32_t ul_clk, ul_value; + + if( ul_mck > GMAC_MCK_SPEED_240MHZ ) + { + return GMAC_INVALID; + } + else if( ul_mck > GMAC_MCK_SPEED_160MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_96; + } + else if( ul_mck > GMAC_MCK_SPEED_120MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_64; + } + else if( ul_mck > GMAC_MCK_SPEED_80MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_48; + } + else if( ul_mck > GMAC_MCK_SPEED_40MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_32; + } + else if( ul_mck > GMAC_MCK_SPEED_20MHZ ) + { + ul_clk = GMAC_NCFGR_CLK_MCK_16; + } + else + { + ul_clk = GMAC_NCFGR_CLK_MCK_8; + } + + ul_value = p_gmac->GMAC_NCFGR; + ul_value &= ~GMAC_NCFGR_CLK_Msk; + ul_value |= ul_clk; + p_gmac->GMAC_NCFGR = ul_value; + return GMAC_OK; + } + +/** + * \brief Enable/Disable retry test. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the GMAC receiver, else to enable it. + */ + static inline void gmac_enable_retry_test( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RTY; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RTY; + } + } + +/** + * \brief Enable/Disable pause (when a valid pause frame is received). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable pause frame, else to enable it. + */ + static inline void gmac_enable_pause_frame( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_PEN; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_PEN; + } + } + +/** + * \brief Set receive buffer offset to 0 ~ 3. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline void gmac_set_rx_buffer_offset( Gmac * p_gmac, + uint8_t uc_offset ) + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RXBUFO_Msk; + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RXBUFO( uc_offset ); + } + +/** + * \brief Enable/Disable receive length field checking. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable receive length field checking, else to enable it. + */ + static inline void gmac_enable_rx_length_check( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_LFERD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_LFERD; + } + } + +/** + * \brief Enable/Disable discarding FCS field of received frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable discarding FCS field of received frames, else to enable it. + */ + static inline void gmac_enable_discard_fcs( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RFCS; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RFCS; + } + } + + +/** + * \brief Enable/Disable frames to be received in half-duplex mode + * while transmitting. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the received in half-duplex mode, else to enable it. + */ + static inline void gmac_enable_efrhd( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_EFRHD; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_EFRHD; + } + } + +/** + * \brief Enable/Disable ignore RX FCS. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable ignore RX FCS, else to enable it. + */ + static inline void gmac_enable_ignore_rx_fcs( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_IRXFCS; + } + else + { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_IRXFCS; + } + } + +/** + * \brief Get Network Status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Network status. + */ + static inline uint32_t gmac_get_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_NSR; + } + +/** + * \brief Get MDIO IN pin status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return MDIO IN pin status. + */ + static inline uint8_t gmac_get_MDIO( Gmac * p_gmac ) + { + return( ( p_gmac->GMAC_NSR & GMAC_NSR_MDIO ) > 0 ); + } + +/** + * \brief Check if PHY is idle. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return 1 if PHY is idle. + */ + static inline uint8_t gmac_is_phy_idle( Gmac * p_gmac ) + { + return( ( p_gmac->GMAC_NSR & GMAC_NSR_IDLE ) > 0 ); + } + +/** + * \brief Return transmit status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Transmit status. + */ + static inline uint32_t gmac_get_tx_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_TSR; + } + +/** + * \brief Clear transmit status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_status Transmit status. + */ + static inline void gmac_clear_tx_status( Gmac * p_gmac, + uint32_t ul_status ) + { + p_gmac->GMAC_TSR = ul_status; + } + +/** + * \brief Return receive status. + * + * \param p_gmac Pointer to the GMAC instance. + */ + static inline uint32_t gmac_get_rx_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_RSR; + } + +/** + * \brief Clear receive status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_status Receive status. + */ + static inline void gmac_clear_rx_status( Gmac * p_gmac, + uint32_t ul_status ) + { + p_gmac->GMAC_RSR = ul_status; + } + +/** + * \brief Set Rx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Rx queue address. + */ + static inline void gmac_set_rx_queue( Gmac * p_gmac, + uint32_t ul_addr ) + { + p_gmac->GMAC_RBQB = GMAC_RBQB_ADDR_Msk & ul_addr; + } + +/** + * \brief Set Rx buffer size. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Rx buffer. + */ + static inline void gmac_set_rx_bufsize( Gmac * p_gmac, + uint32_t ul_code ) + { + p_gmac->GMAC_DCFGR = ( p_gmac->GMAC_DCFGR & ~GMAC_DCFGR_DRBS_Msk ) + | GMAC_DCFGR_DRBS( ul_code ); + } + +/** + * \brief Get Rx Queue Address. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Rx queue address. + */ + static inline uint32_t gmac_get_rx_queue( Gmac * p_gmac ) + { + return p_gmac->GMAC_RBQB; + } + +/** + * \brief Set Tx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Tx queue address. + */ + static inline void gmac_set_tx_queue( Gmac * p_gmac, + uint32_t ul_addr ) + { + p_gmac->GMAC_TBQB = GMAC_TBQB_ADDR_Msk & ul_addr; + } + +/** + * \brief Get Tx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Rx queue address. + */ + static inline uint32_t gmac_get_tx_queue( Gmac * p_gmac ) + { + return p_gmac->GMAC_TBQB; + } + +/** + * \brief Enable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be enabled. + */ + static inline void gmac_enable_interrupt( Gmac * p_gmac, + uint32_t ul_source ) + { + p_gmac->GMAC_IER = ul_source; + } + +/** + * \brief Disable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be disabled. + */ + static inline void gmac_disable_interrupt( Gmac * p_gmac, + uint32_t ul_source ) + { + p_gmac->GMAC_IDR = ul_source; + } + +/** + * \brief Return interrupt status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Interrupt status. + */ + static inline uint32_t gmac_get_interrupt_status( Gmac * p_gmac ) + { + return p_gmac->GMAC_ISR; + } + +/** + * \brief Return interrupt mask. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Interrupt mask. + */ + static inline uint32_t gmac_get_interrupt_mask( Gmac * p_gmac ) + { + return p_gmac->GMAC_IMR; + } + +/** + * \brief Execute PHY maintenance command. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * \param uc_reg_addr Register address. + * \param uc_rw 1 to Read, 0 to write. + * \param us_data Data to be performed, write only. + */ + static inline void gmac_maintain_phy( Gmac * p_gmac, + uint8_t uc_phy_addr, + uint8_t uc_reg_addr, + uint8_t uc_rw, + uint16_t us_data ) + { + /* Wait until bus idle */ + while( ( p_gmac->GMAC_NSR & GMAC_NSR_IDLE ) == 0 ) + { + } + + /* Write maintain register */ + p_gmac->GMAC_MAN = GMAC_MAN_WTN( GMAC_MAN_CODE_VALUE ) + | GMAC_MAN_CLTTO + | GMAC_MAN_PHYA( uc_phy_addr ) + | GMAC_MAN_REGA( uc_reg_addr ) + | GMAC_MAN_OP( ( uc_rw ? GMAC_MAN_RW_TYPE : GMAC_MAN_READ_ONLY ) ) + | GMAC_MAN_DATA( us_data ); + } + +/** + * \brief Get PHY maintenance data returned. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Get PHY data. + */ + static inline uint16_t gmac_get_phy_data( Gmac * p_gmac ) + { + /* Wait until bus idle */ + while( ( p_gmac->GMAC_NSR & GMAC_NSR_IDLE ) == 0 ) + { + } + + /* Return data */ + return ( uint16_t ) ( p_gmac->GMAC_MAN & GMAC_MAN_DATA_Msk ); + } + +/** + * \brief Set Hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_hash_top Hash top. + * \param ul_hash_bottom Hash bottom. + */ + static inline void gmac_set_hash( Gmac * p_gmac, + uint32_t ul_hash_top, + uint32_t ul_hash_bottom ) + { + p_gmac->GMAC_HRB = ul_hash_bottom; + p_gmac->GMAC_HRT = ul_hash_top; + } + +/** + * \brief Set 64 bits Hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ull_hash 64 bits hash value. + */ + static inline void gmac_set_hash64( Gmac * p_gmac, + uint64_t ull_hash ) + { + p_gmac->GMAC_HRB = ( uint32_t ) ull_hash; + p_gmac->GMAC_HRT = ( uint32_t ) ( ull_hash >> 32 ); + } + +/** + * \brief Set MAC Address. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param p_mac_addr GMAC address. + */ + static inline void gmac_set_address( Gmac * p_gmac, + uint8_t uc_index, + const uint8_t * p_mac_addr ) + { + p_gmac->GMAC_SA[ uc_index ].GMAC_SAB = ( p_mac_addr[ 3 ] << 24 ) + | ( p_mac_addr[ 2 ] << 16 ) + | ( p_mac_addr[ 1 ] << 8 ) + | ( p_mac_addr[ 0 ] ); + p_gmac->GMAC_SA[ uc_index ].GMAC_SAT = ( p_mac_addr[ 5 ] << 8 ) + | ( p_mac_addr[ 4 ] ); + } + +/** + * \brief Set MAC Address via 2 dword. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param ul_mac_top GMAC top address. + * \param ul_mac_bottom GMAC bottom address. + */ + static inline void gmac_set_address32( Gmac * p_gmac, + uint8_t uc_index, + uint32_t ul_mac_top, + uint32_t ul_mac_bottom ) + { + p_gmac->GMAC_SA[ uc_index ].GMAC_SAB = ul_mac_bottom; + p_gmac->GMAC_SA[ uc_index ].GMAC_SAT = ul_mac_top; + } + +/** + * \brief Set MAC Address via int64. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param ull_mac 64-bit GMAC address. + */ + static inline void gmac_set_address64( Gmac * p_gmac, + uint8_t uc_index, + uint64_t ull_mac ) + { + p_gmac->GMAC_SA[ uc_index ].GMAC_SAB = ( uint32_t ) ull_mac; + p_gmac->GMAC_SA[ uc_index ].GMAC_SAT = ( uint32_t ) ( ull_mac >> 32 ); + } + +/** + * \brief Select media independent interface mode. + * + * \param p_gmac Pointer to the GMAC instance. + * \param mode Media independent interface mode. + */ + #if ( SAM4E ) + static inline void gmac_select_mii_mode( Gmac * p_gmac, + gmac_mii_mode_t mode ) + { + switch( mode ) + { + case GMAC_PHY_MII: + case GMAC_PHY_RMII: + p_gmac->GMAC_UR |= GMAC_UR_RMIIMII; + break; + + default: + p_gmac->GMAC_UR &= ~GMAC_UR_RMIIMII; + break; + } + } + #else /* if ( SAM4E ) */ + static inline void gmac_select_mii_mode( Gmac * p_gmac, + gmac_mii_mode_t mode ) + { + switch( mode ) + { + case GMAC_PHY_MII: + p_gmac->GMAC_UR |= GMAC_UR_RMII; + break; + + case GMAC_PHY_RMII: + default: + p_gmac->GMAC_UR &= ~GMAC_UR_RMII; + break; + } + } + #endif /* if ( SAM4E ) */ + + #if !( SAM4E ) + +/** + * \brief Set 1588 timer comparison. + * + * \param p_gmac Pointer to the GMAC instance. + * \param seconds47 Second comparison high + * \param seconds31 Second comparison low + * \param nanosec Nanosecond Comparison + */ + static inline void gmac_set_tsu_compare( Gmac * p_gmac, + uint32_t seconds47, + uint32_t seconds31, + uint32_t nanosec ) + { + p_gmac->GMAC_SCH = seconds47; + p_gmac->GMAC_SCL = seconds31; + p_gmac->GMAC_NSC = nanosec; + } + +/** + * \brief Get interrupt status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + * + * \return Interrupt status. + */ + static inline uint32_t gmac_get_priority_interrupt_status( Gmac * p_gmac, + gmac_quelist_t queue_idx ) + { + return p_gmac->GMAC_ISRPQ[ queue_idx - 1 ]; + } + +/** + * \brief Set base address of TX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + */ + static inline void gmac_set_tx_priority_queue( Gmac * p_gmac, + uint32_t ul_addr, + gmac_quelist_t queue_idx ) + { + p_gmac->GMAC_TBQBAPQ[ queue_idx - 1 ] = GMAC_TBQB_ADDR_Msk & ul_addr; + } + +/** + * \brief Get base address of TX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + * + * \return Base address. + */ + static inline uint32_t gmac_get_tx_priority_queue( Gmac * p_gmac, + gmac_quelist_t queue_idx ) + { + return p_gmac->GMAC_TBQBAPQ[ queue_idx - 1 ]; + } + +/** + * \brief Set base address of RX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + */ + static inline void gmac_set_rx_priority_queue( Gmac * p_gmac, + uint32_t ul_addr, + gmac_quelist_t queue_idx ) + { + p_gmac->GMAC_RBQBAPQ[ queue_idx - 1 ] = GMAC_RBQB_ADDR_Msk & ul_addr; + } + +/** + * \brief Get base address of RX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + * + * \return Base address. + */ + static inline uint32_t gmac_get_rx_priority_queue( Gmac * p_gmac, + gmac_quelist_t queue_idx ) + { + return p_gmac->GMAC_RBQBAPQ[ queue_idx - 1 ]; + } + +/** + * \brief Set size of RX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + */ + static inline void gmac_set_rx_priority_bufsize( Gmac * p_gmac, + uint32_t ul_size, + gmac_quelist_t queue_idx ) + { + p_gmac->GMAC_RBSRPQ[ queue_idx - 1 ] = ul_size; + } + +/** + * \brief Enable or disable credit-based shaping on the second highest priority queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable, 1 to enable it + */ + static inline void gmac_enable_cbsque_a( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_CBSCR |= GMAC_CBSCR_QAE; + } + else + { + p_gmac->GMAC_CBSCR &= ~GMAC_CBSCR_QAE; + } + } + +/** + * \brief Enable or disable credit-based shaping on the highest priority queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable, 1 to enable it + */ + static inline void gmac_enable_cbsque_b( Gmac * p_gmac, + uint8_t uc_enable ) + { + if( uc_enable ) + { + p_gmac->GMAC_CBSCR |= GMAC_CBSCR_QBE; + } + else + { + p_gmac->GMAC_CBSCR &= ~GMAC_CBSCR_QBE; + } + } + +/** + * \brief Set credit-based shaping on the highest priority queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param idleslope_a Value for queue A in bytes/second + */ + static inline void gmac_config_idleslope_a( Gmac * p_gmac, + uint32_t idleslope_a ) + { + p_gmac->GMAC_CBSISQA = idleslope_a; + } + +/** + * \brief Set credit-based shaping on the highest priority queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param idleslope_b Value for queue B in bytes/second + */ + static inline void gmac_config_idleslope_b( Gmac * p_gmac, + uint32_t idleslope_b ) + { + p_gmac->GMAC_CBSISQB = idleslope_b; + } + +/** + * \brief Set screening type 1 register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param reg_val Value for screening type 1 + * \param index Index of register + */ + static inline void gmac_write_screener_reg_1( Gmac * p_gmac, + uint32_t reg_val, + uint32_t index ) + { + p_gmac->GMAC_ST1RPQ[ index ] = reg_val; + } + +/** + * \brief Set screening type 2 register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param reg_val Value for screening type 2 + * \param index Index of register + */ + static inline void gmac_write_screener_reg_2( Gmac * p_gmac, + uint32_t reg_val, + uint32_t index ) + { + p_gmac->GMAC_ST2RPQ[ index ] = reg_val; + } + +/** + * \brief Enable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be enabled. + * \param queue_idx Index of queue, start from 1 + */ + static inline void gmac_enable_priority_interrupt( Gmac * p_gmac, + uint32_t ul_source, + gmac_quelist_t queue_idx ) + { + p_gmac->GMAC_IERPQ[ queue_idx - 1 ] = ul_source; + } + +/** + * \brief Disable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be disabled. + * \param queue_idx Index of queue, start from 1 + */ + static inline void gmac_disable_priority_interrupt( Gmac * p_gmac, + uint32_t ul_source, + gmac_quelist_t queue_idx ) + { + p_gmac->GMAC_IDRPQ[ queue_idx - 1 ] = ul_source; + } + +/** + * \brief Get interrupt mask. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + * + * \return Interrupt mask. + */ + static inline uint32_t gmac_get_priority_interrupt_mask( Gmac * p_gmac, + gmac_quelist_t queue_idx ) + { + return p_gmac->GMAC_IMRPQ[ queue_idx - 1 ]; + } + +/** + * \brief Set screening type 2 eherType register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ethertype Ethertype compare value + * \param index Index of register + */ + static inline void gmac_write_ethtype_reg( Gmac * p_gmac, + uint16_t ethertype, + uint32_t index ) + { + p_gmac->GMAC_ST2ER[ index ] = ( uint32_t ) ethertype; + } + +/** + * \brief Set screening type 2 compare word register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param c0reg Compare value 0 + * \param c1reg Compare value 1 + * \param index Index of register + */ + static inline void gmac_write_screen_compare_reg( Gmac * p_gmac, + uint32_t c0reg, + uint16_t c1reg, + uint32_t index ) + { + volatile uint32_t * p_PRAS; + uint32_t ul_dlt; + + ul_dlt = ( uint32_t ) &( p_gmac->GMAC_ST2CW01 ); + ul_dlt = ul_dlt - ( uint32_t ) &( p_gmac->GMAC_ST2CW00 ); + + p_PRAS = ( volatile uint32_t * ) ( ( uint32_t ) &( p_gmac->GMAC_ST2CW00 ) + + index * ul_dlt ); + *p_PRAS = c0reg; + p_PRAS = ( volatile uint32_t * ) ( ( uint32_t ) &( p_gmac->GMAC_ST2CW10 ) + + index * ul_dlt ); + *p_PRAS = ( uint32_t ) c1reg; + } + + #endif /* !(SAM4E) */ + + uint8_t gmac_phy_read( Gmac * p_gmac, + uint8_t uc_phy_address, + uint8_t uc_address, + uint32_t * p_value ); + uint8_t gmac_phy_write( Gmac * p_gmac, + uint8_t uc_phy_address, + uint8_t uc_address, + uint32_t ul_value ); + void gmac_dev_init( Gmac * p_gmac, + gmac_device_t * p_gmac_dev, + gmac_options_t * p_opt ); + uint32_t gmac_dev_read( gmac_device_t * p_gmac_dev, + uint8_t * p_frame, + uint32_t ul_frame_size, + uint32_t * p_rcv_size, + uint8_t ** pp_recv_frame ); + uint32_t gmac_dev_write( gmac_device_t * p_gmac_dev, + void * p_buffer, + uint32_t ul_size ); + uint32_t gmac_dev_get_tx_load( gmac_device_t * p_gmac_dev ); + uint8_t gmac_dev_set_tx_wakeup_callback( gmac_device_t * p_gmac_dev, + gmac_dev_wakeup_cb_t func_wakeup, + uint8_t uc_threshold ); + void gmac_dev_reset( gmac_device_t * p_gmac_dev ); + void gmac_handler( gmac_device_t * p_gmac_dev ); + + void gmac_reset_tx_mem( gmac_device_t * p_dev ); + +/*/ @cond 0 */ +/**INDENT-OFF**/ + #ifdef __cplusplus + } + #endif +/**INDENT-ON**/ +/*/ @endcond */ + + + #define GMAC_STATS 0 + + #if ( GMAC_STATS != 0 ) + +/* Here below some code to study the types and + * frequencies of GMAC interrupts. */ + #define GMAC_IDX_RXUBR 0 + #define GMAC_IDX_TUR 1 + #define GMAC_IDX_RLEX 2 + #define GMAC_IDX_TFC 3 + #define GMAC_IDX_RCOMP 4 + #define GMAC_IDX_TCOMP 5 + #define GMAC_IDX_ROVR 6 + #define GMAC_IDX_HRESP 7 + #define GMAC_IDX_PFNZ 8 + #define GMAC_IDX_PTZ 9 + + struct SGmacStats + { + unsigned recvCount; + unsigned rovrCount; + unsigned bnaCount; + unsigned sendCount; + unsigned sovrCount; + unsigned incompCount; + unsigned truncCount; + + unsigned intStatus[ 10 ]; + }; + extern struct SGmacStats gmacStats; + + struct SIntPair + { + const char * name; + unsigned mask; + int index; + }; + + #define MK_PAIR( NAME ) # NAME, GMAC_IER_ ## NAME, GMAC_IDX_ ## NAME + static const struct SIntPair intPairs[] = { + { MK_PAIR( RXUBR ) }, /* Enable receive used bit read interrupt. */ + { MK_PAIR( TUR ) }, /* Enable transmit underrun interrupt. */ + { MK_PAIR( RLEX ) }, /* Enable retry limit exceeded interrupt. */ + { MK_PAIR( TFC ) }, /* Enable transmit buffers exhausted in mid-frame interrupt. */ + { MK_PAIR( RCOMP ) }, /* Receive complete */ + { MK_PAIR( TCOMP ) }, /* Enable transmit complete interrupt. */ + { MK_PAIR( ROVR ) }, /* Enable receive overrun interrupt. */ + { MK_PAIR( HRESP ) }, /* Enable Hresp not OK interrupt. */ + { MK_PAIR( PFNZ ) }, /* Enable pause frame received interrupt. */ + { MK_PAIR( PTZ ) } /* Enable pause time zero interrupt. */ + }; + + void gmac_show_irq_counts(); + + #endif /* if ( GMAC_STATS != 0 ) */ + +#endif /* GMAC_H_INCLUDED */ diff --git a/FreeRTOS/source/portable/NetworkInterface/LPC17xx/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/LPC17xx/NetworkInterface.c new file mode 100644 index 0000000..ccc8efd --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/LPC17xx/NetworkInterface.c @@ -0,0 +1,270 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* Standard includes. */ +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* Hardware abstraction. */ +#include "FreeRTOS_IO.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_Sockets.h" +#include "NetworkBufferManagement.h" + +/* Driver includes. */ +#include "lpc17xx_emac.h" +#include "lpc17xx_pinsel.h" + +/* Demo includes. */ +#include "NetworkInterface.h" + +#if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES != 1 + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +/* When a packet is ready to be sent, if it cannot be sent immediately then the + * task performing the transmit will block for niTX_BUFFER_FREE_WAIT + * milliseconds. It will do this a maximum of niMAX_TX_ATTEMPTS before giving + * up. */ +#define niTX_BUFFER_FREE_WAIT ( pdMS_TO_TICKS( 2UL ) ) +#define niMAX_TX_ATTEMPTS ( 5 ) + +/* The length of the queue used to send interrupt status words from the + * interrupt handler to the deferred handler task. */ +#define niINTERRUPT_QUEUE_LENGTH ( 10 ) + +/*-----------------------------------------------------------*/ + +/* + * A deferred interrupt handler task that processes + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/*-----------------------------------------------------------*/ + +/* The queue used to communicate Ethernet events with the IP task. */ +extern QueueHandle_t xNetworkEventQueue; + +/* The semaphore used to wake the deferred interrupt handler task when an Rx + * interrupt is received. */ +static SemaphoreHandle_t xEMACRxEventSemaphore = NULL; +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + EMAC_CFG_Type Emac_Config; + PINSEL_CFG_Type xPinConfig; + BaseType_t xStatus, xReturn; + + /* Enable Ethernet Pins */ + boardCONFIGURE_ENET_PINS( xPinConfig ); + + Emac_Config.Mode = EMAC_MODE_AUTO; + Emac_Config.pbEMAC_Addr = ipLOCAL_MAC_ADDRESS; + xStatus = EMAC_Init( &Emac_Config ); + + LPC_EMAC->IntEnable &= ~( EMAC_INT_TX_DONE ); + + if( xStatus != ERROR ) + { + vSemaphoreCreateBinary( xEMACRxEventSemaphore ); + configASSERT( xEMACRxEventSemaphore != NULL ); + + /* The handler task is created at the highest possible priority to + * ensure the interrupt handler can return directly to it. */ + xTaskCreate( prvEMACHandlerTask, "EMAC", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL ); + + /* Enable the interrupt and set its priority to the minimum + * interrupt priority. */ + NVIC_SetPriority( ENET_IRQn, configMAC_INTERRUPT_PRIORITY ); + NVIC_EnableIRQ( ENET_IRQn ); + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + configASSERT( xStatus != ERROR ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ + BaseType_t xReturn = pdFAIL; + int32_t x; + extern void EMAC_StartTransmitNextBuffer( uint32_t ulLength ); + extern void EMAC_SetNextPacketToSend( uint8_t * pucBuffer ); + + + /* Attempt to obtain access to a Tx buffer. */ + for( x = 0; x < niMAX_TX_ATTEMPTS; x++ ) + { + if( EMAC_CheckTransmitIndex() == TRUE ) + { + /* Will the data fit in the Tx buffer? */ + if( pxNetworkBuffer->xDataLength < EMAC_ETH_MAX_FLEN ) /*_RB_ The size needs to come from FreeRTOSIPConfig.h. */ + { + /* Assign the buffer to the Tx descriptor that is now known to + * be free. */ + EMAC_SetNextPacketToSend( pxNetworkBuffer->pucBuffer ); + + /* The EMAC now owns the buffer. */ + pxNetworkBuffer->pucBuffer = NULL; + + /* Initiate the Tx. */ + EMAC_StartTransmitNextBuffer( pxNetworkBuffer->xDataLength ); + iptraceNETWORK_INTERFACE_TRANSMIT(); + + /* The Tx has been initiated. */ + xReturn = pdPASS; + } + + break; + } + else + { + vTaskDelay( niTX_BUFFER_FREE_WAIT ); + } + } + + /* Finished with the network buffer. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void ENET_IRQHandler( void ) +{ + uint32_t ulInterruptCause; + + while( ( ulInterruptCause = LPC_EMAC->IntStatus ) != 0 ) + { + /* Clear the interrupt. */ + LPC_EMAC->IntClear = ulInterruptCause; + + /* Clear fatal error conditions. NOTE: The driver does not clear all + * errors, only those actually experienced. For future reference, range + * errors are not actually errors so can be ignored. */ + if( ( ulInterruptCause & EMAC_INT_TX_UNDERRUN ) != 0U ) + { + LPC_EMAC->Command |= EMAC_CR_TX_RES; + } + + /* Unblock the deferred interrupt handler task if the event was an + * Rx. */ + if( ( ulInterruptCause & EMAC_INT_RX_DONE ) != 0UL ) + { + xSemaphoreGiveFromISR( xEMACRxEventSemaphore, NULL ); + } + } + + /* ulInterruptCause is used for convenience here. A context switch is + * wanted, but coding portEND_SWITCHING_ISR( 1 ) would likely result in a + * compiler warning. */ + portEND_SWITCHING_ISR( ulInterruptCause ); +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void * pvParameters ) +{ + size_t xDataLength; + const uint16_t usCRCLength = 4; + NetworkBufferDescriptor_t * pxNetworkBuffer; + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + +/* This is not included in the header file for some reason. */ + extern uint8_t * EMAC_NextPacketToRead( void ); + + ( void ) pvParameters; + configASSERT( xEMACRxEventSemaphore != NULL ); + + for( ; ; ) + { + /* Wait for the EMAC interrupt to indicate that another packet has been + * received. The while() loop is only needed if INCLUDE_vTaskSuspend is + * set to 0 in FreeRTOSConfig.h. */ + while( xSemaphoreTake( xEMACRxEventSemaphore, portMAX_DELAY ) == pdFALSE ) + { + } + + /* At least one packet has been received. */ + while( EMAC_CheckReceiveIndex() != FALSE ) + { + /* Obtain the length, minus the CRC. The CRC is four bytes + * but the length is already minus 1. */ + xDataLength = ( size_t ) EMAC_GetReceiveDataSize() - ( usCRCLength - 1U ); + + if( xDataLength > 0U ) + { + /* Obtain a network buffer to pass this data into the + * stack. No storage is required as the network buffer + * will point directly to the buffer that already holds + * the received data. */ + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( 0, ( TickType_t ) 0 ); + + if( pxNetworkBuffer != NULL ) + { + pxNetworkBuffer->pucBuffer = EMAC_NextPacketToRead(); + pxNetworkBuffer->xDataLength = xDataLength; + xRxEvent.pvData = ( void * ) pxNetworkBuffer; + + /* Data was received and stored. Send a message to the IP + * task to let it know. */ + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + iptraceETHERNET_RX_EVENT_LOST(); + } + } + else + { + iptraceETHERNET_RX_EVENT_LOST(); + } + + iptraceNETWORK_INTERFACE_RECEIVE(); + } + + /* Release the frame. */ + EMAC_UpdateRxConsumeIndex(); + } + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/LPC18xx/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/LPC18xx/NetworkInterface.c new file mode 100644 index 0000000..f10ab6e --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/LPC18xx/NetworkInterface.c @@ -0,0 +1,1093 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +/* LPCOpen includes. */ +#include "chip.h" +#include "lpc_phy.h" + +/* The size of the stack allocated to the task that handles Rx packets. */ +#define nwRX_TASK_STACK_SIZE 140 + +#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 +#endif + +#ifndef configUSE_RMII + #define configUSE_RMII 1 +#endif + +#ifndef configNUM_RX_DESCRIPTORS + #error please define configNUM_RX_DESCRIPTORS in your FreeRTOSIPConfig.h +#endif + +#ifndef configNUM_TX_DESCRIPTORS + #error please define configNUM_TX_DESCRIPTORS in your FreeRTOSIPConfig.h +#endif + +#ifndef NETWORK_IRQHandler + #error NETWORK_IRQHandler must be defined to the name of the function that is installed in the interrupt vector table to handle Ethernet interrupts. +#endif + +#if !defined( MAC_FF_HMC ) + /* Hash for multicast. */ + #define MAC_FF_HMC ( 1UL << 2UL ) +#endif + +#ifndef iptraceEMAC_TASK_STARTING + #define iptraceEMAC_TASK_STARTING() do {} while( ipFALSE_BOOL ) +#endif + +/* Define the bits of .STATUS that indicate a reception error. */ +#define nwRX_STATUS_ERROR_BITS \ + ( RDES_CE /* CRC Error */ | \ + RDES_RE /* Receive Error */ | \ + RDES_DE /* Descriptor Error */ | \ + RDES_RWT /* Receive Watchdog Timeout */ | \ + RDES_LC /* Late Collision */ | \ + RDES_OE /* Overflow Error */ | \ + RDES_SAF /* Source Address Filter Fail */ | \ + RDES_AFM /* Destination Address Filter Fail */ | \ + RDES_LE /* Length Error */ ) + +/* Define the EMAC status bits that should trigger an interrupt. */ +#define nwDMA_INTERRUPT_MASK \ + ( DMA_IE_TIE /* Transmit interrupt enable */ | \ + DMA_IE_TSE /* Transmit stopped enable */ | \ + DMA_IE_OVE /* Overflow interrupt enable */ | \ + DMA_IE_RIE /* Receive interrupt enable */ | \ + DMA_IE_NIE /* Normal interrupt summary enable */ | \ + DMA_IE_AIE /* Abnormal interrupt summary enable */ | \ + DMA_IE_RUE /* Receive buffer unavailable enable */ | \ + DMA_IE_UNE /* Underflow interrupt enable. */ | \ + DMA_IE_TJE /* Transmit jabber timeout enable */ | \ + DMA_IE_RSE /* Received stopped enable */ | \ + DMA_IE_RWE /* Receive watchdog timeout enable */ | \ + DMA_IE_FBE ) /* Fatal bus error enable */ + +/* Interrupt events to process. Currently only the RX/TX events are processed + * although code for other events is included to allow for possible future + * expansion. */ +#define EMAC_IF_RX_EVENT 1UL +#define EMAC_IF_TX_EVENT 2UL +#define EMAC_IF_ERR_EVENT 4UL +#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) + +/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet + * driver will filter incoming packets and only pass the stack those packets it + * considers need processing. */ +#if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +#if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) || ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + #warning It is adviced to enable both macros +#endif + +#ifndef configPLACE_IN_SECTION_RAM + #define configPLACE_IN_SECTION_RAM + +/* + #define configPLACE_IN_SECTION_RAM __attribute__ ((section(".ramfunc"))) + */ +#endif + +/*-----------------------------------------------------------*/ + +/* + * Delay function passed into the library. The implementation uses FreeRTOS + * calls so the scheduler must be started before the driver can be used. + */ +static void prvDelay( uint32_t ulMilliSeconds ); + +/* + * Initialises the Tx and Rx descriptors respectively. + */ +static void prvSetupTxDescriptors( void ); +static void prvSetupRxDescriptors( void ); + +/* + * A task that processes received frames. + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/* + * Sets up the MAC with the results of an auto-negotiation. + */ +static BaseType_t prvSetLinkSpeed( void ); + +/* + * Generates a CRC for a MAC address that is then used to generate a hash index. + */ +static uint32_t prvGenerateCRC32( const uint8_t * ucAddress ); + +/* + * Generates a hash index when setting a filter to permit a MAC address. + */ +static uint32_t prvGetHashIndex( const uint8_t * ucAddress ); + +/* + * Update the hash table to allow a MAC address. + */ +static void prvAddMACAddress( const uint8_t * ucMacAddress ); + +/* + * Sometimes the DMA will report received data as being longer than the actual + * received from length. This function checks the reported length and corrects + * if if necessary. + */ +static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t * pxDescriptor ); + +/*-----------------------------------------------------------*/ + +/* Bit map of outstanding ETH interrupt events for processing. Currently only + * the Rx and Tx 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; + +/* Tx descriptors and index. */ +static ENET_ENHTXDESC_T xDMATxDescriptors[ configNUM_TX_DESCRIPTORS ]; + +/* ulNextFreeTxDescriptor is declared volatile, because it is accessed from + * to different tasks. */ +static volatile uint32_t ulNextFreeTxDescriptor; +static uint32_t ulTxDescriptorToClear; + +/* Rx descriptors and index. */ +static ENET_ENHRXDESC_T xDMARxDescriptors[ configNUM_RX_DESCRIPTORS ]; +static uint32_t ulNextRxDescriptorToProcess; + +/* The handle of the task that processes Rx packets. The handle is required so + * the task can be notified when new packets arrive. */ +static TaskHandle_t xRxHanderTask = NULL; + +#if ( ipconfigUSE_LLMNR == 1 ) + static const uint8_t xLLMNR_MACAddress[] = { '\x01', '\x00', '\x5E', '\x00', '\x00', '\xFC' }; +#endif /* ipconfigUSE_LLMNR == 1 */ + +/* 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; + +/*-----------------------------------------------------------*/ + + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + BaseType_t xReturn = pdPASS; + + /* The interrupt will be turned on when a link is established. */ + NVIC_DisableIRQ( ETHERNET_IRQn ); + + /* Disable receive and transmit DMA processes. */ + LPC_ETHERNET->DMA_OP_MODE &= ~( DMA_OM_ST | DMA_OM_SR ); + + /* Disable packet reception. */ + LPC_ETHERNET->MAC_CONFIG &= ~( MAC_CFG_RE | MAC_CFG_TE ); + + /* Call the LPCOpen function to initialise the hardware. */ + Chip_ENET_Init( LPC_ETHERNET ); + + /* Save MAC address. */ + Chip_ENET_SetADDR( LPC_ETHERNET, ipLOCAL_MAC_ADDRESS ); + + /* Clear all MAC address hash entries. */ + LPC_ETHERNET->MAC_HASHTABLE_HIGH = 0; + LPC_ETHERNET->MAC_HASHTABLE_LOW = 0; + + #if ( ipconfigUSE_LLMNR == 1 ) + { + prvAddMACAddress( xLLMNR_MACAddress ); + } + #endif /* ipconfigUSE_LLMNR == 1 */ + + /* Promiscuous flag (PR) and Receive All flag (RA) set to zero. The + * registers MAC_HASHTABLE_[LOW|HIGH] will be loaded to allow certain + * multi-cast addresses. */ + LPC_ETHERNET->MAC_FRAME_FILTER = MAC_FF_HMC; + + #if ( configUSE_RMII == 1 ) + { + if( lpc_phy_init( pdTRUE, prvDelay ) != SUCCESS ) + { + xReturn = pdFAIL; + } + } + #else + { + #warning This path has not been tested. + + if( lpc_phy_init( pdFALSE, prvDelay ) != SUCCESS ) + { + xReturn = pdFAIL; + } + } + #endif /* if ( configUSE_RMII == 1 ) */ + + if( xReturn == pdPASS ) + { + /* Guard against the task being created more than once and the + * descriptors being initialised more than once. */ + if( xRxHanderTask == NULL ) + { + xReturn = xTaskCreate( prvEMACHandlerTask, "EMAC", nwRX_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xRxHanderTask ); + configASSERT( xReturn != NULL ); + } + + if( xTXDescriptorSemaphore == NULL ) + { + /* Create a counting semaphore, with a value of 'configNUM_TX_DESCRIPTORS' + * and a maximum of 'configNUM_TX_DESCRIPTORS'. */ + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) configNUM_TX_DESCRIPTORS, ( UBaseType_t ) configNUM_TX_DESCRIPTORS ); + configASSERT( xTXDescriptorSemaphore != NULL ); + } + + /* Enable MAC interrupts. */ + LPC_ETHERNET->DMA_INT_EN = nwDMA_INTERRUPT_MASK; + } + + if( xReturn != pdFAIL ) + { + /* Auto-negotiate was already started. Wait for it to complete. */ + xReturn = prvSetLinkSpeed(); + + if( xReturn == pdPASS ) + { + /* Initialise the descriptors. */ + prvSetupTxDescriptors(); + prvSetupRxDescriptors(); + + /* Clear all interrupts. */ + LPC_ETHERNET->DMA_STAT = DMA_ST_ALL; + + /* Enable receive and transmit DMA processes. */ + LPC_ETHERNET->DMA_OP_MODE |= DMA_OM_ST | DMA_OM_SR; + + /* Set Receiver / Transmitter Enable. */ + LPC_ETHERNET->MAC_CONFIG |= MAC_CFG_RE | MAC_CFG_TE; + + /* Start receive polling. */ + LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1; + + /* Enable interrupts in the NVIC. */ + NVIC_SetPriority( ETHERNET_IRQn, configMAC_INTERRUPT_PRIORITY ); + NVIC_EnableIRQ( ETHERNET_IRQn ); + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#define niBUFFER_1_PACKET_SIZE 1536 + +static __attribute__( ( section( "._ramAHB32" ) ) ) 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; + } +} +/*-----------------------------------------------------------*/ + +configPLACE_IN_SECTION_RAM +static void vClearTXBuffers() +{ + uint32_t ulLastDescriptor = ulNextFreeTxDescriptor; + size_t uxCount = ( ( size_t ) configNUM_TX_DESCRIPTORS ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + NetworkBufferDescriptor_t * pxNetworkBuffer; + uint8_t * ucPayLoad; + #endif + + /* This function is called after a TX-completion interrupt. + * It will release each Network Buffer used in xNetworkInterfaceOutput(). + * 'uxCount' represents the number of descriptors given to DMA for transmission. + * After sending a packet, the DMA will clear the 'TDES_OWN' bit. */ + while( ( uxCount > ( size_t ) 0u ) && ( ( xDMATxDescriptors[ ulTxDescriptorToClear ].CTRLSTAT & TDES_OWN ) == 0 ) ) + { + if( ( ulTxDescriptorToClear == ulLastDescriptor ) && ( uxCount != ( size_t ) configNUM_TX_DESCRIPTORS ) ) + { + break; + } + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + ucPayLoad = ( uint8_t * ) xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD; + + if( ucPayLoad != NULL ) + { + /* B1ADD points to a pucEthernetBuffer of a Network Buffer descriptor. */ + pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad ); + + configASSERT( pxNetworkBuffer != NULL ); + + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD = ( uint32_t ) 0u; + } + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + /* Move onto the next descriptor, wrapping if necessary. */ + ulTxDescriptorToClear++; + + if( ulTxDescriptorToClear >= configNUM_TX_DESCRIPTORS ) + { + ulTxDescriptorToClear = 0; + } + + uxCount--; + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + } +} + +/*-----------------------------------------------------------*/ + +configPLACE_IN_SECTION_RAM +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t bReleaseAfterSend ) +{ + BaseType_t xReturn = pdFAIL; + const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50 ); + + /* Attempt to obtain access to a Tx descriptor. */ + do + { + if( xTXDescriptorSemaphore == NULL ) + { + break; + } + + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + /* Time-out waiting for a free TX descriptor. */ + break; + } + + /* If the descriptor is still owned by the DMA it can't be used. */ + if( ( xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT & TDES_OWN ) != 0 ) + { + /* The semaphore was taken, the TX DMA-descriptor is still not available. + * Actually that should not occur, the 'TDES_OWN' was already confirmed low in vClearTXBuffers(). */ + xSemaphoreGive( xTXDescriptorSemaphore ); + } + else + { + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* bReleaseAfterSend should always be set when using the zero + * copy driver. */ + configASSERT( bReleaseAfterSend != pdFALSE ); + + /* The DMA's descriptor to point directly to the data in the + * network buffer descriptor. The data is not copied. */ + xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD = ( uint32_t ) pxDescriptor->pucEthernetBuffer; + + /* The DMA descriptor will 'own' this Network Buffer, + * until it has been sent. So don't release it now. */ + bReleaseAfterSend = false; + } + #else /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + { + /* The data is copied from the network buffer descriptor into + * the DMA's descriptor. */ + memcpy( ( void * ) xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD, ( void * ) pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ); + } + #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + + xDMATxDescriptors[ ulNextFreeTxDescriptor ].BSIZE = ( uint32_t ) TDES_ENH_BS1( pxDescriptor->xDataLength ); + + /* This descriptor is given back to the DMA. */ + xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT |= TDES_OWN; + + /* Ensure the DMA is polling Tx descriptors. */ + LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1; + + iptraceNETWORK_INTERFACE_TRANSMIT(); + + /* Move onto the next descriptor, wrapping if necessary. */ + ulNextFreeTxDescriptor++; + + if( ulNextFreeTxDescriptor >= configNUM_TX_DESCRIPTORS ) + { + ulNextFreeTxDescriptor = 0; + } + + /* The Tx has been initiated. */ + xReturn = pdPASS; + } + } while( 0 ); + + /* The buffer has been sent so can be released. */ + if( bReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvDelay( uint32_t ulMilliSeconds ) +{ + /* Ensure the scheduler was started before attempting to use the scheduler to + * create a delay. */ + configASSERT( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ); + + vTaskDelay( pdMS_TO_TICKS( ulMilliSeconds ) ); +} +/*-----------------------------------------------------------*/ + +static void prvSetupTxDescriptors( void ) +{ + BaseType_t x; + + /* Start with Tx descriptors clear. */ + memset( ( void * ) xDMATxDescriptors, 0, sizeof( xDMATxDescriptors ) ); + + /* Index to the next Tx descriptor to use. */ + ulNextFreeTxDescriptor = 0ul; + + /* Index to the next Tx descriptor to clear ( after transmission ). */ + ulTxDescriptorToClear = 0ul; + + for( x = 0; x < configNUM_TX_DESCRIPTORS; x++ ) + { + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Nothing to do, B1ADD will be set when data is ready to transmit. + * Currently the memset above will have set it to NULL. */ + } + #else + { + /* Allocate a buffer to the Tx descriptor. This is the most basic + * way of creating a driver as the data is then copied into the + * buffer. */ + xDMATxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE ); + + /* Use an assert to check the allocation as +TCP applications will + * often not use a malloc() failed hook as the TCP stack will recover + * from allocation failures. */ + configASSERT( xDMATxDescriptors[ x ].B1ADD != 0U ); + } + #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + + /* Buffers hold an entire frame so all buffers are both the start and + * end of a frame. */ + /* TDES_ENH_TCH Second Address Chained. */ + /* TDES_ENH_CIC(n) Checksum Insertion Control, tried but it does not work for the LPC18xx... */ + /* TDES_ENH_FS First Segment. */ + /* TDES_ENH_LS Last Segment. */ + /* TDES_ENH_IC Interrupt on Completion. */ + xDMATxDescriptors[ x ].CTRLSTAT = TDES_ENH_TCH | TDES_ENH_CIC( 3 ) | TDES_ENH_FS | TDES_ENH_LS | TDES_ENH_IC; + xDMATxDescriptors[ x ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ x + 1 ]; + } + + xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].CTRLSTAT |= TDES_ENH_TER; + xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ 0 ]; + + /* Point the DMA to the base of the descriptor list. */ + LPC_ETHERNET->DMA_TRANS_DES_ADDR = ( uint32_t ) xDMATxDescriptors; +} +/*-----------------------------------------------------------*/ + +static void prvSetupRxDescriptors( void ) +{ + BaseType_t x; + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + NetworkBufferDescriptor_t * pxNetworkBuffer; + #endif + + /* Index to the next Rx descriptor to use. */ + ulNextRxDescriptorToProcess = 0; + + /* Clear RX descriptor list. */ + memset( ( void * ) xDMARxDescriptors, 0, sizeof( xDMARxDescriptors ) ); + + for( x = 0; x < configNUM_RX_DESCRIPTORS; x++ ) + { + /* Allocate a buffer of the largest possible frame size as it is not + * known what size received frames will be. */ + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 ); + + /* During start-up there should be enough Network Buffers available, + * so it is safe to use configASSERT(). + * In case this assert fails, please check: configNUM_RX_DESCRIPTORS, + * ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, and in case BufferAllocation_2.c + * is included, check the amount of available heap. */ + configASSERT( pxNetworkBuffer != NULL ); + + /* Pass the actual buffer to DMA. */ + xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pxNetworkBuffer->pucEthernetBuffer; + } + #else + { + /* All DMA descriptors are populated with permanent memory blocks. + * Their contents will be copy to Network Buffers. */ + xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE ); + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + /* Use an assert to check the allocation as +TCP applications will often + * not use a malloc failed hook as the TCP stack will recover from + * allocation failures. */ + configASSERT( xDMARxDescriptors[ x ].B1ADD != 0U ); + + xDMARxDescriptors[ x ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ x + 1 ] ); + xDMARxDescriptors[ x ].CTRL = ( uint32_t ) RDES_ENH_BS1( ipTOTAL_ETHERNET_FRAME_SIZE ) | RDES_ENH_RCH; + + /* The descriptor is available for use by the DMA. */ + xDMARxDescriptors[ x ].STATUS = RDES_OWN; + } + + /* RDES_ENH_RER Receive End of Ring. */ + xDMARxDescriptors[ ( configNUM_RX_DESCRIPTORS - 1 ) ].CTRL |= RDES_ENH_RER; + xDMARxDescriptors[ configNUM_RX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ 0 ] ); + + /* Point the DMA to the base of the descriptor list. */ + LPC_ETHERNET->DMA_REC_DES_ADDR = ( uint32_t ) xDMARxDescriptors; +} +/*-----------------------------------------------------------*/ +configPLACE_IN_SECTION_RAM +static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t * pxDescriptor ) +{ + size_t xExpectedLength; + IPPacket_t * pxIPPacket; + + pxIPPacket = ( IPPacket_t * ) pxDescriptor->pucEthernetBuffer; + /* Look at the actual length of the packet, translate it to a host-endian notation. */ + xExpectedLength = sizeof( EthernetHeader_t ) + ( size_t ) FreeRTOS_htons( pxIPPacket->xIPHeader.usLength ); + + if( xExpectedLength == ( pxDescriptor->xDataLength + 4 ) ) + { + pxDescriptor->xDataLength -= 4; + } + else + { + if( pxDescriptor->xDataLength > xExpectedLength ) + { + pxDescriptor->xDataLength = ( size_t ) xExpectedLength; + } + } +} +/*-----------------------------------------------------------*/ +configPLACE_IN_SECTION_RAM +BaseType_t xGetPhyLinkStatus( void ) +{ + BaseType_t xReturn; + + if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +uint32_t ulDataAvailable; + +configPLACE_IN_SECTION_RAM +static BaseType_t prvNetworkInterfaceInput() +{ + BaseType_t xResult = pdFALSE; + uint32_t ulStatus; + eFrameProcessingResult_t eResult; + const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 ); + const UBaseType_t uxMinimumBuffersRemaining = 3UL; + uint16_t usLength; + NetworkBufferDescriptor_t * pxDescriptor; + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + NetworkBufferDescriptor_t * pxNewDescriptor; + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + #if ( ipconfigUSE_LINKED_RX_MESSAGES == 0 ) + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + #endif + + /* Process each descriptor that is not still in use by the DMA. */ + ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS; + + if( ( ulStatus & RDES_OWN ) == 0 ) + { + /* Check packet for errors */ + if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 ) + { + /* There is some reception error. */ + intCount[ 3 ]++; + /* Clear error bits. */ + ulStatus &= ~( ( uint32_t ) nwRX_STATUS_ERROR_BITS ); + } + else + { + xResult++; + + eResult = ipCONSIDER_FRAME_FOR_PROCESSING( ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD ) ); + + if( eResult == eProcessBuffer ) + { + if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 ) + { + ulPHYLinkStatus |= PHY_LINK_CONNECTED; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (message received)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) ); + } + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining ) + { + pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xDescriptorWaitTime ); + } + else + { + /* Too risky to allocate a new Network Buffer. */ + pxNewDescriptor = NULL; + } + + if( pxNewDescriptor != NULL ) + #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining ) + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + { + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + const uint8_t * pucBuffer; + #endif + + /* Get the actual length. */ + usLength = RDES_FLMSK( ulStatus ); + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + /* Replace the character buffer 'B1ADD'. */ + pucBuffer = ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD ); + xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD = ( uint32_t ) pxNewDescriptor->pucEthernetBuffer; + + /* 'B1ADD' contained the address of a 'pucEthernetBuffer' that + * belongs to a Network Buffer. Find the original Network Buffer. */ + pxDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer ); + + /* This zero-copy driver makes sure that every 'xDMARxDescriptors' contains + * a reference to a Network Buffer at any time. + * In case it runs out of Network Buffers, a DMA buffer won't be replaced, + * and the received messages is dropped. */ + configASSERT( pxDescriptor != NULL ); + } + #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + { + /* Create a buffer of exactly the required length. */ + pxDescriptor = pxGetNetworkBufferWithDescriptor( usLength, xDescriptorWaitTime ); + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + if( pxDescriptor != NULL ) + { + pxDescriptor->xDataLength = ( size_t ) usLength; + #if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) + { + /* Copy the data into the allocated buffer. */ + memcpy( ( void * ) pxDescriptor->pucEthernetBuffer, ( void * ) xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD, usLength ); + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + /* It is possible that more data was copied than + * actually makes up the frame. If this is the case + * adjust the length to remove any trailing bytes. */ + prvRemoveTrailingBytes( pxDescriptor ); + + /* Pass the data to the TCP/IP task for processing. */ + xRxEvent.pvData = ( void * ) pxDescriptor; + + if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFALSE ) + { + /* Could not send the descriptor into the TCP/IP + * stack, it must be released. */ + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + else + { + iptraceNETWORK_INTERFACE_RECEIVE(); + + /* The data that was available at the top of this + * loop has been sent, so is no longer available. */ + ulDataAvailable = pdFALSE; + } + } + } + } + else + { + /* The packet is discarded as uninteresting. */ + ulDataAvailable = pdFALSE; + } + + /* Got here because received data was sent to the IP task or the + * data contained an error and was discarded. Give the descriptor + * back to the DMA. */ + xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS = ulStatus | RDES_OWN; + + /* Move onto the next descriptor. */ + ulNextRxDescriptorToProcess++; + + if( ulNextRxDescriptorToProcess >= configNUM_RX_DESCRIPTORS ) + { + ulNextRxDescriptorToProcess = 0; + } + + ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS; + } /* if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 ) */ + } /* if( ( ulStatus & RDES_OWN ) == 0 ) */ + + /* Restart receive polling. */ + LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1; + + return xResult; +} +/*-----------------------------------------------------------*/ + +configPLACE_IN_SECTION_RAM +void NETWORK_IRQHandler( void ) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + uint32_t ulDMAStatus; + const uint32_t ulRxInterruptMask = + DMA_ST_RI | /* Receive interrupt */ + DMA_ST_RU; /* Receive buffer unavailable */ + const uint32_t ulTxInterruptMask = + DMA_ST_TI | /* Transmit interrupt */ + DMA_ST_TPS; /* Transmit process stopped */ + + configASSERT( xRxHanderTask ); + + /* Get pending interrupts. */ + ulDMAStatus = LPC_ETHERNET->DMA_STAT; + + /* RX group interrupt(s). */ + if( ( ulDMAStatus & ulRxInterruptMask ) != 0x00 ) + { + /* Remember that an RX event has happened. */ + ulISREvents |= EMAC_IF_RX_EVENT; + vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken ); + intCount[ 0 ]++; + } + + /* TX group interrupt(s). */ + if( ( ulDMAStatus & ulTxInterruptMask ) != 0x00 ) + { + /* Remember that a TX event has happened. */ + ulISREvents |= EMAC_IF_TX_EVENT; + vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken ); + intCount[ 1 ]++; + } + + /* Test for 'Abnormal interrupt summary'. */ + if( ( ulDMAStatus & DMA_ST_AIE ) != 0x00 ) + { + /* The trace macro must be written such that it can be called from + * an interrupt. */ + iptraceETHERNET_RX_EVENT_LOST(); + } + + /* Clear pending interrupts */ + LPC_ETHERNET->DMA_STAT = ulDMAStatus; + + /* Context switch needed? */ + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvSetLinkSpeed( void ) +{ + BaseType_t xReturn = pdFAIL; + TickType_t xTimeOnEntering; + uint32_t ulPhyStatus; + const TickType_t xAutoNegotiateDelay = pdMS_TO_TICKS( 5000UL ); + + /* Ensure polling does not starve lower priority tasks by temporarily + * setting the priority of this task to that of the idle task. */ + vTaskPrioritySet( NULL, tskIDLE_PRIORITY ); + + xTimeOnEntering = xTaskGetTickCount(); + + do + { + ulPhyStatus = lpcPHYStsPoll(); + + if( ( ulPhyStatus & PHY_LINK_CONNECTED ) != 0x00 ) + { + /* Set interface speed and duplex. */ + if( ( ulPhyStatus & PHY_LINK_SPEED100 ) != 0x00 ) + { + Chip_ENET_SetSpeed( LPC_ETHERNET, 1 ); + } + else + { + Chip_ENET_SetSpeed( LPC_ETHERNET, 0 ); + } + + if( ( ulPhyStatus & PHY_LINK_FULLDUPLX ) != 0x00 ) + { + Chip_ENET_SetDuplex( LPC_ETHERNET, true ); + } + else + { + Chip_ENET_SetDuplex( LPC_ETHERNET, false ); + } + + xReturn = pdPASS; + break; + } + } while( ( xTaskGetTickCount() - xTimeOnEntering ) < xAutoNegotiateDelay ); + + /* Reset the priority of this task back to its original value. */ + vTaskPrioritySet( NULL, ipconfigIP_TASK_PRIORITY ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static uint32_t prvGenerateCRC32( const uint8_t * ucAddress ) +{ + unsigned int j; + const uint32_t Polynomial = 0xEDB88320; + uint32_t crc = ~0ul; + const uint8_t * pucCurrent = ( const uint8_t * ) ucAddress; + const uint8_t * pucLast = pucCurrent + 6; + + /* Calculate normal CRC32 */ + while( pucCurrent < pucLast ) + { + crc ^= *( pucCurrent++ ); + + for( j = 0; j < 8; j++ ) + { + if( ( crc & 1 ) != 0 ) + { + crc = ( crc >> 1 ) ^ Polynomial; + } + else + { + crc >>= 1; + } + } + } + + return ~crc; +} +/*-----------------------------------------------------------*/ + +static uint32_t prvGetHashIndex( const uint8_t * ucAddress ) +{ + uint32_t ulCrc = prvGenerateCRC32( ucAddress ); + uint32_t ulIndex = 0ul; + BaseType_t xCount = 6; + + /* Take the lowest 6 bits of the CRC32 and reverse them */ + while( xCount-- ) + { + ulIndex <<= 1; + ulIndex |= ( ulCrc & 1 ); + ulCrc >>= 1; + } + + /* This is the has value of 'ucAddress' */ + return ulIndex; +} +/*-----------------------------------------------------------*/ + +static void prvAddMACAddress( const uint8_t * ucMacAddress ) +{ + BaseType_t xIndex; + + xIndex = prvGetHashIndex( ucMacAddress ); + + if( xIndex >= 32 ) + { + LPC_ETHERNET->MAC_HASHTABLE_HIGH |= ( 1u << ( xIndex - 32 ) ); + } + else + { + LPC_ETHERNET->MAC_HASHTABLE_LOW |= ( 1u << xIndex ); + } +} +/*-----------------------------------------------------------*/ + +configPLACE_IN_SECTION_RAM +static void prvEMACHandlerTask( void * pvParameters ) +{ + TimeOut_t xPhyTime; + TickType_t xPhyRemTime; + UBaseType_t uxLastMinBufferCount = 0; + UBaseType_t uxCurrentCount; + BaseType_t xResult = 0; + uint32_t ulStatus; + const TickType_t xBlockTime = pdMS_TO_TICKS( 5000ul ); + + /* Remove compiler warning about unused parameter. */ + ( void ) pvParameters; + + /* A possibility to set some additional task properties. */ + iptraceEMAC_TASK_STARTING(); + + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + + for( ; ; ) + { + uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); + + if( uxLastMinBufferCount != uxCurrentCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinBufferCount = uxCurrentCount; + FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", + uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); + } + + #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + { + static UBaseType_t uxLastMinQueueSpace = 0; + + uxCurrentCount = uxGetMinimumIPQueueSpace(); + + if( uxLastMinQueueSpace != uxCurrentCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinQueueSpace = uxCurrentCount; + FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); + } + } + #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ + + ulTaskNotifyTake( pdTRUE, xBlockTime ); + + xResult = ( BaseType_t ) 0; + + if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) + { + /* Code to release TX buffers if zero-copy is used. */ + ulISREvents &= ~EMAC_IF_TX_EVENT; + { + /* Check if DMA packets have been delivered. */ + vClearTXBuffers(); + } + } + + if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 ) + { + ulISREvents &= ~EMAC_IF_RX_EVENT; + + xResult = prvNetworkInterfaceInput(); + + if( xResult > 0 ) + { + while( prvNetworkInterfaceInput() > 0 ) + { + } + } + } + + if( 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 ) + { + ulStatus = lpcPHYStsPoll(); + + if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != ( ulStatus & PHY_LINK_CONNECTED ) ) + { + ulPHYLinkStatus = ulStatus; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (polled PHY)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) ); + } + + vTaskSetTimeOutState( &xPhyTime ); + + if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); + } + else + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + } + } + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/LPC18xx/ReadMe.txt b/FreeRTOS/source/portable/NetworkInterface/LPC18xx/ReadMe.txt new file mode 100644 index 0000000..cde8e0e --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/LPC18xx/ReadMe.txt @@ -0,0 +1,3 @@ +NetworkInterface.c: +Requires NXP's LPCOpen library and was developed on an LPC1830 and LPC1835 Xplorer +boards from NGX. \ No newline at end of file diff --git a/FreeRTOS/source/portable/NetworkInterface/LPC54018/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/LPC54018/NetworkInterface.c new file mode 100644 index 0000000..86b006d --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/LPC54018/NetworkInterface.c @@ -0,0 +1,382 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* FreeRTOS includes. */ +#include "LPC54018.h" +#include "FreeRTOS.h" +#include "list.h" + +#include + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" + +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" + +#include "fsl_enet.h" +#include "fsl_phy.h" + +#include "fsl_enet_mdio.h" +#include "fsl_phylan8720a.h" +#include "fsl_debug_console.h" + +#define PHY_ADDRESS ( 0x00U ) +/* MDIO operations. */ +#define EXAMPLE_MDIO_OPS lpc_enet_ops +/* PHY operations. */ +#define EXAMPLE_PHY_OPS phylan8720a_ops +#define ENET_RXBD_NUM ( 4 ) +#define ENET_TXBD_NUM ( 4 ) +#define ENET_RXBUFF_SIZE ( ENET_FRAME_MAX_FRAMELEN ) +#define ENET_BuffSizeAlign( n ) ENET_ALIGN( n, ENET_BUFF_ALIGNMENT ) +#define ENET_ALIGN( x, align ) ( ( unsigned int ) ( ( x ) + ( ( align ) - 1 ) ) & ( unsigned int ) ( ~( unsigned int ) ( ( align ) - 1 ) ) ) + +#if defined( __GNUC__ ) + #ifndef __ALIGN_END + #define __ALIGN_END __attribute__( ( aligned( ENET_BUFF_ALIGNMENT ) ) ) + #endif + #ifndef __ALIGN_BEGIN + #define __ALIGN_BEGIN + #endif +#else + #ifndef __ALIGN_END + #define __ALIGN_END + #endif + #ifndef __ALIGN_BEGIN + #if defined( __CC_ARM ) || defined( __ARMCC_VERSION ) + #define __ALIGN_BEGIN __attribute__( ( aligned( ENET_BUFF_ALIGNMENT ) ) ) + #elif defined( __ICCARM__ ) + #define __ALIGN_BEGIN + #endif + #endif +#endif /* if defined( __GNUC__ ) */ + +/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet + * driver will filter incoming packets and only pass the stack those packets it + * considers need processing. */ +#if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +#ifndef NETWORK_INTERFACE_RX_PRIORITY + #define NETWORK_INTERFACE_RX_PRIORITY ( configMAX_PRIORITIES - 1 ) +#endif + +/******************************************************************************* + * Variables + ******************************************************************************/ +#if defined( __ICCARM__ ) + #pragma data_alignment = ENET_BUFF_ALIGNMENT +#endif +__ALIGN_BEGIN enet_rx_bd_struct_t g_rxBuffDescrip[ ENET_RXBD_NUM ] __ALIGN_END; +#if defined( __ICCARM__ ) + #pragma data_alignment = ENET_BUFF_ALIGNMENT +#endif +__ALIGN_BEGIN enet_tx_bd_struct_t g_txBuffDescrip[ ENET_TXBD_NUM ] __ALIGN_END; + +enet_handle_t g_handle = { 0 }; +/* The MAC address for ENET device. */ +uint8_t g_macAddr[ 6 ] = { 0xde, 0xad, 0x00, 0xbe, 0xef, 0x01 }; + +bool g_linkStatus = false; + +/*! @brief Enet PHY and MDIO interface handler. */ +static mdio_handle_t mdioHandle = { .ops = &EXAMPLE_MDIO_OPS }; +static phy_handle_t phyHandle = { .phyAddr = PHY_ADDRESS, .mdioHandle = &mdioHandle, .ops = &EXAMPLE_PHY_OPS }; + +__ALIGN_BEGIN uint32_t receiveBuffer[ ENET_RXBD_NUM ][ ENET_RXBUFF_SIZE / sizeof( uint32_t ) + 1 ] __ALIGN_END; +uint32_t rxbuffer[ ENET_RXBD_NUM ]; + +TaskHandle_t receiveTaskHandle; + +void ENET_IntCallback( ENET_Type * base, + enet_handle_t * handle, + enet_event_t event, + uint8_t channel, + void * param ) +{ + BaseType_t needsToYield = pdFALSE; + + switch( event ) + { + case kENET_TxIntEvent: + break; + + case kENET_RxIntEvent: + vTaskNotifyGiveFromISR( receiveTaskHandle, &needsToYield ); + portEND_SWITCHING_ISR( needsToYield ); + break; + + default: + break; + } +} + +static void prvProcessFrame( int length ) +{ + NetworkBufferDescriptor_t * pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( length, 0 ); + + if( pxBufferDescriptor != NULL ) + { + ENET_ReadFrame( ENET, &g_handle, pxBufferDescriptor->pucEthernetBuffer, length, 0 ); + pxBufferDescriptor->xDataLength = length; + + if( ipCONSIDER_FRAME_FOR_PROCESSING( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer ) + { + IPStackEvent_t xRxEvent; + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = ( void * ) pxBufferDescriptor; + + if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); + iptraceETHERNET_RX_EVENT_LOST(); + PRINTF( "RX Event Lost\n" ); + } + } + else + { + PRINTF( "RX Event not to be considered\n" ); + vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); + /* Not sure if a trace is required. The stack did not want this message */ + } + } + else + { + PRINTF( "RX No Buffer Available\n" ); + ENET_ReadFrame( ENET, &g_handle, NULL, 0, 0 ); + /* No buffer available to receive this message */ + iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER(); + } +} + +static void rx_task( void * parameter ) +{ + while( pdTRUE ) + { + if( ulTaskNotifyTake( pdTRUE, pdMS_TO_TICKS( 500 ) ) == pdFALSE ) /* no RX packets for a bit so check for a link */ + { + PHY_GetLinkStatus( &phyHandle, &g_linkStatus ); + } + else + { + BaseType_t receiving = pdTRUE; + + while( receiving == pdTRUE ) + { + uint32_t length; + const status_t status = ENET_GetRxFrameSize( ENET, &g_handle, &length, 0 ); + + switch( status ) + { + case kStatus_Success: /* there is a frame. process it */ + + if( length ) + { + prvProcessFrame( length ); + } + + break; + + case kStatus_ENET_RxFrameEmpty: /* Received an empty frame. Ignore it */ + receiving = pdFALSE; + break; + + case kStatus_ENET_RxFrameError: /* Received an error frame. Read & drop it */ + PRINTF( "RX Receive Error\n" ); + ENET_ReadFrame( ENET, &g_handle, NULL, 0, 0 ); + /* Not sure if a trace is required. The MAC had an error and needed to dump bytes */ + break; + + default: + PRINTF( "RX Receive default\n" ); + break; + } + } + } + } +} + +BaseType_t xGetPhyLinkStatus( void ) +{ + return g_linkStatus ? pdTRUE : pdFALSE; +} + + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + BaseType_t returnValue = pdFAIL; + static enum + { + initPhy, waitForLink, startReceiver, configurePhy + } + networkInitialisePhase = initPhy; + + switch( networkInitialisePhase ) + { + default: + networkInitialisePhase = initPhy; + + /* fall through */ + case initPhy: + { + phy_config_t phyConfig; + phyConfig.phyAddr = PHY_ADDRESS; + phyConfig.autoNeg = true; + mdioHandle.resource.base = ENET; + + status_t status = PHY_Init( &phyHandle, &phyConfig ); + + if( status == kStatus_PHY_AutoNegotiateFail ) + { + PRINTF( "\nPHY Auto-negotiation failed. Please check the cable connection and link partner setting.\n" ); + break; + } + } + + case startReceiver: + networkInitialisePhase = startReceiver; + + if( xTaskCreate( rx_task, "rx_task", 512, NULL, NETWORK_INTERFACE_RX_PRIORITY, &receiveTaskHandle ) != pdPASS ) + { + PRINTF( "Network Receive Task creation failed!.\n" ); + break; + } + + /* fall through */ + case waitForLink: + networkInitialisePhase = waitForLink; + { + if( !xGetPhyLinkStatus() ) + { + PRINTF( "No Link\n" ); + break; + } + } + + /* fall through */ + case configurePhy: + { + networkInitialisePhase = configurePhy; + enet_config_t config; + phy_speed_t speed; + phy_duplex_t duplex; + PHY_GetLinkSpeedDuplex( &phyHandle, &speed, &duplex ); + /* Get default configuration 100M RMII. */ + ENET_GetDefaultConfig( &config ); + + /* Use the actual speed and duplex when phy success to finish the autonegotiation. */ + config.miiSpeed = ( enet_mii_speed_t ) speed; + config.miiDuplex = ( enet_mii_duplex_t ) duplex; + + /* Initialize ENET. */ + uint32_t refClock = 50000000; /* 50MHZ for rmii reference clock. */ + ENET_Init( ENET, &config, g_macAddr, refClock ); + + /* Enable the rx interrupt. */ + ENET_EnableInterrupts( ENET, ( kENET_DmaRx ) ); + + /* Initialize Descriptor. */ + int bufferIndex; + + for( bufferIndex = 0; bufferIndex < ENET_RXBD_NUM; bufferIndex++ ) + { + rxbuffer[ bufferIndex ] = ( uint32_t ) &receiveBuffer[ bufferIndex ]; + } + + /* prepare the buffer configuration. */ + enet_buffer_config_t buffConfig[ 1 ] = + { + { + ENET_RXBD_NUM, ENET_TXBD_NUM, + &g_txBuffDescrip[ 0 ], &g_txBuffDescrip[ 0 ], + &g_rxBuffDescrip[ 0 ], &g_rxBuffDescrip[ ENET_RXBD_NUM ], + &rxbuffer[ 0 ], ENET_BuffSizeAlign( ENET_RXBUFF_SIZE ), + } + }; + ENET_DescriptorInit( ENET, &config, &buffConfig[ 0 ] ); + + /* Create the handler. */ + ENET_CreateHandler( ENET, &g_handle, &config, &buffConfig[ 0 ], ENET_IntCallback, NULL ); + NVIC_SetPriority( 65 - 16, 4 ); /* TODO this is a hack and I would expect a nice ENET API for priority. */ + + /* Active TX/RX. */ + ENET_StartRxTx( ENET, 1, 1 ); + } + returnValue = pdPASS; + break; + } + + return returnValue; +} + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t xReleaseAfterSend ) +{ + BaseType_t response = pdFALSE; + status_t status; + + if( xGetPhyLinkStatus() ) + { + status = ENET_SendFrame( ENET, &g_handle, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); + + switch( status ) + { + default: /* anything not Success will be a failure */ + case kStatus_ENET_TxFrameBusy: + PRINTF( "TX Frame Busy\n" ); + break; + + case kStatus_Success: + iptraceNETWORK_INTERFACE_TRANSMIT(); + response = pdTRUE; + break; + } + } + + if( xReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + return response; +} + +/* statically allocate the buffers */ +/* allocating them as uint32_t's to force them into word alignment, a requirement of the DMA. */ +__ALIGN_BEGIN static uint32_t buffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ ( ipBUFFER_PADDING + ENET_RXBUFF_SIZE ) / sizeof( uint32_t ) + 1 ] __ALIGN_END; +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + for( int x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ ) + { + pxNetworkBuffers[ x ].pucEthernetBuffer = ( uint8_t * ) &buffers[ x ][ 0 ] + ipBUFFER_PADDING; + buffers[ x ][ 0 ] = ( uint32_t ) &pxNetworkBuffers[ x ]; + } +} diff --git a/FreeRTOS/source/portable/NetworkInterface/M487/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/M487/NetworkInterface.c new file mode 100644 index 0000000..5b66983 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/M487/NetworkInterface.c @@ -0,0 +1,355 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "list.h" +#include "queue.h" +#include "semphr.h" +#include "task.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + + +#include "m480_eth.h" + +/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet + * driver will filter incoming packets and only pass the stack those packets it + * considers need processing. */ +#if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to twice + * the size of the stack used by the idle task - but allow this to be overridden in + * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE ) +#endif + + +static SemaphoreHandle_t xTXMutex = NULL; + +/* The handle of the task that processes Rx packets. The handle is required so + * the task can be notified when new packets arrive. */ +static TaskHandle_t xRxHanderTask = NULL; +static TimerHandle_t xPhyHandlerTask = NULL; + +/* + * A task that processes received frames. + */ +static void prvEMACHandlerTask( void * pvParameters ); +static void prvPhyTmrCallback( TimerHandle_t xTimer ); + +/* The size of each buffer when BufferAllocation_1 is used: + * http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html */ + +#define niBUFFER_1_PACKET_SIZE 1536 +#ifdef __ICCARM__ + #pragma data_alignment=4 + static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] +#else + static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__( ( aligned( 4 ) ) ); +#endif + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + uint8_t hwaddr[ 6 ]; + BaseType_t xReturn = pdPASS; + + /* Init ETH */ + numaker_mac_address( hwaddr ); + FreeRTOS_UpdateMACAddress( hwaddr ); + FreeRTOS_printf( ( "mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", hwaddr[ 0 ], hwaddr[ 1 ], hwaddr[ 2 ], hwaddr[ 3 ], hwaddr[ 4 ], hwaddr[ 5 ] ) ); + + /* Enable clock & set EMAC configuration */ + /* Enable MAC and DMA transmission and reception */ + if( numaker_eth_init( hwaddr ) < 0 ) + { + xReturn = pdFAIL; + } + else + { + xReturn = pdPASS; + + /* Guard against the task being created more than once and the + * descriptors being initialized more than once. */ + /* Timer task to monitor PHY Link status */ + if( xPhyHandlerTask == NULL ) + { + xPhyHandlerTask = xTimerCreate( "TimerPhy", pdMS_TO_TICKS( 1000 ), pdTRUE, 0, prvPhyTmrCallback ); + configASSERT( xPhyHandlerTask ); + xReturn = xTimerStart( xPhyHandlerTask, 0 ); + configASSERT( xReturn ); + } + + /* Rx task */ + if( xRxHanderTask == NULL ) + { + xReturn = xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xRxHanderTask ); + configASSERT( xReturn ); + } + + if( xTXMutex == NULL ) + { + xTXMutex = xSemaphoreCreateMutex(); + configASSERT( xTXMutex ); + } + } + + NVIC_SetPriority( EMAC_RX_IRQn, configMAC_INTERRUPT_PRIORITY ); + NVIC_SetPriority( EMAC_TX_IRQn, configMAC_INTERRUPT_PRIORITY ); + + numaker_eth_enable_interrupts(); + + FreeRTOS_printf( ( "ETH-RX priority:%d\n", NVIC_GetPriority( EMAC_RX_IRQn ) ) ); + + return xReturn; +} + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t xReleaseAfterSend ) +{ + uint8_t * buffer = NULL; + + if( pxDescriptor->xDataLength >= PACKET_BUFFER_SIZE ) + { + FreeRTOS_printf( ( "TX buffer length %d over %d\n", pxDescriptor->xDataLength, PACKET_BUFFER_SIZE ) ); + return pdFALSE; + } + + buffer = numaker_eth_get_tx_buf(); + + if( buffer == NULL ) + { + NU_DEBUGF( ( "Eth TX slots are busy\n" ) ); + return pdFALSE; + } + + /* Get exclusive access */ + xSemaphoreTake( xTXMutex, portMAX_DELAY ); + NU_DEBUGF( ( "%s ... buffer=0x%x\r\n", __FUNCTION__, buffer ) ); + /*SendData: pt = pxDescriptor->pucBuffer, length = pxDescriptor->xDataLength */ + memcpy( buffer, pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ); + numaker_eth_trigger_tx( pxDescriptor->xDataLength, NULL ); + /* Call the standard trace macro to log the send event. */ + iptraceNETWORK_INTERFACE_TRANSMIT(); + + if( xReleaseAfterSend != pdFALSE ) + { + /* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet + * buffer. The Ethernet buffer is therefore no longer needed, and must be + * freed for re-use. */ + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + xSemaphoreGive( xTXMutex ); + + return pdTRUE; +} + + +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; + } +} + + +BaseType_t xGetPhyLinkStatus( void ) +{ + BaseType_t xReturn; + + if( numaker_eth_link_ok() ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} + +static void prvPhyTmrCallback( TimerHandle_t xTimer ) +{ + IPStackEvent_t xRxEvent; + static BaseType_t lastLink = pdFAIL; + BaseType_t currLink = xGetPhyLinkStatus(); + + if( currLink != lastLink ) + { + FreeRTOS_printf( ( "PHY Link %s\n", ( currLink ) ? "Up" : "Down" ) ); + + if( !currLink ) + { + xRxEvent.eEventType = eNetworkDownEvent; + xSendEventStructToIPTask( &xRxEvent, 0 ); + } + + lastLink = currLink; + } +} + + +static void prvEMACHandlerTask( void * pvParameters ) +{ + TimeOut_t xPhyTime; + TickType_t xPhyRemTime; + UBaseType_t uxLastMinBufferCount = 0; + UBaseType_t uxCurrentCount; + BaseType_t xResult = 0; + uint32_t ulStatus; + uint16_t dataLength = 0; + uint8_t * buffer = NULL; + NetworkBufferDescriptor_t * pxBufferDescriptor = NULL; + IPStackEvent_t xRxEvent; + const TickType_t xBlockTime = pdMS_TO_TICKS( 5000ul ); + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + /* A possibility to set some additional task properties. */ + + for( ; ; ) + { + uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); + + if( uxLastMinBufferCount != uxCurrentCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinBufferCount = uxCurrentCount; + FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", + uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); + } + + /* No events to process now, wait for the next. */ + ulTaskNotifyTake( pdFALSE, portMAX_DELAY ); + + while( 1 ) + { + /* get received frame */ + if( numaker_eth_get_rx_buf( &dataLength, &buffer ) != 0 ) + { + /* The event was lost because a network buffer was not available. + * Call the standard trace macro to log the occurrence. */ + iptraceETHERNET_RX_EVENT_LOST(); + break; + } + + /* Allocate a network buffer descriptor that points to a buffer + * large enough to hold the received frame. As this is the simple + * rather than efficient example the received data will just be copied + * into this buffer. */ + + pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( PACKET_BUFFER_SIZE, 0 ); + + if( pxBufferDescriptor != NULL ) + { + memcpy( pxBufferDescriptor->pucEthernetBuffer, buffer, dataLength ); + pxBufferDescriptor->xDataLength = dataLength; + } + else + { + numaker_eth_rx_next(); + iptraceETHERNET_RX_EVENT_LOST(); + break; + } + + /* The event about to be sent to the TCP/IP is an Rx event. */ + xRxEvent.eEventType = eNetworkRxEvent; + + /* pvData is used to point to the network buffer descriptor that + * now references the received data. */ + xRxEvent.pvData = ( void * ) pxBufferDescriptor; + + /* Send the data to the TCP/IP stack. */ + if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) + { + /* The buffer could not be sent to the IP task so the buffer + * must be released. */ + vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); + + /* Make a call to the standard trace macro to log the + * occurrence. */ + + iptraceETHERNET_RX_EVENT_LOST(); + } + else + { + /* The message was successfully sent to the TCP/IP stack. + * Call the standard trace macro to log the occurrence. */ + iptraceNETWORK_INTERFACE_RECEIVE(); + } + + numaker_eth_rx_next(); + } + + numaker_eth_trigger_rx(); + } +} + +void xNetworkCallback( char event ) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + switch( event ) + { + case 'R': /*For RX event */ + + /* Wakeup the prvEMACHandlerTask. */ + if( xRxHanderTask != NULL ) + { + vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken ); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + } + + break; + + case 'T': /*For TX event */ + /* ack of tx done, no-op in this stage */ + break; + + default: + break; + } +} diff --git a/FreeRTOS/source/portable/NetworkInterface/M487/m480_eth.c b/FreeRTOS/source/portable/NetworkInterface/M487/m480_eth.c new file mode 100644 index 0000000..1fea773 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/M487/m480_eth.c @@ -0,0 +1,497 @@ +/**************************************************************************//** + * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. + * + * 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. Neither the name of Nuvoton Technology Corp. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + *****************************************************************************/ +#include "FreeRTOS.h" +#include "list.h" +#include "FreeRTOS_IP.h" + +#include "m480_eth.h" + +#define ETH_TRIGGER_RX() do { EMAC->RXST = 0; } while( 0 ) +#define ETH_TRIGGER_TX() do { EMAC->TXST = 0; } while( 0 ) +#define ETH_ENABLE_TX() do { EMAC->CTL |= EMAC_CTL_TXON; } while( 0 ) +#define ETH_ENABLE_RX() do { EMAC->CTL |= EMAC_CTL_RXON; } while( 0 ) +#define ETH_DISABLE_TX() do { EMAC->CTL &= ~EMAC_CTL_TXON; } while( 0 ) +#define ETH_DISABLE_RX() do { EMAC->CTL &= ~EMAC_CTL_RXON; } while( 0 ) + + +struct eth_descriptor rx_desc[ RX_DESCRIPTOR_NUM ] __attribute__( ( aligned( 4 ) ) ); +struct eth_descriptor tx_desc[ TX_DESCRIPTOR_NUM ] __attribute__( ( aligned( 4 ) ) ); +#ifdef __ICCARM__ + #pragma data_alignment=4 + struct eth_descriptor rx_desc[ RX_DESCRIPTOR_NUM ]; + struct eth_descriptor tx_desc[ TX_DESCRIPTOR_NUM ]; + uint8_t rx_buf[ RX_DESCRIPTOR_NUM ][ PACKET_BUFFER_SIZE ]; + uint8_t tx_buf[ TX_DESCRIPTOR_NUM ][ PACKET_BUFFER_SIZE ]; +#else + struct eth_descriptor rx_desc[ RX_DESCRIPTOR_NUM ] __attribute__( ( aligned( 4 ) ) ); + struct eth_descriptor tx_desc[ TX_DESCRIPTOR_NUM ] __attribute__( ( aligned( 4 ) ) ); + uint8_t rx_buf[ RX_DESCRIPTOR_NUM ][ PACKET_BUFFER_SIZE ] __attribute__( ( aligned( 4 ) ) ); + uint8_t tx_buf[ TX_DESCRIPTOR_NUM ][ PACKET_BUFFER_SIZE ] __attribute__( ( aligned( 4 ) ) ); +#endif +struct eth_descriptor volatile * cur_tx_desc_ptr, * cur_rx_desc_ptr, * fin_tx_desc_ptr; + + +/* PTP source clock is 84MHz (Real chip using PLL). Each tick is 11.90ns */ +/* Assume we want to set each tick to 100ns. */ +/* Increase register = (100 * 2^31) / (10^9) = 214.71 =~ 215 = 0xD7 */ +/* Addend register = 2^32 * tick_freq / (84MHz), where tick_freq = (2^31 / 215) MHz */ +/* From above equation, addend register = 2^63 / (84M * 215) ~= 510707200 = 0x1E70C600 */ + + + +static void mdio_write( uint8_t addr, + uint8_t reg, + uint16_t val ) +{ + EMAC->MIIMDAT = val; + EMAC->MIIMCTL = ( addr << EMAC_MIIMCTL_PHYADDR_Pos ) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_WRITE_Msk | EMAC_MIIMCTL_MDCON_Msk; + + while( EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk ) + { + } +} + + +static uint16_t mdio_read( uint8_t addr, + uint8_t reg ) +{ + EMAC->MIIMCTL = ( addr << EMAC_MIIMCTL_PHYADDR_Pos ) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_MDCON_Msk; + + while( EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk ) + { + } + + return( EMAC->MIIMDAT ); +} + +static int reset_phy( void ) +{ + uint16_t reg; + uint32_t delayCnt; + + + mdio_write( CONFIG_PHY_ADDR, MII_BMCR, BMCR_RESET ); + + delayCnt = 2000; + + while( delayCnt-- > 0 ) + { + if( ( mdio_read( CONFIG_PHY_ADDR, MII_BMCR ) & BMCR_RESET ) == 0 ) + { + break; + } + } + + if( delayCnt == 0 ) + { + NU_DEBUGF( ( "Reset phy failed\n" ) ); + return( -1 ); + } + + mdio_write( CONFIG_PHY_ADDR, MII_ADVERTISE, ADVERTISE_CSMA | + ADVERTISE_10HALF | + ADVERTISE_10FULL | + ADVERTISE_100HALF | + ADVERTISE_100FULL ); + + reg = mdio_read( CONFIG_PHY_ADDR, MII_BMCR ); + mdio_write( CONFIG_PHY_ADDR, MII_BMCR, reg | BMCR_ANRESTART ); + + delayCnt = 200000; + + while( delayCnt-- > 0 ) + { + if( ( mdio_read( CONFIG_PHY_ADDR, MII_BMSR ) & ( BMSR_ANEGCOMPLETE | BMSR_LSTATUS ) ) + == ( BMSR_ANEGCOMPLETE | BMSR_LSTATUS ) ) + { + break; + } + } + + if( delayCnt == 0 ) + { + NU_DEBUGF( ( "AN failed. Set to 100 FULL\n" ) ); + EMAC->CTL |= ( EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk ); + return( -1 ); + } + else + { + reg = mdio_read( CONFIG_PHY_ADDR, MII_LPA ); + + if( reg & ADVERTISE_100FULL ) + { + NU_DEBUGF( ( "100 full\n" ) ); + EMAC->CTL |= ( EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk ); + } + else if( reg & ADVERTISE_100HALF ) + { + NU_DEBUGF( ( "100 half\n" ) ); + EMAC->CTL = ( EMAC->CTL & ~EMAC_CTL_FUDUP_Msk ) | EMAC_CTL_OPMODE_Msk; + } + else if( reg & ADVERTISE_10FULL ) + { + NU_DEBUGF( ( "10 full\n" ) ); + EMAC->CTL = ( EMAC->CTL & ~EMAC_CTL_OPMODE_Msk ) | EMAC_CTL_FUDUP_Msk; + } + else + { + NU_DEBUGF( ( "10 half\n" ) ); + EMAC->CTL &= ~( EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk ); + } + } + + FreeRTOS_printf( ( "PHY ID 1:0x%x\r\n", mdio_read( CONFIG_PHY_ADDR, MII_PHYSID1 ) ) ); + FreeRTOS_printf( ( "PHY ID 2:0x%x\r\n", mdio_read( CONFIG_PHY_ADDR, MII_PHYSID2 ) ) ); + + return( 0 ); +} + + +static void init_tx_desc( void ) +{ + uint32_t i; + + + cur_tx_desc_ptr = fin_tx_desc_ptr = &tx_desc[ 0 ]; + + for( i = 0; i < TX_DESCRIPTOR_NUM; i++ ) + { + tx_desc[ i ].status1 = TXFD_PADEN | TXFD_CRCAPP | TXFD_INTEN; + tx_desc[ i ].buf = &tx_buf[ i ][ 0 ]; + tx_desc[ i ].status2 = 0; + tx_desc[ i ].next = &tx_desc[ ( i + 1 ) % TX_DESCRIPTOR_NUM ]; + } + + EMAC->TXDSA = ( unsigned int ) &tx_desc[ 0 ]; +} + +static void init_rx_desc( void ) +{ + uint32_t i; + + + cur_rx_desc_ptr = &rx_desc[ 0 ]; + + for( i = 0; i < RX_DESCRIPTOR_NUM; i++ ) + { + rx_desc[ i ].status1 = OWNERSHIP_EMAC; + rx_desc[ i ].buf = &rx_buf[ i ][ 0 ]; + rx_desc[ i ].status2 = 0; + rx_desc[ i ].next = &rx_desc[ ( i + 1 ) % TX_DESCRIPTOR_NUM ]; + } + + EMAC->RXDSA = ( unsigned int ) &rx_desc[ 0 ]; +} + +void numaker_set_mac_addr( uint8_t * addr ) +{ + EMAC->CAM0M = ( addr[ 0 ] << 24 ) | + ( addr[ 1 ] << 16 ) | + ( addr[ 2 ] << 8 ) | + addr[ 3 ]; + + EMAC->CAM0L = ( addr[ 4 ] << 24 ) | + ( addr[ 5 ] << 16 ); +} + +static void __eth_clk_pin_init() +{ + /* Unlock protected registers */ + SYS_UnlockReg(); + + /* Enable IP clock */ + CLK_EnableModuleClock( EMAC_MODULE ); + + /* Configure MDC clock rate to HCLK / (127 + 1) = 1.25 MHz if system is running at 160 MH */ + CLK_SetModuleClock( EMAC_MODULE, 0, CLK_CLKDIV3_EMAC( 127 ) ); + + /* Update System Core Clock */ + SystemCoreClockUpdate(); + + /*---------------------------------------------------------------------------------------------------------*/ + /* Init I/O Multi-function */ + /*---------------------------------------------------------------------------------------------------------*/ + /* Configure RMII pins */ + SYS->GPA_MFPL &= ~( SYS_GPA_MFPL_PA6MFP_Msk | SYS_GPA_MFPL_PA7MFP_Msk ); + SYS->GPA_MFPL |= SYS_GPA_MFPL_PA6MFP_EMAC_RMII_RXERR | SYS_GPA_MFPL_PA7MFP_EMAC_RMII_CRSDV; + SYS->GPC_MFPL &= ~( SYS_GPC_MFPL_PC6MFP_Msk | SYS_GPC_MFPL_PC7MFP_Msk ); + SYS->GPC_MFPL |= SYS_GPC_MFPL_PC6MFP_EMAC_RMII_RXD1 | SYS_GPC_MFPL_PC7MFP_EMAC_RMII_RXD0; + SYS->GPC_MFPH &= ~SYS_GPC_MFPH_PC8MFP_Msk; + SYS->GPC_MFPH |= SYS_GPC_MFPH_PC8MFP_EMAC_RMII_REFCLK; + SYS->GPE_MFPH &= ~( SYS_GPE_MFPH_PE8MFP_Msk | SYS_GPE_MFPH_PE9MFP_Msk | SYS_GPE_MFPH_PE10MFP_Msk | + SYS_GPE_MFPH_PE11MFP_Msk | SYS_GPE_MFPH_PE12MFP_Msk ); + SYS->GPE_MFPH |= SYS_GPE_MFPH_PE8MFP_EMAC_RMII_MDC | + SYS_GPE_MFPH_PE9MFP_EMAC_RMII_MDIO | + SYS_GPE_MFPH_PE10MFP_EMAC_RMII_TXD0 | + SYS_GPE_MFPH_PE11MFP_EMAC_RMII_TXD1 | + SYS_GPE_MFPH_PE12MFP_EMAC_RMII_TXEN; + + /* Enable high slew rate on all RMII TX output pins */ + PE->SLEWCTL = ( GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN10_Pos ) | + ( GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN11_Pos ) | + ( GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN12_Pos ); + + + /* Lock protected registers */ + SYS_LockReg(); +} + +int numaker_eth_init( uint8_t * mac_addr ) +{ + int ret = 0; + + /* init CLK & pins */ + __eth_clk_pin_init(); + + /* Reset MAC */ + EMAC->CTL = EMAC_CTL_RST_Msk; + + while( EMAC->CTL & EMAC_CTL_RST_Msk ) + { + } + + init_tx_desc(); + init_rx_desc(); + + numaker_set_mac_addr( mac_addr ); /* need to reconfigure hardware address because we just RESET EMAC... */ + + + /* Configure the MAC interrupt enable register. */ + EMAC->INTEN = EMAC_INTEN_RXIEN_Msk | + EMAC_INTEN_TXIEN_Msk | + EMAC_INTEN_RXGDIEN_Msk | + EMAC_INTEN_TXCPIEN_Msk | + EMAC_INTEN_RXBEIEN_Msk | + EMAC_INTEN_TXBEIEN_Msk | + EMAC_INTEN_RDUIEN_Msk | + EMAC_INTEN_TSALMIEN_Msk | + EMAC_INTEN_WOLIEN_Msk; + + /* Configure the MAC control register. */ + EMAC->CTL = EMAC_CTL_STRIPCRC_Msk | EMAC_CTL_RMIIEN_Msk; + + /* Accept packets for us and all broadcast and multicast packets */ + EMAC->CAMCTL = EMAC_CAMCTL_CMPEN_Msk | + EMAC_CAMCTL_AMP_Msk | + EMAC_CAMCTL_ABP_Msk; + EMAC->CAMEN = 1; /* Enable CAM entry 0 */ + + ret = reset_phy(); + + EMAC_ENABLE_RX(); + EMAC_ENABLE_TX(); + return ret; +} + + + +void ETH_halt( void ) +{ + EMAC->CTL &= ~( EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk ); +} + +unsigned int m_status; + +void EMAC_RX_IRQHandler( void ) +{ +/* NU_DEBUGF(("%s ... \r\n", __FUNCTION__)); */ + m_status = EMAC->INTSTS & 0xFFFF; + EMAC->INTSTS = m_status; + + if( m_status & EMAC_INTSTS_RXBEIF_Msk ) + { + /* Shouldn't goes here, unless descriptor corrupted */ + NU_DEBUGF( ( "RX descriptor corrupted \r\n" ) ); + /*return; */ + } + + /* FIX ME: for rx-event, to ack rx_isr into event queue */ + xNetworkCallback( 'R' ); +} + + +void numaker_eth_trigger_rx( void ) +{ + ETH_TRIGGER_RX(); +} + +int numaker_eth_get_rx_buf( uint16_t * len, + uint8_t ** buf ) +{ + unsigned int cur_entry, status; + + cur_entry = EMAC->CRXDSA; + + if( ( cur_entry == ( uint32_t ) cur_rx_desc_ptr ) && ( !( m_status & EMAC_INTSTS_RDUIF_Msk ) ) ) /* cur_entry may equal to cur_rx_desc_ptr if RDU occurred */ + { + return -1; + } + + status = cur_rx_desc_ptr->status1; + + if( status & OWNERSHIP_EMAC ) + { + return -1; + } + + if( status & RXFD_RXGD ) + { + *buf = cur_rx_desc_ptr->buf; + *len = status & 0xFFFF; + } + + return 0; +} + +void numaker_eth_rx_next( void ) +{ + cur_rx_desc_ptr->status1 = OWNERSHIP_EMAC; + cur_rx_desc_ptr = cur_rx_desc_ptr->next; +} + +void EMAC_TX_IRQHandler( void ) +{ + unsigned int cur_entry, status; + + status = EMAC->INTSTS & 0xFFFF0000; + EMAC->INTSTS = status; + + if( status & EMAC_INTSTS_TXBEIF_Msk ) + { + /* Shouldn't goes here, unless descriptor corrupted */ + return; + } + + cur_entry = EMAC->CTXDSA; + + while( cur_entry != ( uint32_t ) fin_tx_desc_ptr ) + { + fin_tx_desc_ptr = fin_tx_desc_ptr->next; + } + + /* FIX ME: for tx-event, no-op at this stage */ + xNetworkCallback( 'T' ); +} + +uint8_t * numaker_eth_get_tx_buf( void ) +{ + if( cur_tx_desc_ptr->status1 & OWNERSHIP_EMAC ) + { + return( NULL ); + } + else + { + return( cur_tx_desc_ptr->buf ); + } +} + +void numaker_eth_trigger_tx( uint16_t length, + void * p ) +{ + struct eth_descriptor volatile * desc; + + cur_tx_desc_ptr->status2 = ( unsigned int ) length; + desc = cur_tx_desc_ptr->next; /* in case TX is transmitting and overwrite next pointer before we can update cur_tx_desc_ptr */ + cur_tx_desc_ptr->status1 |= OWNERSHIP_EMAC; + cur_tx_desc_ptr = desc; + + ETH_TRIGGER_TX(); +} + +int numaker_eth_link_ok( void ) +{ + /* first, a dummy read to latch */ + mdio_read( CONFIG_PHY_ADDR, MII_BMSR ); + + if( mdio_read( CONFIG_PHY_ADDR, MII_BMSR ) & BMSR_LSTATUS ) + { + return 1; + } + + return 0; +} + +/*void numaker_eth_set_cb(eth_callback_t eth_cb, void *userData) */ +/*{ */ +/* nu_eth_txrx_cb = eth_cb; */ +/* nu_userData = userData; */ +/*} */ + +/* Provide ethernet devices with a semi-unique MAC address */ +void numaker_mac_address( uint8_t * mac ) +{ + uint32_t uID1; + /* Fetch word 0 */ + uint32_t word0 = *( uint32_t * ) 0x7F804; /* 2KB Data Flash at 0x7F800 */ + /* Fetch word 1 */ + /* we only want bottom 16 bits of word1 (MAC bits 32-47) */ + /* and bit 9 forced to 1, bit 8 forced to 0 */ + /* Locally administered MAC, reduced conflicts */ + /* http://en.wikipedia.org/wiki/MAC_address */ + uint32_t word1 = *( uint32_t * ) 0x7F800; /* 2KB Data Flash at 0x7F800 */ + + if( word0 == 0xFFFFFFFF ) /* Not burn any mac address at 1st 2 words of Data Flash */ + { + /* with a semi-unique MAC address from the UUID */ + /* Enable FMC ISP function */ + SYS_UnlockReg(); + FMC_Open(); + /* = FMC_ReadUID(0); */ + uID1 = FMC_ReadUID( 1 ); + word1 = ( uID1 & 0x003FFFFF ) | ( ( uID1 & 0x030000 ) << 6 ) >> 8; + word0 = ( ( FMC_ReadUID( 0 ) >> 4 ) << 20 ) | ( ( uID1 & 0xFF ) << 12 ) | ( FMC_ReadUID( 2 ) & 0xFFF ); + /* Disable FMC ISP function */ + FMC_Close(); + /* Lock protected registers */ + SYS_LockReg(); + } + + word1 |= 0x00000200; + word1 &= 0x0000FEFF; + + mac[ 0 ] = ( word1 & 0x0000ff00 ) >> 8; + mac[ 1 ] = ( word1 & 0x000000ff ); + mac[ 2 ] = ( word0 & 0xff000000 ) >> 24; + mac[ 3 ] = ( word0 & 0x00ff0000 ) >> 16; + mac[ 4 ] = ( word0 & 0x0000ff00 ) >> 8; + mac[ 5 ] = ( word0 & 0x000000ff ); + + NU_DEBUGF( ( "mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ] ) ); +} + +void numaker_eth_enable_interrupts( void ) +{ + EMAC->INTEN |= EMAC_INTEN_RXIEN_Msk | + EMAC_INTEN_TXIEN_Msk; + NVIC_EnableIRQ( EMAC_RX_IRQn ); + NVIC_EnableIRQ( EMAC_TX_IRQn ); +} + +void numaker_eth_disable_interrupts( void ) +{ + NVIC_DisableIRQ( EMAC_RX_IRQn ); + NVIC_DisableIRQ( EMAC_TX_IRQn ); +} diff --git a/FreeRTOS/source/portable/NetworkInterface/M487/m480_eth.h b/FreeRTOS/source/portable/NetworkInterface/M487/m480_eth.h new file mode 100644 index 0000000..0431155 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/M487/m480_eth.h @@ -0,0 +1,171 @@ +/**************************************************************************//** + * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. + * + * 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. Neither the name of Nuvoton Technology Corp. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + *****************************************************************************/ +#include "M480.h" +#ifndef _M480_ETH_ + #define _M480_ETH_ + +/* Generic MII registers. */ + + #define MII_BMCR 0x00 /* Basic mode control register */ + #define MII_BMSR 0x01 /* Basic mode status register */ + #define MII_PHYSID1 0x02 /* PHYS ID 1 */ + #define MII_PHYSID2 0x03 /* PHYS ID 2 */ + #define MII_ADVERTISE 0x04 /* Advertisement control reg */ + #define MII_LPA 0x05 /* Link partner ability reg */ + #define MII_EXPANSION 0x06 /* Expansion register */ + #define MII_DCOUNTER 0x12 /* Disconnect counter */ + #define MII_FCSCOUNTER 0x13 /* False carrier counter */ + #define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ + #define MII_RERRCOUNTER 0x15 /* Receive error counter */ + #define MII_SREVISION 0x16 /* Silicon revision */ + #define MII_RESV1 0x17 /* Reserved... */ + #define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ + #define MII_PHYADDR 0x19 /* PHY address */ + #define MII_RESV2 0x1a /* Reserved... */ + #define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ + #define MII_NCONFIG 0x1c /* Network interface config */ + +/* Basic mode control register. */ + #define BMCR_RESV 0x007f /* Unused... */ + #define BMCR_CTST 0x0080 /* Collision test */ + #define BMCR_FULLDPLX 0x0100 /* Full duplex */ + #define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ + #define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ + #define BMCR_PDOWN 0x0800 /* Power down the DP83840 */ + #define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ + #define BMCR_SPEED100 0x2000 /* Select 100Mbps */ + #define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ + #define BMCR_RESET 0x8000 /* Reset the DP83840 */ + +/* Basic mode status register. */ + #define BMSR_ERCAP 0x0001 /* Ext-reg capability */ + #define BMSR_JCD 0x0002 /* Jabber detected */ + #define BMSR_LSTATUS 0x0004 /* Link status */ + #define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ + #define BMSR_RFAULT 0x0010 /* Remote fault detected */ + #define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ + #define BMSR_RESV 0x07c0 /* Unused... */ + #define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ + #define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ + #define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ + #define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ + #define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */ + +/* Advertisement control register. */ + #define ADVERTISE_SLCT 0x001f /* Selector bits */ + #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_100BASE4 0x0200 /* Try for 100mbps 4k packets */ + #define ADVERTISE_RESV 0x1c00 /* Unused... */ + #define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ + #define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ + #define ADVERTISE_NPAGE 0x8000 /* Next page bit */ + + #define RX_DESCRIPTOR_NUM 4 /*8 // Max Number of Rx Frame Descriptors */ + #define TX_DESCRIPTOR_NUM 2 /*4 // Max number of Tx Frame Descriptors */ + + #define PACKET_BUFFER_SIZE 1520 + + #define CONFIG_PHY_ADDR 1 + + +/* Frame Descriptor's Owner bit */ + #define OWNERSHIP_EMAC 0x80000000 /* 1 = EMAC */ +/*#define OWNERSHIP_CPU 0x7fffffff // 0 = CPU */ + + + +/* Rx Frame Descriptor Status */ + #define RXFD_RXGD 0x00100000 /* Receiving Good Packet Received */ + #define RXFD_RTSAS 0x00800000 /* RX Time Stamp Available */ + + +/* Tx Frame Descriptor's Control bits */ + #define TXFD_TTSEN 0x08 /* Tx Time Stamp Enable */ + #define TXFD_INTEN 0x04 /* Interrupt Enable */ + #define TXFD_CRCAPP 0x02 /* Append CRC */ + #define TXFD_PADEN 0x01 /* Padding Enable */ + +/* Tx Frame Descriptor Status */ + #define TXFD_TXCP 0x00080000 /* Transmission Completion */ + #define TXFD_TTSAS 0x08000000 /* TX Time Stamp Available */ + +/* Tx/Rx buffer descriptor structure */ + struct eth_descriptor; + struct eth_descriptor + { + uint32_t status1; + uint8_t * buf; + uint32_t status2; + struct eth_descriptor * next; + #ifdef TIME_STAMPING + uint32_t backup1; + uint32_t backup2; + uint32_t reserved1; + uint32_t reserved2; + #endif + }; + + #ifdef TIME_STAMPING + + #define ETH_TS_ENABLE() do { EMAC->TSCTL = EMAC_TSCTL_TSEN_Msk; } while( 0 ) + #define ETH_TS_START() do { EMAC->TSCTL |= ( EMAC_TSCTL_TSMODE_Msk | EMAC_TSCTL_TSIEN_Msk ); } while( 0 ) + s32_t ETH_settime( u32_t sec, + u32_t nsec ); + s32_t ETH_gettime( u32_t * sec, + u32_t * nsec ); + s32_t ETH_updatetime( u32_t neg, + u32_t sec, + u32_t nsec ); + s32_t ETH_adjtimex( int ppm ); + void ETH_setinc( void ); + + #endif /* ifdef TIME_STAMPING */ + + #ifdef NU_TRACE + #define NU_DEBUGF( x ) { printf x; } + #else + #define NU_DEBUGF( x ) + #endif + + void numaker_set_mac_addr( uint8_t * addr ); + int numaker_eth_init( uint8_t * mac_addr ); + uint8_t * numaker_eth_get_tx_buf( void ); + void numaker_eth_trigger_tx( uint16_t length, + void * p ); + int numaker_eth_get_rx_buf( uint16_t * len, + uint8_t ** buf ); + void numaker_eth_rx_next( void ); + void numaker_eth_trigger_rx( void ); + int numaker_eth_link_ok( void ); + void numaker_mac_address( uint8_t * mac ); + void numaker_eth_enable_interrupts( void ); + void numaker_eth_disable_interrupts( void ); + +#endif /* _M480_ETH_ */ diff --git a/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/NetworkInterface.c new file mode 100644 index 0000000..eb8b2ba --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/NetworkInterface.c @@ -0,0 +1,370 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://github.com/FreeRTOS + * https://www.FreeRTOS.org + */ + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "list.h" +#include "task.h" +#include "semphr.h" + +/* Standard library definitions */ +#include +#include + +/* FreeRTOS+TCP includes. */ +#include +#include +#include +#include + +/* PHY includes. */ +#include "SMM_MPS2.h" +#include "ether_lan9118/smsc9220_eth_drv.h" +#include "ether_lan9118/smsc9220_emac_config.h" + +/* Sets the size of the stack (in words, not bytes) of the task that reads bytes + * from the network. */ +#ifndef nwRX_TASK_STACK_SIZE + #define nwRX_TASK_STACK_SIZE ( configMINIMAL_STACK_SIZE * 2 ) +#endif + +#ifndef nwETHERNET_RX_HANDLER_TASK_PRIORITY + #define nwETHERNET_RX_HANDLER_TASK_PRIORITY ( configMAX_PRIORITIES - 3 ) +#endif + +/* The number of attempts to get a successful call to smsc9220_send_by_chunks() + * when transmitting a packet before giving up. */ +#define niMAX_TX_ATTEMPTS ( 5 ) + +/* Address of ISER and ICER registers in the Cortex-M NVIC. */ +#define nwNVIC_ISER ( *( ( volatile uint32_t * ) 0xE000E100UL ) ) +#define nwNVIC_ICER ( *( ( volatile uint32_t * ) 0xE000E180UL ) ) + +/*-----------------------------------------------------------*/ + +/* + * The task that processes incoming Ethernet packets. It is unblocked by the + * Ethernet Rx interrupt. + */ +static void prvRxTask( void * pvParameters ); + +/* + * Performs low level reads to obtain data from the Ethernet hardware. + */ +static uint32_t prvLowLevelInput( NetworkBufferDescriptor_t ** pxNetworkBuffer ); + +static void prvWait_ms( uint32_t ulSleep_ms ); +static void prvSetMACAddress( void ); + +/*-----------------------------------------------------------*/ + +static const struct smsc9220_eth_dev_cfg_t SMSC9220_ETH_DEV_CFG = +{ + .base = SMSC9220_BASE +}; + +static struct smsc9220_eth_dev_data_t SMSC9220_ETH_DEV_DATA = +{ + .state = 0 +}; + +static const struct smsc9220_eth_dev_t SMSC9220_ETH_DEV = +{ + &( SMSC9220_ETH_DEV_CFG ), + &( SMSC9220_ETH_DEV_DATA ) +}; + +static TaskHandle_t xRxTaskHandle = NULL; + +/*-----------------------------------------------------------*/ + +static void prvWait_ms( uint32_t ulSleep_ms ) +{ + vTaskDelay( pdMS_TO_TICKS( ulSleep_ms ) ); +} +/*-----------------------------------------------------------*/ + +static void prvSetMACAddress( void ) +{ + const struct smsc9220_eth_dev_t * dev = &SMSC9220_ETH_DEV; + uint32_t ucMACLow = 0; + uint32_t ucMACHigh = 0; + + /* Using local variables to make sure the right alignment is used. The MAC + * address is 6 bytes, hence the copy of 4 bytes followed by 2 bytes. */ + memcpy( ( void * ) &ucMACLow, ( void * ) ipLOCAL_MAC_ADDRESS, 4 ); + memcpy( ( void * ) &ucMACHigh, ( void * ) ( ipLOCAL_MAC_ADDRESS + 4 ), 2 ); + + if( smsc9220_mac_regwrite( dev, SMSC9220_MAC_REG_OFFSET_ADDRL, ucMACLow ) != 0 ) + { + smsc9220_mac_regwrite( dev, SMSC9220_MAC_REG_OFFSET_ADDRH, ucMACHigh ); + } +} +/*-----------------------------------------------------------*/ + +static void prvRxTask( void * pvParameters ) +{ + const TickType_t xBlockTime = pdMS_TO_TICKS( 1500UL ); + const struct smsc9220_eth_dev_t * dev = &SMSC9220_ETH_DEV; + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + NetworkBufferDescriptor_t * pxNetworkBuffer = NULL; + uint32_t ulDataRead; + + ( void ) pvParameters; + + for( ; ; ) + { + /* Wait for the Ethernet ISR to receive a packet. */ + ulTaskNotifyTake( pdFALSE, xBlockTime ); + + while( ( ulDataRead = prvLowLevelInput( &pxNetworkBuffer ) ) != 0UL ) + { + xRxEvent.pvData = ( void * ) pxNetworkBuffer; + + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + } + + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL ); /*_RB_ Can this move up. */ + } +} +/*-----------------------------------------------------------*/ + +static uint32_t prvLowLevelInput( NetworkBufferDescriptor_t ** pxNetworkBuffer ) +{ + const struct smsc9220_eth_dev_t * dev = &SMSC9220_ETH_DEV; + const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250UL ); + uint32_t ulMessageLength = 0, ulReceivedBytes = 0; + + ulMessageLength = smsc9220_peek_next_packet_size( dev ); + + if( ulMessageLength != 0 ) + { + *pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ulMessageLength, + xDescriptorWaitTime ); + + if( *pxNetworkBuffer != NULL ) + { + ( *pxNetworkBuffer )->xDataLength = ulMessageLength; + + ulReceivedBytes = smsc9220_receive_by_chunks( dev, + ( char * ) ( ( *pxNetworkBuffer )->pucEthernetBuffer ), + ulMessageLength ); /* not used */ + ( *pxNetworkBuffer )->xDataLength = ulReceivedBytes; + } + else + { + FreeRTOS_printf( ( "pxNetworkBuffer = NULL\n" ) ); + } + } + + return ulReceivedBytes; +} +/*-----------------------------------------------------------*/ + +void EthernetISR( void ) +{ + const struct smsc9220_eth_dev_t * dev = &SMSC9220_ETH_DEV; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + uint32_t ulIRQStatus; + const uint32_t ulRXFifoStatusIRQBit = 1UL << SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL; + extern uint32_t get_irq_status( const struct smsc9220_eth_dev_t * dev ); + + /* Should not enable this interrupt until after the handler task has been + * created. */ + configASSERT( xRxTaskHandle ); + + ulIRQStatus = get_irq_status( dev ); + + if( ( ulIRQStatus & ulRXFifoStatusIRQBit ) != 0 ) + { + /* Unblock the task that will process this interrupt. */ + vTaskNotifyGiveFromISR( xRxTaskHandle, &xHigherPriorityTaskWoken ); + smsc9220_clear_interrupt( dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL ); + + /* Re-enabled by the task that handles the incoming packet. */ /*_RB_ Is this necessary? */ + smsc9220_disable_interrupt( dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL ); + } + + smsc9220_clear_all_interrupts( dev ); + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + const struct smsc9220_eth_dev_t * dev = &SMSC9220_ETH_DEV; + const uint32_t ulEthernetIRQ = 13UL; + BaseType_t xReturn = pdFAIL; + enum smsc9220_error_t err; + + if( xRxTaskHandle == NULL ) + { + /* Task has not been created before. */ + xReturn = xTaskCreate( prvRxTask, + "EMAC", + nwRX_TASK_STACK_SIZE, + NULL, + nwETHERNET_RX_HANDLER_TASK_PRIORITY, + &xRxTaskHandle ); + configASSERT( xReturn != pdFALSE ); + } + + if( xReturn == pdPASS ) + { + err = smsc9220_init( dev, prvWait_ms ); + + if( err != SMSC9220_ERROR_NONE ) + { + FreeRTOS_debug_printf( ( "%s: %d\n", "smsc9220_init failed", err ) ); + xReturn = pdFAIL; + } + else + { + /* Disable the Ethernet interrupt in the NVIC. */ + nwNVIC_ICER = ( uint32_t ) ( 1UL << ( ulEthernetIRQ & 0x1FUL ) ); + + smsc9220_disable_all_interrupts( dev ); + smsc9220_clear_all_interrupts( dev ); + + smsc9220_set_fifo_level_irq( dev, SMSC9220_FIFO_LEVEL_IRQ_RX_STATUS_POS, + SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN ); + smsc9220_set_fifo_level_irq( dev, SMSC9220_FIFO_LEVEL_IRQ_TX_STATUS_POS, + SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN ); + smsc9220_set_fifo_level_irq( dev, SMSC9220_FIFO_LEVEL_IRQ_TX_DATA_POS, + SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MAX ); + prvSetMACAddress(); + + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_GPIO0 ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_GPIO1 ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_GPIO2 ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_FULL ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_RX_DROPPED_FRAME ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_TX_STATUS_FIFO_LEVEL ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_TX_STATUS_FIFO_FULL ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_TX_DATA_FIFO_AVAILABLE ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_TX_DATA_FIFO_OVERRUN ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_TX_ERROR ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_RX_ERROR ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_RX_WATCHDOG_TIMEOUT ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_TX_STATUS_OVERFLOW ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_TX_POWER_MANAGEMENT ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_PHY ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_GP_TIMER ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_RX_DMA ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_TX_IOC ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_RX_DROPPED_FRAME_HALF ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_RX_STOPPED ); + smsc9220_enable_interrupt( dev, SMSC9220_INTERRUPT_TX_STOPPED ); + + /* Enable the Ethernet interrupt in the NVIC. */ + nwNVIC_ISER = ( uint32_t ) ( 1UL << ( ulEthernetIRQ & 0x1FUL ) ); + + xReturn = pdPASS; + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t xReleaseAfterSend ) +{ + const struct smsc9220_eth_dev_t * dev = &SMSC9220_ETH_DEV; + enum smsc9220_error_t error = SMSC9220_ERROR_NONE; + BaseType_t xReturn = pdFAIL, x; + + for( x = 0; x < niMAX_TX_ATTEMPTS; x++ ) + { + if( pxNetworkBuffer->xDataLength < SMSC9220_ETH_MAX_FRAME_SIZE ) + { + error = smsc9220_send_by_chunks( dev, + pxNetworkBuffer->xDataLength, + true, + ( char * ) ( pxNetworkBuffer->pucEthernetBuffer ), + pxNetworkBuffer->xDataLength ); + + if( error == SMSC9220_ERROR_NONE ) + { + xReturn = pdPASS; + break; + } + else + { + xReturn = pdFAIL; + FreeRTOS_debug_printf( ( "Error send by chuncks: %d\n", + error ) ); + } + } + else + { + xReturn = pdFAIL; + FreeRTOS_debug_printf( ( "Packet size too large:%d\n", + pxNetworkBuffer->xDataLength ) ); + break; + } + } + + if( xReleaseAfterSend == pdTRUE ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + /* FIX ME if you want to use BufferAllocation_1.c, which uses statically + * allocated network buffers. */ + + /* Hard force an assert as this driver cannot be used with BufferAllocation_1.c + * without implementing this function. */ + configASSERT( xRxTaskHandle == ( TaskHandle_t ) 1 ); + ( void ) pxNetworkBuffers; +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ + const struct smsc9220_eth_dev_t * dev = &SMSC9220_ETH_DEV; + uint32_t ulPHYBasicStatusValue; + BaseType_t xLinkStatusUp; + + /* Get current status */ + smsc9220_phy_regread( dev, SMSC9220_PHY_REG_OFFSET_BSTATUS, + &ulPHYBasicStatusValue ); + xLinkStatusUp = ( bool ) ( ulPHYBasicStatusValue & + ( 1ul << ( PHY_REG_BSTATUS_LINK_STATUS_INDEX ) ) ); + return xLinkStatusUp; +} diff --git a/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/SMM_MPS2.h b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/SMM_MPS2.h new file mode 100644 index 0000000..54f8846 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/SMM_MPS2.h @@ -0,0 +1,615 @@ +/* + * copyright (c) 2006-2016 ARM Limited + * SPDX-License-Identifier: BSD-3-Clause + * All rights reserved. + * + * 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. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + ******************************************************************************* + * File: smm_mps2.h + * Release: Version 1.1 + *******************************************************************************/ + +#ifndef __SMM_MPS2_H +#define __SMM_MPS2_H + +#if defined( __CC_ARM ) + #pragma anon_unions +#endif + + + +/******************************************************************************/ +/* FPGA System Register declaration */ +/******************************************************************************/ + +typedef struct +{ + volatile uint32_t LED; /* Offset: 0x000 (R/W) LED connections */ + /* [31:2] : Reserved */ + /* [1:0] : LEDs */ + uint32_t RESERVED1[ 1 ]; + volatile uint32_t BUTTON; /* Offset: 0x008 (R/W) Buttons */ + /* [31:2] : Reserved */ + /* [1:0] : Buttons */ + uint32_t RESERVED2[ 1 ]; + volatile uint32_t CLK1HZ; /* Offset: 0x010 (R/W) 1Hz up counter */ + volatile uint32_t CLK100HZ; /* Offset: 0x014 (R/W) 100Hz up counter */ + volatile uint32_t COUNTER; /* Offset: 0x018 (R/W) Cycle Up Counter */ + /* Increments when 32-bit prescale counter reach zero */ + uint32_t RESERVED3[ 1 ]; + volatile uint32_t PRESCALE; /* Offset: 0x020 (R/W) Prescaler */ + /* Bit[31:0] : reload value for prescale counter */ + volatile uint32_t PSCNTR; /* Offset: 0x024 (R/W) 32-bit Prescale counter */ + /* current value of the pre-scaler counter */ + /* The Cycle Up Counter increment when the prescale down counter reach 0 */ + /* The pre-scaler counter is reloaded with PRESCALE after reaching 0. */ + uint32_t RESERVED4[ 9 ]; + volatile uint32_t MISC; /* Offset: 0x04C (R/W) Misc control * / */ + /* [31:10] : Reserved */ + /* [9] : SHIELD_1_SPI_nCS */ + /* [8] : SHIELD_0_SPI_nCS */ + /* [7] : ADC_SPI_nCS */ + /* [6] : CLCD_BL_CTRL */ + /* [5] : CLCD_RD */ + /* [4] : CLCD_RS */ + /* [3] : CLCD_RESET */ + /* [2] : RESERVED */ + /* [1] : SPI_nSS */ + /* [0] : CLCD_CS */ +} MPS2_FPGAIO_TypeDef; + +/* MISC register bit definitions */ + +#define CLCD_CS_Pos 0 +#define CLCD_CS_Msk ( 1UL << CLCD_CS_Pos ) +#define SPI_nSS_Pos 1 +#define SPI_nSS_Msk ( 1UL << SPI_nSS_Pos ) +#define CLCD_RESET_Pos 3 +#define CLCD_RESET_Msk ( 1UL << CLCD_RESET_Pos ) +#define CLCD_RS_Pos 4 +#define CLCD_RS_Msk ( 1UL << CLCD_RS_Pos ) +#define CLCD_RD_Pos 5 +#define CLCD_RD_Msk ( 1UL << CLCD_RD_Pos ) +#define CLCD_BL_Pos 6 +#define CLCD_BL_Msk ( 1UL << CLCD_BL_Pos ) +#define ADC_nCS_Pos 7 +#define ADC_nCS_Msk ( 1UL << ADC_nCS_Pos ) +#define SHIELD_0_nCS_Pos 8 +#define SHIELD_0_nCS_Msk ( 1UL << SHIELD_0_nCS_Pos ) +#define SHIELD_1_nCS_Pos 9 +#define SHIELD_1_nCS_Msk ( 1UL << SHIELD_1_nCS_Pos ) + +/******************************************************************************/ +/* SCC Register declaration */ +/******************************************************************************/ + +typedef struct /* */ +{ + volatile uint32_t CFG_REG0; /* Offset: 0x000 (R/W) Remaps block RAM to ZBT */ + /* [31:1] : Reserved */ + /* [0] 1 : REMAP BlockRam to ZBT */ + volatile uint32_t LEDS; /* Offset: 0x004 (R/W) Controls the MCC user LEDs */ + /* [31:8] : Reserved */ + /* [7:0] : MCC LEDs */ + uint32_t RESERVED0[ 1 ]; + volatile uint32_t SWITCHES; /* Offset: 0x00C (R/ ) Denotes the state of the MCC user switches */ + /* [31:8] : Reserved */ + /* [7:0] : These bits indicate state of the MCC switches */ + volatile uint32_t CFG_REG4; /* Offset: 0x010 (R/ ) Denotes the board revision */ + /* [31:4] : Reserved */ + /* [3:0] : Used by the MCC to pass PCB revision. 0 = A 1 = B */ + uint32_t RESERVED1[ 35 ]; + volatile uint32_t SYS_CFGDATA_RTN; /* Offset: 0x0A0 (R/W) User data register */ + /* [31:0] : Data */ + volatile uint32_t SYS_CFGDATA_OUT; /* Offset: 0x0A4 (R/W) User data register */ + /* [31:0] : Data */ + volatile uint32_t SYS_CFGCTRL; /* Offset: 0x0A8 (R/W) Control register */ + /* [31] : Start (generates interrupt on write to this bit) */ + /* [30] : R/W access */ + /* [29:26] : Reserved */ + /* [25:20] : Function value */ + /* [19:12] : Reserved */ + /* [11:0] : Device (value of 0/1/2 for supported clocks) */ + volatile uint32_t SYS_CFGSTAT; /* Offset: 0x0AC (R/W) Contains status information */ + /* [31:2] : Reserved */ + /* [1] : Error */ + /* [0] : Complete */ + volatile uint32_t RESERVED2[ 20 ]; + volatile uint32_t SCC_DLL; /* Offset: 0x100 (R/W) DLL Lock Register */ + /* [31:24] : DLL LOCK MASK[7:0] - Indicate if the DLL locked is masked */ + /* [23:16] : DLL LOCK MASK[7:0] - Indicate if the DLLs are locked or unlocked */ + /* [15:1] : Reserved */ + /* [0] : This bit indicates if all enabled DLLs are locked */ + uint32_t RESERVED3[ 957 ]; + volatile uint32_t SCC_AID; /* Offset: 0xFF8 (R/ ) SCC AID Register */ + /* [31:24] : FPGA build number */ + /* [23:20] : V2M-MPS2 target board revision (A = 0, B = 1) */ + /* [19:11] : Reserved */ + /* [10] : if "1" SCC_SW register has been implemented */ + /* [9] : if "1" SCC_LED register has been implemented */ + /* [8] : if "1" DLL lock register has been implemented */ + /* [7:0] : number of SCC configuration register */ + volatile uint32_t SCC_ID; /* Offset: 0xFFC (R/ ) Contains information about the FPGA image */ + /* [31:24] : Implementer ID: 0x41 = ARM */ + /* [23:20] : Application note IP variant number */ + /* [19:16] : IP Architecture: 0x4 =AHB */ + /* [15:4] : Primary part number: 386 = AN386 */ + /* [3:0] : Application note IP revision number */ +} MPS2_SCC_TypeDef; + + +/******************************************************************************/ +/* SSP Peripheral declaration */ +/******************************************************************************/ + +typedef struct /* Document DDI0194G_ssp_pl022_r1p3_trm.pdf */ +{ + volatile uint32_t CR0; /* Offset: 0x000 (R/W) Control register 0 */ + /* [31:16] : Reserved */ + /* [15:8] : Serial clock rate */ + /* [7] : SSPCLKOUT phase, applicable to Motorola SPI frame format only */ + /* [6] : SSPCLKOUT polarity, applicable to Motorola SPI frame format only */ + /* [5:4] : Frame format */ + /* [3:0] : Data Size Select */ + volatile uint32_t CR1; /* Offset: 0x004 (R/W) Control register 1 */ + /* [31:4] : Reserved */ + /* [3] : Slave-mode output disable */ + /* [2] : Master or slave mode select */ + /* [1] : Synchronous serial port enable */ + /* [0] : Loop back mode */ + volatile uint32_t DR; /* Offset: 0x008 (R/W) Data register */ + /* [31:16] : Reserved */ + /* [15:0] : Transmit/Receive FIFO */ + volatile uint32_t SR; /* Offset: 0x00C (R/ ) Status register */ + /* [31:5] : Reserved */ + /* [4] : PrimeCell SSP busy flag */ + /* [3] : Receive FIFO full */ + /* [2] : Receive FIFO not empty */ + /* [1] : Transmit FIFO not full */ + /* [0] : Transmit FIFO empty */ + volatile uint32_t CPSR; /* Offset: 0x010 (R/W) Clock prescale register */ + /* [31:8] : Reserved */ + /* [8:0] : Clock prescale divisor */ + volatile uint32_t IMSC; /* Offset: 0x014 (R/W) Interrupt mask set or clear register */ + /* [31:4] : Reserved */ + /* [3] : Transmit FIFO interrupt mask */ + /* [2] : Receive FIFO interrupt mask */ + /* [1] : Receive timeout interrupt mask */ + /* [0] : Receive overrun interrupt mask */ + volatile uint32_t RIS; /* Offset: 0x018 (R/ ) Raw interrupt status register */ + /* [31:4] : Reserved */ + /* [3] : raw interrupt state, prior to masking, of the SSPTXINTR interrupt */ + /* [2] : raw interrupt state, prior to masking, of the SSPRXINTR interrupt */ + /* [1] : raw interrupt state, prior to masking, of the SSPRTINTR interrupt */ + /* [0] : raw interrupt state, prior to masking, of the SSPRORINTR interrupt */ + volatile uint32_t MIS; /* Offset: 0x01C (R/ ) Masked interrupt status register */ + /* [31:4] : Reserved */ + /* [3] : transmit FIFO masked interrupt state, after masking, of the SSPTXINTR interrupt */ + /* [2] : receive FIFO masked interrupt state, after masking, of the SSPRXINTR interrupt */ + /* [1] : receive timeout masked interrupt state, after masking, of the SSPRTINTR interrupt */ + /* [0] : receive over run masked interrupt status, after masking, of the SSPRORINTR interrupt */ + volatile uint32_t ICR; /* Offset: 0x020 ( /W) Interrupt clear register */ + /* [31:2] : Reserved */ + /* [1] : Clears the SSPRTINTR interrupt */ + /* [0] : Clears the SSPRORINTR interrupt */ + volatile uint32_t DMACR; /* Offset: 0x024 (R/W) DMA control register */ + /* [31:2] : Reserved */ + /* [1] : Transmit DMA Enable */ + /* [0] : Receive DMA Enable */ +} MPS2_SSP_TypeDef; + + +/* SSP_CR0 Control register 0 */ +#define SSP_CR0_DSS_Pos 0 /* Data Size Select */ +#define SSP_CR0_DSS_Msk ( 0xF << SSP_CR0_DSS_Pos ) +#define SSP_CR0_FRF_Pos 4 /* Frame Format Select */ +#define SSP_CR0_FRF_Msk ( 3UL << SSP_CR0_FRM_Pos ) +#define SSP_CR0_SPO_Pos 6 /* SSPCLKOUT polarity */ +#define SSP_CR0_SPO_Msk ( 1UL << SSP_CR0_SPO_Pos ) +#define SSP_CR0_SPH_Pos 7 /* SSPCLKOUT phase */ +#define SSP_CR0_SPH_Msk ( 1UL << SSP_CR0_SPH_Pos ) +#define SSP_CR0_SCR_Pos 8 /* Serial Clock Rate (divide) */ +#define SSP_CR0_SCR_Msk ( 0xFF << SSP_CR0_SCR_Pos ) + +#define SSP_CR0_SCR_DFLT 0x0300 /* Serial Clock Rate (divide), default set at 3 */ +#define SSP_CR0_FRF_MOT 0x0000 /* Frame format, Motorola */ +#define SSP_CR0_DSS_8 0x0007 /* Data packet size, 8bits */ +#define SSP_CR0_DSS_16 0x000F /* Data packet size, 16bits */ + +/* SSP_CR1 Control register 1 */ +#define SSP_CR1_LBM_Pos 0 /* Loop Back Mode */ +#define SSP_CR1_LBM_Msk ( 1UL << SSP_CR1_LBM_Pos ) +#define SSP_CR1_SSE_Pos 1 /* Serial port enable */ +#define SSP_CR1_SSE_Msk ( 1UL << SSP_CR1_SSE_Pos ) +#define SSP_CR1_MS_Pos 2 /* Master or Slave mode */ +#define SSP_CR1_MS_Msk ( 1UL << SSP_CR1_MS_Pos ) +#define SSP_CR1_SOD_Pos 3 /* Slave Output mode Disable */ +#define SSP_CR1_SOD_Msk ( 1UL << SSP_CR1_SOD_Pos ) + +/* SSP_SR Status register */ +#define SSP_SR_TFE_Pos 0 /* Transmit FIFO empty */ +#define SSP_SR_TFE_Msk ( 1UL << SSP_SR_TFE_Pos ) +#define SSP_SR_TNF_Pos 1 /* Transmit FIFO not full */ +#define SSP_SR_TNF_Msk ( 1UL << SSP_SR_TNF_Pos ) +#define SSP_SR_RNE_Pos 2 /* Receive FIFO not empty */ +#define SSP_SR_RNE_Msk ( 1UL << SSP_SR_RNE_Pos ) +#define SSP_SR_RFF_Pos 3 /* Receive FIFO full */ +#define SSP_SR_RFF_Msk ( 1UL << SSP_SR_RFF_Pos ) +#define SSP_SR_BSY_Pos 4 /* Busy */ +#define SSP_SR_BSY_Msk ( 1UL << SSP_SR_BSY_Pos ) + +/* SSP_CPSR Clock prescale register */ +#define SSP_CPSR_CPD_Pos 0 /* Clock prescale divisor */ +#define SSP_CPSR_CPD_Msk ( 0xFF << SSP_CPSR_CDP_Pos ) + +#define SSP_CPSR_DFLT 0x0008 /* Clock prescale (use with SCR), default set at 8 */ + +/* SSPIMSC Interrupt mask set and clear register */ +#define SSP_IMSC_RORIM_Pos 0 /* Receive overrun not Masked */ +#define SSP_IMSC_RORIM_Msk ( 1UL << SSP_IMSC_RORIM_Pos ) +#define SSP_IMSC_RTIM_Pos 1 /* Receive timeout not Masked */ +#define SSP_IMSC_RTIM_Msk ( 1UL << SSP_IMSC_RTIM_Pos ) +#define SSP_IMSC_RXIM_Pos 2 /* Receive FIFO not Masked */ +#define SSP_IMSC_RXIM_Msk ( 1UL << SSP_IMSC_RXIM_Pos ) +#define SSP_IMSC_TXIM_Pos 3 /* Transmit FIFO not Masked */ +#define SSP_IMSC_TXIM_Msk ( 1UL << SSP_IMSC_TXIM_Pos ) + +/* SSPRIS Raw interrupt status register */ +#define SSP_RIS_RORRIS_Pos 0 /* Raw Overrun interrupt flag */ +#define SSP_RIS_RORRIS_Msk ( 1UL << SSP_RIS_RORRIS_Pos ) +#define SSP_RIS_RTRIS_Pos 1 /* Raw Timemout interrupt flag */ +#define SSP_RIS_RTRIS_Msk ( 1UL << SSP_RIS_RTRIS_Pos ) +#define SSP_RIS_RXRIS_Pos 2 /* Raw Receive interrupt flag */ +#define SSP_RIS_RXRIS_Msk ( 1UL << SSP_RIS_RXRIS_Pos ) +#define SSP_RIS_TXRIS_Pos 3 /* Raw Transmit interrupt flag */ +#define SSP_RIS_TXRIS_Msk ( 1UL << SSP_RIS_TXRIS_Pos ) + +/* SSPMIS Masked interrupt status register */ +#define SSP_MIS_RORMIS_Pos 0 /* Masked Overrun interrupt flag */ +#define SSP_MIS_RORMIS_Msk ( 1UL << SSP_MIS_RORMIS_Pos ) +#define SSP_MIS_RTMIS_Pos 1 /* Masked Timemout interrupt flag */ +#define SSP_MIS_RTMIS_Msk ( 1UL << SSP_MIS_RTMIS_Pos ) +#define SSP_MIS_RXMIS_Pos 2 /* Masked Receive interrupt flag */ +#define SSP_MIS_RXMIS_Msk ( 1UL << SSP_MIS_RXMIS_Pos ) +#define SSP_MIS_TXMIS_Pos 3 /* Masked Transmit interrupt flag */ +#define SSP_MIS_TXMIS_Msk ( 1UL << SSP_MIS_TXMIS_Pos ) + +/* SSPICR Interrupt clear register */ +#define SSP_ICR_RORIC_Pos 0 /* Clears Overrun interrupt flag */ +#define SSP_ICR_RORIC_Msk ( 1UL << SSP_ICR_RORIC_Pos ) +#define SSP_ICR_RTIC_Pos 1 /* Clears Timemout interrupt flag */ +#define SSP_ICR_RTIC_Msk ( 1UL << SSP_ICR_RTIC_Pos ) + +/* SSPDMACR DMA control register */ +#define SSP_DMACR_RXDMAE_Pos 0 /* Enable Receive FIFO DMA */ +#define SSP_DMACR_RXDMAE_Msk ( 1UL << SSP_DMACR_RXDMAE_Pos ) +#define SSP_DMACR_TXDMAE_Pos 1 /* Enable Transmit FIFO DMA */ +#define SSP_DMACR_TXDMAE_Msk ( 1UL << SSP_DMACR_TXDMAE_Pos ) + +/******************************************************************************/ +/* Audio and Touch Screen (I2C) Peripheral declaration */ +/******************************************************************************/ + +typedef struct +{ + union + { + volatile uint32_t CONTROLS; /* Offset: 0x000 CONTROL Set Register ( /W) */ + volatile uint32_t CONTROL; /* Offset: 0x000 CONTROL Status Register (R/ ) */ + }; + volatile uint32_t CONTROLC; /* Offset: 0x004 CONTROL Clear Register ( /W) */ +} MPS2_I2C_TypeDef; + +#define SDA 1 << 1 +#define SCL 1 << 0 + + +/******************************************************************************/ +/* Audio I2S Peripheral declaration */ +/******************************************************************************/ + +typedef struct +{ + /*!< Offset: 0x000 CONTROL Register (R/W) */ + volatile uint32_t CONTROL; /* CONTROL */ + /* TX Enable */ + /* <0=> TX disabled */ + /* <1=> TX enabled */ + /* TX IRQ Enable */ + /* <0=> TX IRQ disabled */ + /* <1=> TX IRQ enabled */ + /* RX Enable */ + /* <0=> RX disabled */ + /* <1=> RX enabled */ + /* RX IRQ Enable */ + /* <0=> RX IRQ disabled */ + /* <1=> RX IRQ enabled */ + /* TX Buffer Water Level */ + /* <0=> / IRQ triggers when any space available */ + /* <1=> / IRQ triggers when more than 1 space available */ + /* <2=> / IRQ triggers when more than 2 space available */ + /* <3=> / IRQ triggers when more than 3 space available */ + /* <4=> Undefined! */ + /* <5=> Undefined! */ + /* <6=> Undefined! */ + /* <7=> Undefined! */ + /* RX Buffer Water Level */ + /* <0=> Undefined! */ + /* <1=> / IRQ triggers when less than 1 space available */ + /* <2=> / IRQ triggers when less than 2 space available */ + /* <3=> / IRQ triggers when less than 3 space available */ + /* <4=> / IRQ triggers when less than 4 space available */ + /* <5=> Undefined! */ + /* <6=> Undefined! */ + /* <7=> Undefined! */ + /* FIFO reset */ + /* <0=> Normal operation */ + /* <1=> FIFO reset */ + /* Audio Codec reset */ + /* <0=> Normal operation */ + /* <1=> Assert audio Codec reset */ + /*!< Offset: 0x004 STATUS Register (R/ ) */ + volatile uint32_t STATUS; /* STATUS */ + /* TX Buffer alert */ + /* <0=> TX buffer don't need service yet */ + /* <1=> TX buffer need service */ + /* RX Buffer alert */ + /* <0=> RX buffer don't need service yet */ + /* <1=> RX buffer need service */ + /* TX Buffer Empty */ + /* <0=> TX buffer have data */ + /* <1=> TX buffer empty */ + /* TX Buffer Full */ + /* <0=> TX buffer not full */ + /* <1=> TX buffer full */ + /* RX Buffer Empty */ + /* <0=> RX buffer have data */ + /* <1=> RX buffer empty */ + /* RX Buffer Full */ + /* <0=> RX buffer not full */ + /* <1=> RX buffer full */ + union + { + /*!< Offset: 0x008 Error Status Register (R/ ) */ + volatile uint32_t ERROR; /* ERROR */ + /* TX error */ + /* <0=> Okay */ + /* <1=> TX overrun/underrun */ + /* RX error */ + /* <0=> Okay */ + /* <1=> RX overrun/underrun */ + /*!< Offset: 0x008 Error Clear Register ( /W) */ + volatile uint32_t ERRORCLR; /* ERRORCLR */ + /* TX error */ + /* <0=> Okay */ + /* <1=> Clear TX error */ + /* RX error */ + /* <0=> Okay */ + /* <1=> Clear RX error */ + }; + /*!< Offset: 0x00C Divide ratio Register (R/W) */ + volatile uint32_t DIVIDE; /* Divide ratio for Left/Right clock */ + /* TX error (default 0x80) */ + /*!< Offset: 0x010 Transmit Buffer ( /W) */ + volatile uint32_t TXBUF; /* Transmit buffer */ + /* Right channel */ + /* Left channel */ + /*!< Offset: 0x014 Receive Buffer (R/ ) */ + volatile uint32_t RXBUF; /* Receive buffer */ + /* Right channel */ + /* Left channel */ + uint32_t RESERVED1[ 186 ]; + volatile uint32_t ITCR; /* Integration Test Control Register */ + /* ITEN */ + /* <0=> Normal operation */ + /* <1=> Integration Test mode enable */ + volatile uint32_t ITIP1; /* Integration Test Input Register 1 */ + /* SDIN */ + volatile uint32_t ITOP1; /* Integration Test Output Register 1 */ + /* SDOUT */ + /* SCLK */ + /* LRCK */ + /* IRQOUT */ +} MPS2_I2S_TypeDef; + +#define I2S_CONTROL_TXEN_Pos 0 +#define I2S_CONTROL_TXEN_Msk ( 1UL << I2S_CONTROL_TXEN_Pos ) + +#define I2S_CONTROL_TXIRQEN_Pos 1 +#define I2S_CONTROL_TXIRQEN_Msk ( 1UL << I2S_CONTROL_TXIRQEN_Pos ) + +#define I2S_CONTROL_RXEN_Pos 2 +#define I2S_CONTROL_RXEN_Msk ( 1UL << I2S_CONTROL_RXEN_Pos ) + +#define I2S_CONTROL_RXIRQEN_Pos 3 +#define I2S_CONTROL_RXIRQEN_Msk ( 1UL << I2S_CONTROL_RXIRQEN_Pos ) + +#define I2S_CONTROL_TXWLVL_Pos 8 +#define I2S_CONTROL_TXWLVL_Msk ( 7UL << I2S_CONTROL_TXWLVL_Pos ) + +#define I2S_CONTROL_RXWLVL_Pos 12 +#define I2S_CONTROL_RXWLVL_Msk ( 7UL << I2S_CONTROL_RXWLVL_Pos ) +/* FIFO reset*/ +#define I2S_CONTROL_FIFORST_Pos 16 +#define I2S_CONTROL_FIFORST_Msk ( 1UL << I2S_CONTROL_FIFORST_Pos ) +/* Codec reset*/ +#define I2S_CONTROL_CODECRST_Pos 17 +#define I2S_CONTROL_CODECRST_Msk ( 1UL << I2S_CONTROL_CODECRST_Pos ) + +#define I2S_STATUS_TXIRQ_Pos 0 +#define I2S_STATUS_TXIRQ_Msk ( 1UL << I2S_STATUS_TXIRQ_Pos ) + +#define I2S_STATUS_RXIRQ_Pos 1 +#define I2S_STATUS_RXIRQ_Msk ( 1UL << I2S_STATUS_RXIRQ_Pos ) + +#define I2S_STATUS_TXEmpty_Pos 2 +#define I2S_STATUS_TXEmpty_Msk ( 1UL << I2S_STATUS_TXEmpty_Pos ) + +#define I2S_STATUS_TXFull_Pos 3 +#define I2S_STATUS_TXFull_Msk ( 1UL << I2S_STATUS_TXFull_Pos ) + +#define I2S_STATUS_RXEmpty_Pos 4 +#define I2S_STATUS_RXEmpty_Msk ( 1UL << I2S_STATUS_RXEmpty_Pos ) + +#define I2S_STATUS_RXFull_Pos 5 +#define I2S_STATUS_RXFull_Msk ( 1UL << I2S_STATUS_RXFull_Pos ) + +#define I2S_ERROR_TXERR_Pos 0 +#define I2S_ERROR_TXERR_Msk ( 1UL << I2S_ERROR_TXERR_Pos ) + +#define I2S_ERROR_RXERR_Pos 1 +#define I2S_ERROR_RXERR_Msk ( 1UL << I2S_ERROR_RXERR_Pos ) + +/******************************************************************************/ +/* SMSC9220 Register Definitions */ +/******************************************************************************/ + +typedef struct /* SMSC LAN9220 */ +{ + volatile uint32_t RX_DATA_PORT; /* Receive FIFO Ports (offset 0x0) */ + uint32_t RESERVED1[ 0x7 ]; + volatile uint32_t TX_DATA_PORT; /* Transmit FIFO Ports (offset 0x20) */ + uint32_t RESERVED2[ 0x7 ]; + + volatile uint32_t RX_STAT_PORT; /* Receive FIFO status port (offset 0x40) */ + volatile uint32_t RX_STAT_PEEK; /* Receive FIFO status peek (offset 0x44) */ + volatile uint32_t TX_STAT_PORT; /* Transmit FIFO status port (offset 0x48) */ + volatile uint32_t TX_STAT_PEEK; /* Transmit FIFO status peek (offset 0x4C) */ + + volatile uint32_t ID_REV; /* Chip ID and Revision (offset 0x50) */ + volatile uint32_t IRQ_CFG; /* Main Interrupt Configuration (offset 0x54) */ + volatile uint32_t INT_STS; /* Interrupt Status (offset 0x58) */ + volatile uint32_t INT_EN; /* Interrupt Enable Register (offset 0x5C) */ + uint32_t RESERVED3; /* Reserved for future use (offset 0x60) */ + volatile uint32_t BYTE_TEST; /* Read-only byte order testing register 87654321h (offset 0x64) */ + volatile uint32_t FIFO_INT; /* FIFO Level Interrupts (offset 0x68) */ + volatile uint32_t RX_CFG; /* Receive Configuration (offset 0x6C) */ + volatile uint32_t TX_CFG; /* Transmit Configuration (offset 0x70) */ + volatile uint32_t HW_CFG; /* Hardware Configuration (offset 0x74) */ + volatile uint32_t RX_DP_CTL; /* RX Datapath Control (offset 0x78) */ + volatile uint32_t RX_FIFO_INF; /* Receive FIFO Information (offset 0x7C) */ + volatile uint32_t TX_FIFO_INF; /* Transmit FIFO Information (offset 0x80) */ + volatile uint32_t PMT_CTRL; /* Power Management Control (offset 0x84) */ + volatile uint32_t GPIO_CFG; /* General Purpose IO Configuration (offset 0x88) */ + volatile uint32_t GPT_CFG; /* General Purpose Timer Configuration (offset 0x8C) */ + volatile uint32_t GPT_CNT; /* General Purpose Timer Count (offset 0x90) */ + uint32_t RESERVED4; /* Reserved for future use (offset 0x94) */ + volatile uint32_t ENDIAN; /* WORD SWAP Register (offset 0x98) */ + volatile uint32_t FREE_RUN; /* Free Run Counter (offset 0x9C) */ + volatile uint32_t RX_DROP; /* RX Dropped Frames Counter (offset 0xA0) */ + volatile uint32_t MAC_CSR_CMD; /* MAC CSR Synchronizer Command (offset 0xA4) */ + volatile uint32_t MAC_CSR_DATA; /* MAC CSR Synchronizer Data (offset 0xA8) */ + volatile uint32_t AFC_CFG; /* Automatic Flow Control Configuration (offset 0xAC) */ + volatile uint32_t E2P_CMD; /* EEPROM Command (offset 0xB0) */ + volatile uint32_t E2P_DATA; /* EEPROM Data (offset 0xB4) */ +} SMSC9220_TypeDef; + +/* SMSC9220 MAC Registers Indices */ +#define SMSC9220_MAC_CR 0x1 +#define SMSC9220_MAC_ADDRH 0x2 +#define SMSC9220_MAC_ADDRL 0x3 +#define SMSC9220_MAC_HASHH 0x4 +#define SMSC9220_MAC_HASHL 0x5 +#define SMSC9220_MAC_MII_ACC 0x6 +#define SMSC9220_MAC_MII_DATA 0x7 +#define SMSC9220_MAC_FLOW 0x8 +#define SMSC9220_MAC_VLAN1 0x9 +#define SMSC9220_MAC_VLAN2 0xA +#define SMSC9220_MAC_WUFF 0xB +#define SMSC9220_MAC_WUCSR 0xC + +/* SMSC9220 PHY Registers Indices */ +#define SMSC9220_PHY_BCONTROL 0x0 +#define SMSC9220_PHY_BSTATUS 0x1 +#define SMSC9220_PHY_ID1 0x2 +#define SMSC9220_PHY_ID2 0x3 +#define SMSC9220_PHY_ANEG_ADV 0x4 +#define SMSC9220_PHY_ANEG_LPA 0x5 +#define SMSC9220_PHY_ANEG_EXP 0x6 +#define SMSC9220_PHY_MCONTROL 0x17 +#define SMSC9220_PHY_MSTATUS 0x18 +#define SMSC9220_PHY_CSINDICATE 0x27 +#define SMSC9220_PHY_INTSRC 0x29 +#define SMSC9220_PHY_INTMASK 0x30 +#define SMSC9220_PHY_CS 0x31 + +/******************************************************************************/ +/* Peripheral memory map */ +/******************************************************************************/ + +#define MPS2_SSP1_BASE ( 0x40020000ul ) /* User SSP Base Address */ +#define MPS2_SSP0_BASE ( 0x40021000ul ) /* CLCD SSP Base Address */ +#define MPS2_TSC_I2C_BASE ( 0x40022000ul ) /* Touch Screen I2C Base Address */ +#define MPS2_AAIC_I2C_BASE ( 0x40023000ul ) /* Audio Interface I2C Base Address */ +#define MPS2_AAIC_I2S_BASE ( 0x40024000ul ) /* Audio Interface I2S Base Address */ +#define MPS2_SSP2_BASE ( 0x40025000ul ) /* adc SSP Base Address */ +#define MPS2_SSP3_BASE ( 0x40026000ul ) /* Shield 0 SSP Base Address */ +#define MPS2_SSP4_BASE ( 0x40027000ul ) /* Shield 1 SSP Base Address */ +#define MPS2_FPGAIO_BASE ( 0x40028000ul ) /* FPGAIO Base Address */ +#define MPS2_SHIELD0_I2C_BASE ( 0x40029000ul ) /* Shield 0 I2C Base Address */ +#define MPS2_SHIELD1_I2C_BASE ( 0x4002A000ul ) /* Shield 1 I2C Base Address */ +#define MPS2_SCC_BASE ( 0x4002F000ul ) /* SCC Base Address */ + +#ifdef CORTEX_M7 + #define SMSC9220_BASE ( 0xA0000000ul ) /* Ethernet SMSC9220 Base Address */ +#else + #define SMSC9220_BASE ( 0x40200000ul ) /* Ethernet SMSC9220 Base Address */ +#endif + +#define MPS2_VGA_TEXT_BUFFER ( 0x41000000ul ) /* VGA Text Buffer Address */ +#define MPS2_VGA_BUFFER ( 0x41100000ul ) /* VGA Buffer Base Address */ + +/******************************************************************************/ +/* Peripheral declaration */ +/******************************************************************************/ + +#define SMSC9220 ( ( SMSC9220_TypeDef * ) SMSC9220_BASE ) +#define MPS2_TS_I2C ( ( MPS2_I2C_TypeDef * ) MPS2_TSC_I2C_BASE ) +#define MPS2_AAIC_I2C ( ( MPS2_I2C_TypeDef * ) MPS2_AAIC_I2C_BASE ) +#define MPS2_SHIELD0_I2C ( ( MPS2_I2C_TypeDef * ) MPS2_SHIELD0_I2C_BASE ) +#define MPS2_SHIELD1_I2C ( ( MPS2_I2C_TypeDef * ) MPS2_SHIELD1_I2C_BASE ) +#define MPS2_AAIC_I2S ( ( MPS2_I2S_TypeDef * ) MPS2_AAIC_I2S_BASE ) +#define MPS2_FPGAIO ( ( MPS2_FPGAIO_TypeDef * ) MPS2_FPGAIO_BASE ) +#define MPS2_SCC ( ( MPS2_SCC_TypeDef * ) MPS2_SCC_BASE ) +#define MPS2_SSP0 ( ( MPS2_SSP_TypeDef * ) MPS2_SSP0_BASE ) +#define MPS2_SSP1 ( ( MPS2_SSP_TypeDef * ) MPS2_SSP1_BASE ) +#define MPS2_SSP2 ( ( MPS2_SSP_TypeDef * ) MPS2_SSP2_BASE ) +#define MPS2_SSP3 ( ( MPS2_SSP_TypeDef * ) MPS2_SSP3_BASE ) +#define MPS2_SSP4 ( ( MPS2_SSP_TypeDef * ) MPS2_SSP4_BASE ) + +/******************************************************************************/ +/* General Function Definitions */ +/******************************************************************************/ + + +/******************************************************************************/ +/* General MACRO Definitions */ +/******************************************************************************/ + + + +#endif /* __SMM_MPS2_H */ diff --git a/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/smsc9220_emac_config.h b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/smsc9220_emac_config.h new file mode 100644 index 0000000..9f93468 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/smsc9220_emac_config.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Arm Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SMSC9220_EMAC_CONFIG_H +#define SMSC9220_EMAC_CONFIG_H + +#define SMSC9220_HWADDR_SIZE 6U +#define SMSC9220_BUFF_ALIGNMENT 4U + +/* + * Maximum Transfer Unit + * The IEEE 802.3 specification limits the data portion of the 802.3 frame + * to a minimum of 46 and a maximum of 1522 bytes, this is on L2 level. + */ +#define SMSC9220_ETH_MTU_SIZE 1500U +#define SMSC9220_ETH_IF_NAME "smsc9220" +#define SMSC9220_ETH_MAX_FRAME_SIZE 1522U + +/** \brief Defines for receiver thread */ +#define FLAG_RX 1U +#define LINK_STATUS_THREAD_PRIORITY ( osPriorityNormal ) +#define LINK_STATUS_THREAD_STACKSIZE 512U +#define LINK_STATUS_TASK_PERIOD_MS 200U +#define PHY_STATE_LINK_DOWN false +#define PHY_STATE_LINK_UP true +#define CRC_LENGTH_BYTES 4U + +#endif /* SMSC9220_EMAC_CONFIG_H */ diff --git a/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/smsc9220_eth_drv.c b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/smsc9220_eth_drv.c new file mode 100644 index 0000000..2e84f1b --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/smsc9220_eth_drv.c @@ -0,0 +1,1230 @@ +/* + * Copyright (c) 2016-2019 Arm Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "smsc9220_eth_drv.h" +/*#include "CMSIS/SMM_MPS2.h" */ + +/** Setter bit manipulation macro */ +#define SET_BIT( WORD, BIT_INDEX ) ( ( WORD ) |= ( 1U << ( BIT_INDEX ) ) ) +/** Clearing bit manipulation macro */ +#define CLR_BIT( WORD, BIT_INDEX ) ( ( WORD ) &= ~( 1U << ( BIT_INDEX ) ) ) +/** Getter bit manipulation macro */ +#define GET_BIT( WORD, BIT_INDEX ) ( bool ) ( ( ( WORD ) & ( 1U << ( BIT_INDEX ) ) ) ) + +/** Setter bit-field manipulation macro */ +#define SET_BIT_FIELD( WORD, BIT_MASK, BIT_OFFSET, VALUE ) \ + ( WORD |= ( ( VALUE & BIT_MASK ) << BIT_OFFSET ) ) + +/** Clearing bit-field manipulation macro */ +#define CLR_BIT_FIELD( WORD, BIT_MASK, BIT_OFFSET, VALUE ) \ + ( WORD &= ~( ( VALUE & BIT_MASK ) << BIT_OFFSET ) ) + +/** Getter bit-field manipulation macro */ +#define GET_BIT_FIELD( WORD, BIT_MASK, BIT_OFFSET ) \ + ( ( WORD >> BIT_OFFSET ) & BIT_MASK ) + +/** Millisec timeout macros */ +#define RESET_TIME_OUT_MS 10U +#define REG_WRITE_TIME_OUT_MS 50U +#define PHY_RESET_TIME_OUT_MS 100U +#define INIT_FINISH_DELAY 2000U + +struct smsc9220_eth_reg_map_t +{ + volatile uint32_t rx_data_port; /**< Receive FIFO Ports (offset 0x0) */ + uint32_t reserved1[ 0x7 ]; + volatile uint32_t tx_data_port; /**< Transmit FIFO Ports (offset 0x20) */ + uint32_t reserved2[ 0x7 ]; + + volatile uint32_t rx_status_port; /**< Receive FIFO status port (offset 0x40) */ + volatile uint32_t rx_status_peek; /**< Receive FIFO status peek (offset 0x44) */ + volatile uint32_t tx_status_port; /**< Transmit FIFO status port (offset 0x48) */ + volatile uint32_t tx_status_peek; /**< Transmit FIFO status peek (offset 0x4C) */ + + volatile uint32_t id_revision; /**< Chip ID and Revision (offset 0x50) */ + volatile uint32_t irq_cfg; /**< Main Interrupt Config (offset 0x54) */ + volatile uint32_t irq_status; /**< Interrupt Status (offset 0x58) */ + volatile uint32_t irq_enable; /**< Interrupt Enable Register (offset 0x5C) */ + uint32_t reserved3; /**< Reserved for future use (offset 0x60) */ + volatile uint32_t byte_test; /**< Byte order test 87654321h (offset 0x64) */ + volatile uint32_t fifo_level_irq; /**< FIFO Level Interrupts (offset 0x68) */ + volatile uint32_t rx_cfg; /**< Receive Configuration (offset 0x6C) */ + volatile uint32_t tx_cfg; /**< Transmit Configuration (offset 0x70) */ + volatile uint32_t hw_cfg; /**< Hardware Configuration (offset 0x74) */ + volatile uint32_t rx_datapath_ctrl; /**< RX Datapath Control (offset 0x78) */ + volatile uint32_t rx_fifo_inf; /**< Receive FIFO Information (offset 0x7C) */ + volatile uint32_t tx_fifo_inf; /**< Transmit FIFO Information (offset 0x80) */ + volatile uint32_t pmt_ctrl; /**< Power Management Control (offset 0x84) */ + volatile uint32_t gpio_cfg; /**< GPIO Configuration (offset 0x88) */ + volatile uint32_t gptimer_cfg; /**< GP Timer Configuration (offset 0x8C) */ + volatile uint32_t gptimer_count; /**< GP Timer Count (offset 0x90) */ + uint32_t reserved4; /**< Reserved for future use (offset 0x94) */ + volatile uint32_t word_swap; /**< WORD SWAP Register (offset 0x98) */ + volatile uint32_t free_run_counter; /**< Free Run Counter (offset 0x9C) */ + volatile uint32_t rx_dropped_frames; /**< RX Dropped Frames Counter (offset 0xA0) */ + volatile uint32_t mac_csr_cmd; /**< MAC CSR Synchronizer Cmd (offset 0xA4) */ + volatile uint32_t mac_csr_data; /**< MAC CSR Synchronizer Data (offset 0xA8) */ + volatile uint32_t afc_cfg; /**< AutomaticFlow Ctrl Config (offset 0xAC) */ + volatile uint32_t eeprom_cmd; /**< EEPROM Command (offset 0xB0) */ + volatile uint32_t eeprom_data; /**< EEPROM Data (offset 0xB4) */ +}; + +/** + * \brief TX FIFO Size definitions + * + */ +#define TX_STATUS_FIFO_SIZE_BYTES 512U /*< fixed allocation in bytes */ +#define TX_DATA_FIFO_SIZE_KBYTES_POS 16U +#define TX_DATA_FIFO_SIZE_KBYTES_MASK 0x0FU +#define KBYTES_TO_BYTES_MULTIPLIER 1024U + +/** + * \brief FIFO Info definitions + * + */ +#define FIFO_USED_SPACE_MASK 0xFFFFU +#define DATA_FIFO_USED_SPACE_POS 0U +#define STATUS_FIFO_USED_SPACE_POS 16U + +/** + * \brief MAC CSR Synchronizer Command bit definitions + * + */ +enum mac_csr_cmd_bits_t +{ + MAC_CSR_CMD_RW_INDEX = 30U, + MAC_CSR_CMD_BUSY_INDEX = 31U, +}; + +#define MAC_CSR_CMD_ADDRESS_MASK 0x0FU + +/** + * \brief MAC Control register bit definitions + * + */ +enum mac_reg_cr_bits_t +{ + MAC_REG_CR_RXEN_INDEX = 2U, + MAC_REG_CR_TXEN_INDEX = 3U +}; + +/** + * \brief MII Access register bit definitions + * + */ +enum mac_reg_mii_acc_bits_t +{ + MAC_REG_MII_ACC_BUSY_INDEX = 0U, + MAC_REG_MII_ACC_WRITE_INDEX = 1U, + MAC_REG_MII_ACC_PHYADDR_INDEX = 11U +}; +#define MAC_REG_MII_ACC_MII_REG_MASK 0x1FU +#define MAC_REG_MII_ACC_MII_REG_OFFSET 6U + +/** + * \brief Hardware config register bit definitions + * + */ +enum hw_cfg_reg_bits_t +{ + HW_CFG_REG_SRST_INDEX = 0U, + HW_CFG_REG_SRST_TIMEOUT_INDEX = 1U, + HW_CFG_REG_MUST_BE_ONE_INDEX = 20U, +}; +#define HW_CFG_REG_TX_FIFO_SIZE_POS 16U +#define HW_CFG_REG_TX_FIFO_SIZE_MIN 2U /*< Min Tx fifo size in KB */ +#define HW_CFG_REG_TX_FIFO_SIZE_MAX 14U /*< Max Tx fifo size in KB */ +#define HW_CFG_REG_TX_FIFO_SIZE 5U /*< Tx fifo size in KB */ + +/** + * \brief EEPROM command register bit definitions + * + */ +enum eeprom_cmd_reg_bits_t +{ + EEPROM_CMD_REG_BUSY_INDEX = 31U, +}; + +/** + * \brief PHY Basic Control register bit definitions + * + */ +enum phy_reg_bctrl_reg_bits_t +{ + PHY_REG_BCTRL_RST_AUTO_NEG_INDEX = 9U, + PHY_REG_BCTRL_AUTO_NEG_EN_INDEX = 12U, + PHY_REG_BCTRL_RESET_INDEX = 15U +}; + +/** + * \brief TX Command A bit definitions + * + */ +#define TX_CMD_DATA_START_OFFSET_BYTES_POS 16U +#define TX_CMD_DATA_START_OFFSET_BYTES_MASK 0x1FU + + +enum tx_command_a_bits_t +{ + TX_COMMAND_A_LAST_SEGMENT_INDEX = 12U, + TX_COMMAND_A_FIRST_SEGMENT_INDEX = 13U +}; + +#define TX_CMD_PKT_LEN_BYTES_MASK 0x7FFU +#define TX_CMD_PKT_TAG_MASK 0xFFFFU +#define TX_CMD_PKT_TAG_POS 16U + + +/** + * \brief RX Fifo Status bit definitions + * + */ +enum rx_fifo_status_bits_t +{ + RX_FIFO_STATUS_CRC_ERROR_INDEX = 1U, + RX_FIFO_STATUS_DRIBBLING_BIT_INDEX = 2U, + RX_FIFO_STATUS_MII_ERROR_INDEX = 3U, + RX_FIFO_STATUS_REC_WD_TIMEOUT_INDEX = 4U, + RX_FIFO_STATUS_FRAME_TYPE_INDEX = 5U, + RX_FIFO_STATUS_COLLISION_SEEN_INDEX = 6U, + RX_FIFO_STATUS_FRAME_TOO_LONG_INDEX = 7U, + RX_FIFO_STATUS_MULTICAST_INDEX = 10U, + RX_FIFO_STATUS_RUNT_FRAME_INDEX = 11U, + RX_FIFO_STATUS_LENGTH_ERROR_INDEX = 12U, + RX_FIFO_STATUS_BROADCAST_FRAME_INDEX = 13U, + RX_FIFO_STATUS_ERROR_INDEX = 15U, + RX_FIFO_STATUS_FILTERING_FAIL_INDEX = 30U, +}; +#define RX_FIFO_STATUS_PKT_LENGTH_POS 16U +#define RX_FIFO_STATUS_PKT_LENGTH_MASK 0x3FFFU + +/** + * \brief Interrupt Configuration register bit definitions + * + */ +enum irq_cfg_bits_t +{ + IRQ_CFG_IRQ_TYPE = 0U, + IRQ_CFG_IRQ_POL = 4U, + IRQ_CFG_IRQ_EN_INDEX = 8U +}; + +#define IRQ_CFG_INT_DEAS_MASK 0xFFU +#define IRQ_CFG_INT_DEAS_POS 24U +#define IRQ_CFG_INT_DEAS_10US 0x22U + +/** + * \brief Automatic Flow Control register bit definitions + * + */ +enum afc_bits_t +{ + AFC_ANY_INDEX = 0U, + AFC_ADDR_INDEX = 1U, + AFC_BROADCAST_INDEX = 2U, + AFC_MULTICAST_INDEX = 3U +}; + +#define AFC_BACK_DUR_MASK 0x0FU +#define AFC_BACK_DUR_POS 4U +#define AFC_BACK_DUR 4U /**< equal to 50us */ + +#define AFC_LOW_LEVEL_MASK 0xFFU +#define AFC_LOW_LEVEL_POS 8U +#define AFC_LOW_LEVEL 55U /**< specifies in multiple of 64 bytes */ + +#define AFC_HIGH_LEVEL_MASK 0xFFU +#define AFC_HIGH_LEVEL_POS 16U +#define AFC_HIGH_LEVEL 110U /**< specifies in multiple of 64 bytes */ + +/** + * \brief Auto-Negotiation Advertisement register bit definitions + * + */ +enum aneg_bits_t +{ + ANEG_10_BASE_T_INDEX = 5U, /**< 10Mbps able */ + ANEG_10_BASE_T_FULL_DUPL_INDEX = 6U, /**< 10Mbps with full duplex */ + ANEG_100_BASE_TX_INDEX = 7U, /**< 100Mbps Tx able */ + ANEG_100_BASE_TX_FULL_DUPL_INDEX = 8U, /**< 100Mbps with full duplex */ + ANEG_SYMM_PAUSE_INDEX = 10U, /**< Symmetric Pause */ + ANEG_ASYMM_PAUSE_INDEX = 11U /**< Asymmetric Pause */ +}; + +/** + * \brief Transmit Configuration register bit definitions + * + */ +enum tx_cfg_bits_t +{ + TX_CFG_STOP_INDEX = 0U, /*< stop */ + TX_CFG_ON_INDEX = 1U, /*< on */ + TX_CFG_AO_INDEX = 2U, /*< allow overrun */ + TX_CFG_TXD_DUMP_INDEX = 14U, /*< Data FIFO dump */ + TX_CFG_TXS_DUMP_INDEX = 15U /*< Status FIFO dump */ +}; + +/** + * \brief Chip ID definitions + * + */ +/*#define CHIP_ID 0x9220U */ +#define CHIP_ID 0x0118U +#define CHIP_ID_MASK 0xFFFFU +#define CHIP_ID_POS 16U + +/** + * \brief GPIO Configuration register bit definitions + * + */ +enum gpio_cfg_bits_t +{ + GPIO_CFG_GPIO0_PUSHPULL_INDEX = 16U, /*< GPIO0 push/pull or open-drain */ + GPIO_CFG_GPIO1_PUSHPULL_INDEX = 17U, /*< GPIO1 push/pull or open-drain */ + GPIO_CFG_GPIO2_PUSHPULL_INDEX = 18U, /*< GPIO2 push/pull or open-drain */ + GPIO_CFG_GPIO0_LED_INDEX = 28U, /*< GPIO0 set to LED1 */ + GPIO_CFG_GPIO1_LED_INDEX = 29U, /*< GPIO1 set to LED2 */ + GPIO_CFG_GPIO2_LED_INDEX = 30U /*< GPIO2 set to LED3 */ +}; + + +static void fill_tx_fifo( const struct smsc9220_eth_dev_t * dev, + uint8_t * data, + uint32_t size_bytes ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + uint32_t tx_data_port_tmp = 0; + uint8_t * tx_data_port_tmp_ptr = ( uint8_t * ) &tx_data_port_tmp; + + /*If the data length is not a multiple of 4, then the beginning of the first + * DWORD of the TX DATA FIFO gets filled up with zeros and a byte offset is + * set accordingly to guarantee proper transmission.*/ + uint32_t remainder_bytes = ( size_bytes % 4 ); + uint32_t filler_bytes = ( 4 - remainder_bytes ); + + for( uint32_t i = 0; i < 4; i++ ) + { + if( i < filler_bytes ) + { + tx_data_port_tmp_ptr[ i ] = 0; + } + else + { + tx_data_port_tmp_ptr[ i ] = data[ i - filler_bytes ]; + } + } + + register_map->tx_data_port = tx_data_port_tmp; + size_bytes -= remainder_bytes; + data += remainder_bytes; + + while( size_bytes > 0 ) + { + /* Keep the same endianness in data as in the temp variable */ + tx_data_port_tmp_ptr[ 0 ] = data[ 0 ]; + tx_data_port_tmp_ptr[ 1 ] = data[ 1 ]; + tx_data_port_tmp_ptr[ 2 ] = data[ 2 ]; + tx_data_port_tmp_ptr[ 3 ] = data[ 3 ]; + register_map->tx_data_port = tx_data_port_tmp; + data += 4; + size_bytes -= 4; + } +} + +static void empty_rx_fifo( const struct smsc9220_eth_dev_t * dev, + uint8_t * data, + uint32_t size_bytes ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + uint32_t rx_data_port_tmp = 0; + uint8_t * rx_data_port_tmp_ptr = ( uint8_t * ) &rx_data_port_tmp; + + uint32_t remainder_bytes = ( size_bytes % 4 ); + + size_bytes -= remainder_bytes; + + while( size_bytes > 0 ) + { + /* Keep the same endianness in data as in the temp variable */ + rx_data_port_tmp = register_map->rx_data_port; + data[ 0 ] = rx_data_port_tmp_ptr[ 0 ]; + data[ 1 ] = rx_data_port_tmp_ptr[ 1 ]; + data[ 2 ] = rx_data_port_tmp_ptr[ 2 ]; + data[ 3 ] = rx_data_port_tmp_ptr[ 3 ]; + data += 4; + size_bytes -= 4; + } + + rx_data_port_tmp = register_map->rx_data_port; + + for( uint32_t i = 0; i < remainder_bytes; i++ ) + { + data[ i ] = rx_data_port_tmp_ptr[ i ]; + } +} + +enum smsc9220_error_t smsc9220_mac_regread( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_mac_reg_offsets_t regoffset, + uint32_t * data ) +{ + volatile uint32_t val; + uint32_t maccmd = GET_BIT_FIELD( regoffset, + MAC_CSR_CMD_ADDRESS_MASK, 0 ); + uint32_t time_out = REG_WRITE_TIME_OUT_MS; + + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + /* Make sure there's no pending operation */ + if( !( GET_BIT( register_map->mac_csr_cmd, MAC_CSR_CMD_BUSY_INDEX ) ) ) + { + SET_BIT( maccmd, MAC_CSR_CMD_RW_INDEX ); + SET_BIT( maccmd, MAC_CSR_CMD_BUSY_INDEX ); + register_map->mac_csr_cmd = maccmd; /* Start operation */ + + do + { + val = register_map->byte_test; /* A no-op read. */ + ( void ) val; + + if( dev->data->wait_ms ) + { + dev->data->wait_ms( 1 ); + } + + time_out--; + } while( time_out && + GET_BIT( register_map->mac_csr_cmd, MAC_CSR_CMD_BUSY_INDEX ) ); + + if( !time_out ) + { + return SMSC9220_ERROR_TIMEOUT; + } + else + { + *data = register_map->mac_csr_data; + } + } + else + { + return SMSC9220_ERROR_BUSY; + } + + return SMSC9220_ERROR_NONE; +} + +enum smsc9220_error_t smsc9220_mac_regwrite( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_mac_reg_offsets_t regoffset, + uint32_t data ) +{ + volatile uint32_t read = 0; + uint32_t maccmd = GET_BIT_FIELD( regoffset, + MAC_CSR_CMD_ADDRESS_MASK, 0 ); + uint32_t time_out = REG_WRITE_TIME_OUT_MS; + + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + /* Make sure there's no pending operation */ + if( !GET_BIT( register_map->mac_csr_cmd, MAC_CSR_CMD_BUSY_INDEX ) ) + { + register_map->mac_csr_data = data; /* Store data. */ + CLR_BIT( maccmd, MAC_CSR_CMD_RW_INDEX ); + SET_BIT( maccmd, MAC_CSR_CMD_BUSY_INDEX ); + register_map->mac_csr_cmd = maccmd; + + do + { + read = register_map->byte_test; /* A no-op read. */ + ( void ) read; + + if( dev->data->wait_ms ) + { + dev->data->wait_ms( 1 ); + } + + time_out--; + } while( time_out && + ( register_map->mac_csr_cmd & + GET_BIT( register_map->mac_csr_cmd, MAC_CSR_CMD_BUSY_INDEX ) ) ); + + if( !time_out ) + { + return SMSC9220_ERROR_TIMEOUT; + } + } + else + { + return SMSC9220_ERROR_BUSY; + } + + return SMSC9220_ERROR_NONE; +} + +enum smsc9220_error_t smsc9220_phy_regread( const struct smsc9220_eth_dev_t * dev, + enum phy_reg_offsets_t regoffset, + uint32_t * data ) +{ + uint32_t val = 0; + uint32_t phycmd = 0; + uint32_t time_out = REG_WRITE_TIME_OUT_MS; + + if( smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_MII_ACC, &val ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + if( !GET_BIT( val, MAC_REG_MII_ACC_BUSY_INDEX ) ) + { + phycmd = 0; + SET_BIT( phycmd, MAC_REG_MII_ACC_PHYADDR_INDEX ); + SET_BIT_FIELD( phycmd, MAC_REG_MII_ACC_MII_REG_MASK, + MAC_REG_MII_ACC_MII_REG_OFFSET, regoffset ); + CLR_BIT( phycmd, MAC_REG_MII_ACC_WRITE_INDEX ); + SET_BIT( phycmd, MAC_REG_MII_ACC_BUSY_INDEX ); + + if( smsc9220_mac_regwrite( dev, SMSC9220_MAC_REG_OFFSET_MII_ACC, + phycmd ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + val = 0; + + do + { + if( dev->data->wait_ms ) + { + dev->data->wait_ms( 1 ); + } + + time_out--; + + if( smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_MII_ACC, + &val ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + } while( time_out && ( GET_BIT( val, MAC_REG_MII_ACC_BUSY_INDEX ) ) ); + + if( !time_out ) + { + return SMSC9220_ERROR_TIMEOUT; + } + else if( smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_MII_DATA, + data ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + } + else + { + return SMSC9220_ERROR_BUSY; + } + + return SMSC9220_ERROR_NONE; +} + +enum smsc9220_error_t smsc9220_phy_regwrite( const struct smsc9220_eth_dev_t * dev, + enum phy_reg_offsets_t regoffset, + uint32_t data ) +{ + uint32_t val = 0; + uint32_t phycmd = 0; + uint32_t time_out = REG_WRITE_TIME_OUT_MS; + + if( smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_MII_ACC, &val ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + if( !GET_BIT( val, MAC_REG_MII_ACC_BUSY_INDEX ) ) + { + /* Load the data */ + if( smsc9220_mac_regwrite( dev, SMSC9220_MAC_REG_OFFSET_MII_DATA, + ( data & 0xFFFF ) ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + phycmd = 0; + SET_BIT( phycmd, MAC_REG_MII_ACC_PHYADDR_INDEX ); + SET_BIT_FIELD( phycmd, MAC_REG_MII_ACC_MII_REG_MASK, + MAC_REG_MII_ACC_MII_REG_OFFSET, regoffset ); + SET_BIT( phycmd, MAC_REG_MII_ACC_WRITE_INDEX ); + SET_BIT( phycmd, MAC_REG_MII_ACC_BUSY_INDEX ); + + /* Start operation */ + if( smsc9220_mac_regwrite( dev, SMSC9220_MAC_REG_OFFSET_MII_ACC, + phycmd ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + phycmd = 0; + + do + { + if( dev->data->wait_ms ) + { + dev->data->wait_ms( 1 ); + } + + time_out--; + + if( smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_MII_ACC, + &phycmd ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + } while( time_out && GET_BIT( phycmd, 0 ) ); + + if( !time_out ) + { + return SMSC9220_ERROR_TIMEOUT; + } + } + else + { + return SMSC9220_ERROR_BUSY; + } + + return SMSC9220_ERROR_NONE; +} + +uint32_t smsc9220_read_id( const struct smsc9220_eth_dev_t * dev ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + return register_map->id_revision; +} + +enum smsc9220_error_t smsc9220_soft_reset( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t time_out = RESET_TIME_OUT_MS; + + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + /* Soft reset */ + SET_BIT( register_map->hw_cfg, HW_CFG_REG_SRST_INDEX ); + + do + { + if( dev->data->wait_ms ) + { + dev->data->wait_ms( 1 ); + } + + time_out--; + } while( time_out && + GET_BIT( register_map->hw_cfg, HW_CFG_REG_SRST_TIMEOUT_INDEX ) ); + + if( !time_out ) + { + return SMSC9220_ERROR_TIMEOUT; + } + + return SMSC9220_ERROR_NONE; +} + +void smsc9220_set_txfifo( const struct smsc9220_eth_dev_t * dev, + uint32_t val ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + if( ( val >= HW_CFG_REG_TX_FIFO_SIZE_MIN ) && + ( val <= HW_CFG_REG_TX_FIFO_SIZE_MAX ) ) + { + register_map->hw_cfg = val << HW_CFG_REG_TX_FIFO_SIZE_POS; + } +} + +enum smsc9220_error_t smsc9220_set_fifo_level_irq( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_fifo_level_irq_pos_t irq_level_pos, + uint32_t level ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + enum smsc9220_error_t eReturn = SMSC9220_ERROR_PARAM; + + #if ( SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN > 0 ) + if( level < SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN ) + { + /* An error will be returned. */ + } + else + #endif + + if( level <= SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MAX ) + { + CLR_BIT_FIELD( register_map->fifo_level_irq, SMSC9220_FIFO_LEVEL_IRQ_MASK, + irq_level_pos, SMSC9220_FIFO_LEVEL_IRQ_MASK ); + SET_BIT_FIELD( register_map->fifo_level_irq, SMSC9220_FIFO_LEVEL_IRQ_MASK, + irq_level_pos, level ); + eReturn = SMSC9220_ERROR_NONE; + } + + return eReturn; +} + +enum smsc9220_error_t smsc9220_wait_eeprom( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t time_out = REG_WRITE_TIME_OUT_MS; + + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + do + { + if( dev->data->wait_ms ) + { + dev->data->wait_ms( 1 ); + } + + time_out--; + } while( time_out && + GET_BIT( register_map->eeprom_cmd, EEPROM_CMD_REG_BUSY_INDEX ) ); + + if( !time_out ) + { + return SMSC9220_ERROR_TIMEOUT; + } + + return SMSC9220_ERROR_NONE; +} + +void smsc9220_init_irqs( const struct smsc9220_eth_dev_t * dev ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + smsc9220_disable_all_interrupts( dev ); + smsc9220_clear_all_interrupts( dev ); + + /* Set IRQ deassertion interval */ + SET_BIT_FIELD( register_map->irq_cfg, IRQ_CFG_INT_DEAS_MASK, + IRQ_CFG_INT_DEAS_POS, IRQ_CFG_INT_DEAS_10US ); + + /* enable interrupts */ + SET_BIT( register_map->irq_cfg, IRQ_CFG_IRQ_TYPE ); + SET_BIT( register_map->irq_cfg, IRQ_CFG_IRQ_POL ); + SET_BIT( register_map->irq_cfg, IRQ_CFG_IRQ_EN_INDEX ); +} + +enum smsc9220_error_t smsc9220_check_phy( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t phyid1 = 0; + uint32_t phyid2 = 0; + + if( smsc9220_phy_regread( dev, SMSC9220_PHY_REG_OFFSET_ID1, &phyid1 ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + if( smsc9220_phy_regread( dev, SMSC9220_PHY_REG_OFFSET_ID2, &phyid2 ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + if( ( ( phyid1 == 0xFFFF ) && ( phyid2 == 0xFFFF ) ) || + ( ( phyid1 == 0x0 ) && ( phyid2 == 0x0 ) ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + return SMSC9220_ERROR_NONE; +} + +enum smsc9220_error_t smsc9220_reset_phy( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t read = 0; + + if( smsc9220_phy_regread( dev, SMSC9220_PHY_REG_OFFSET_BCTRL, &read ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + SET_BIT( read, PHY_REG_BCTRL_RESET_INDEX ); + + if( smsc9220_phy_regwrite( dev, SMSC9220_PHY_REG_OFFSET_BCTRL, read ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + return SMSC9220_ERROR_NONE; +} + +void smsc9220_advertise_cap( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t aneg_adv = 0; + + smsc9220_phy_regread( dev, SMSC9220_PHY_REG_OFFSET_ANEG_ADV, &aneg_adv ); + + SET_BIT( aneg_adv, ANEG_10_BASE_T_INDEX ); + SET_BIT( aneg_adv, ANEG_10_BASE_T_FULL_DUPL_INDEX ); + SET_BIT( aneg_adv, ANEG_100_BASE_TX_INDEX ); + SET_BIT( aneg_adv, ANEG_100_BASE_TX_FULL_DUPL_INDEX ); + SET_BIT( aneg_adv, ANEG_SYMM_PAUSE_INDEX ); + SET_BIT( aneg_adv, ANEG_ASYMM_PAUSE_INDEX ); + + smsc9220_phy_regwrite( dev, SMSC9220_PHY_REG_OFFSET_ANEG_ADV, aneg_adv ); +} + +void smsc9220_enable_xmit( const struct smsc9220_eth_dev_t * dev ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + SET_BIT( register_map->tx_cfg, TX_CFG_ON_INDEX ); +} + +void smsc9220_disable_xmit( const struct smsc9220_eth_dev_t * dev ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + CLR_BIT( register_map->tx_cfg, TX_CFG_ON_INDEX ); +} + +void smsc9220_enable_mac_xmit( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t mac_cr = 0; + + smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_CR, &mac_cr ); + + SET_BIT( mac_cr, MAC_REG_CR_TXEN_INDEX ); + + smsc9220_mac_regwrite( dev, SMSC9220_MAC_REG_OFFSET_CR, mac_cr ); +} + +void smsc9220_disable_mac_xmit( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t mac_cr = 0; + + smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_CR, &mac_cr ); + + CLR_BIT( mac_cr, MAC_REG_CR_TXEN_INDEX ); + + smsc9220_mac_regwrite( dev, SMSC9220_MAC_REG_OFFSET_CR, mac_cr ); +} + +void smsc9220_enable_mac_recv( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t mac_cr = 0; + + smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_CR, &mac_cr ); + + SET_BIT( mac_cr, MAC_REG_CR_RXEN_INDEX ); + + smsc9220_mac_regwrite( dev, SMSC9220_MAC_REG_OFFSET_CR, mac_cr ); +} + +void smsc9220_disable_mac_recv( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t mac_cr = 0; + + smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_CR, &mac_cr ); + + CLR_BIT( mac_cr, MAC_REG_CR_RXEN_INDEX ); + + smsc9220_mac_regwrite( dev, SMSC9220_MAC_REG_OFFSET_CR, mac_cr ); +} + +int smsc9220_check_id( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t id = smsc9220_read_id( dev ); + + return( ( GET_BIT_FIELD( id, CHIP_ID_MASK, CHIP_ID_POS ) == CHIP_ID ) ? 0 : 1 ); +} + +void smsc9220_enable_interrupt( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_interrupt_source source ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + SET_BIT( register_map->irq_enable, source ); +} + +void smsc9220_disable_interrupt( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_interrupt_source source ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + CLR_BIT( register_map->irq_enable, source ); +} + +void smsc9220_disable_all_interrupts( const struct smsc9220_eth_dev_t * dev ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + register_map->irq_enable = 0; +} + +void smsc9220_clear_interrupt( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_interrupt_source source ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + SET_BIT( register_map->irq_status, source ); +} + +uint32_t get_irq_status( const struct smsc9220_eth_dev_t * dev ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + return register_map->irq_status; +} + + +void smsc9220_clear_all_interrupts( const struct smsc9220_eth_dev_t * dev ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + register_map->irq_status = UINT32_MAX; +} + +int smsc9220_get_interrupt( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_interrupt_source source ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + return GET_BIT( register_map->irq_status, source ); +} + +void smsc9220_establish_link( const struct smsc9220_eth_dev_t * dev ) +{ + uint32_t bcr = 0; + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + smsc9220_phy_regread( dev, SMSC9220_PHY_REG_OFFSET_BCTRL, &bcr ); + SET_BIT( bcr, PHY_REG_BCTRL_AUTO_NEG_EN_INDEX ); + SET_BIT( bcr, PHY_REG_BCTRL_RST_AUTO_NEG_INDEX ); + smsc9220_phy_regwrite( dev, SMSC9220_PHY_REG_OFFSET_BCTRL, bcr ); + + SET_BIT( register_map->hw_cfg, HW_CFG_REG_MUST_BE_ONE_INDEX ); +} + +enum smsc9220_error_t smsc9220_read_mac_address( const struct smsc9220_eth_dev_t * dev, + char * mac ) +{ + uint32_t mac_low = 0; + uint32_t mac_high = 0; + + if( !mac ) + { + return SMSC9220_ERROR_PARAM; + } + + if( smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_ADDRH, &mac_high ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + if( smsc9220_mac_regread( dev, SMSC9220_MAC_REG_OFFSET_ADDRL, &mac_low ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + mac[ 0 ] = mac_low & 0xFF; + mac[ 1 ] = ( mac_low >> 8 ) & 0xFF; + mac[ 2 ] = ( mac_low >> 16 ) & 0xFF; + mac[ 3 ] = ( mac_low >> 24 ) & 0xFF; + mac[ 4 ] = mac_high & 0xFF; + mac[ 5 ] = ( mac_high >> 8 ) & 0xFF; + + return SMSC9220_ERROR_NONE; +} + +uint32_t smsc9220_get_tx_data_fifo_size( const struct smsc9220_eth_dev_t * dev ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + uint32_t tx_fifo_size = + GET_BIT_FIELD( register_map->hw_cfg, + TX_DATA_FIFO_SIZE_KBYTES_MASK, + TX_DATA_FIFO_SIZE_KBYTES_POS ) * KBYTES_TO_BYTES_MULTIPLIER; + + return( tx_fifo_size - TX_STATUS_FIFO_SIZE_BYTES ); +} + +enum smsc9220_error_t smsc9220_init( const struct smsc9220_eth_dev_t * dev, + void ( *wait_ms_function )( uint32_t ) ) +{ + uint32_t phyreset = 0; + enum smsc9220_error_t error = SMSC9220_ERROR_NONE; + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + if( !wait_ms_function ) + { + return SMSC9220_ERROR_PARAM; + } + + dev->data->wait_ms = wait_ms_function; + + error = smsc9220_check_id( dev ); + + if( error != SMSC9220_ERROR_NONE ) + { + return error; + } + + error = smsc9220_soft_reset( dev ); + + if( error != SMSC9220_ERROR_NONE ) + { + return error; + } + + smsc9220_set_txfifo( dev, HW_CFG_REG_TX_FIFO_SIZE ); + + SET_BIT_FIELD( register_map->afc_cfg, AFC_BACK_DUR_MASK, + AFC_BACK_DUR_POS, AFC_BACK_DUR ); + SET_BIT_FIELD( register_map->afc_cfg, AFC_LOW_LEVEL_MASK, + AFC_LOW_LEVEL_POS, AFC_LOW_LEVEL ); + SET_BIT_FIELD( register_map->afc_cfg, AFC_HIGH_LEVEL_MASK, + AFC_HIGH_LEVEL_POS, AFC_HIGH_LEVEL ); + + error = smsc9220_wait_eeprom( dev ); + + if( error != SMSC9220_ERROR_NONE ) + { + return error; + } + + /* Configure GPIOs as LED outputs. */ + register_map->gpio_cfg = 0; + SET_BIT( register_map->gpio_cfg, GPIO_CFG_GPIO0_PUSHPULL_INDEX ); + SET_BIT( register_map->gpio_cfg, GPIO_CFG_GPIO1_PUSHPULL_INDEX ); + SET_BIT( register_map->gpio_cfg, GPIO_CFG_GPIO2_PUSHPULL_INDEX ); + SET_BIT( register_map->gpio_cfg, GPIO_CFG_GPIO0_LED_INDEX ); + SET_BIT( register_map->gpio_cfg, GPIO_CFG_GPIO1_LED_INDEX ); + SET_BIT( register_map->gpio_cfg, GPIO_CFG_GPIO2_LED_INDEX ); + + smsc9220_init_irqs( dev ); + + /* Configure MAC addresses here if needed. */ + + error = smsc9220_check_phy( dev ); + + if( error != SMSC9220_ERROR_NONE ) + { + return error; + } + + error = smsc9220_reset_phy( dev ); + + if( error != SMSC9220_ERROR_NONE ) + { + return error; + } + + if( dev->data->wait_ms ) + { + dev->data->wait_ms( PHY_RESET_TIME_OUT_MS ); + } + + /* Checking whether phy reset completed successfully.*/ + error = smsc9220_phy_regread( dev, SMSC9220_PHY_REG_OFFSET_BCTRL, + &phyreset ); + + if( error != SMSC9220_ERROR_NONE ) + { + return error; + } + + if( GET_BIT( phyreset, PHY_REG_BCTRL_RESET_INDEX ) ) + { + return SMSC9220_ERROR_INTERNAL; + } + + smsc9220_advertise_cap( dev ); + smsc9220_establish_link( dev ); + + smsc9220_enable_mac_xmit( dev ); + smsc9220_enable_xmit( dev ); + smsc9220_enable_mac_recv( dev ); + + /* This sleep is compulsory otherwise txmit/receive will fail. */ + if( dev->data->wait_ms ) + { + dev->data->wait_ms( INIT_FINISH_DELAY ); + } + + dev->data->state = 1; + + return SMSC9220_ERROR_NONE; +} + +enum smsc9220_error_t smsc9220_send_by_chunks( const struct smsc9220_eth_dev_t * dev, + uint32_t total_payload_length, + bool is_new_packet, + const char * data, + uint32_t current_size ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + bool is_first_segment = false; + bool is_last_segment = false; + uint32_t txcmd_a, txcmd_b = 0; + uint32_t tx_buffer_free_space = 0; + volatile uint32_t xmit_stat = 0; + + if( !data ) + { + return SMSC9220_ERROR_PARAM; + } + + if( is_new_packet ) + { + is_first_segment = true; + dev->data->ongoing_packet_length = total_payload_length; + dev->data->ongoing_packet_length_sent = 0; + } + else if( ( dev->data->ongoing_packet_length != total_payload_length ) || + ( dev->data->ongoing_packet_length_sent >= total_payload_length ) ) + { + return SMSC9220_ERROR_PARAM; + } + + /* Would next chunk fit into buffer? */ + tx_buffer_free_space = GET_BIT_FIELD( register_map->tx_fifo_inf, + FIFO_USED_SPACE_MASK, + DATA_FIFO_USED_SPACE_POS ); + + if( current_size > tx_buffer_free_space ) + { + return SMSC9220_ERROR_INTERNAL; /* Not enough space in FIFO */ + } + + if( ( dev->data->ongoing_packet_length_sent + current_size ) == + total_payload_length ) + { + is_last_segment = true; + } + + txcmd_a = 0; + txcmd_b = 0; + + if( is_last_segment ) + { + SET_BIT( txcmd_a, TX_COMMAND_A_LAST_SEGMENT_INDEX ); + } + + if( is_first_segment ) + { + SET_BIT( txcmd_a, TX_COMMAND_A_FIRST_SEGMENT_INDEX ); + } + + uint32_t data_start_offset_bytes = ( 4 - ( current_size % 4 ) ); + + SET_BIT_FIELD( txcmd_a, TX_CMD_PKT_LEN_BYTES_MASK, 0, current_size ); + SET_BIT_FIELD( txcmd_a, TX_CMD_DATA_START_OFFSET_BYTES_MASK, + TX_CMD_DATA_START_OFFSET_BYTES_POS, + data_start_offset_bytes ); + + SET_BIT_FIELD( txcmd_b, TX_CMD_PKT_LEN_BYTES_MASK, 0, current_size ); + SET_BIT_FIELD( txcmd_b, TX_CMD_PKT_TAG_MASK, TX_CMD_PKT_TAG_POS, + current_size ); + + register_map->tx_data_port = txcmd_a; + register_map->tx_data_port = txcmd_b; + + fill_tx_fifo( dev, ( uint8_t * ) data, current_size ); + + if( is_last_segment ) + { + /* Pop status port for error check */ + xmit_stat = register_map->tx_status_port; + ( void ) xmit_stat; + } + + dev->data->ongoing_packet_length_sent += current_size; + return SMSC9220_ERROR_NONE; +} + +uint32_t smsc9220_get_rxfifo_data_used_space( const struct + smsc9220_eth_dev_t * dev ) +{ + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + + return GET_BIT_FIELD( register_map->rx_fifo_inf, FIFO_USED_SPACE_MASK, + DATA_FIFO_USED_SPACE_POS ); +} + +uint32_t smsc9220_receive_by_chunks( const struct smsc9220_eth_dev_t * dev, + char * data, + uint32_t dlen ) +{ + uint32_t packet_length_byte = 0; + + if( !data ) + { + return 0; /* Invalid input parameter, cannot read */ + } + + packet_length_byte = dlen; /*_RB_ Hard set to length read from peek register. */ + dev->data->current_rx_size_words = packet_length_byte; + + empty_rx_fifo( dev, ( uint8_t * ) data, packet_length_byte ); + dev->data->current_rx_size_words = 0; + return packet_length_byte; +} + +uint32_t smsc9220_peek_next_packet_size( const struct + smsc9220_eth_dev_t * dev ) +{ + uint32_t packet_size = 0; + struct smsc9220_eth_reg_map_t * register_map = + ( struct smsc9220_eth_reg_map_t * ) dev->cfg->base; + volatile uint32_t rx_status_from_peek = 0; + + if( smsc9220_get_rxfifo_data_used_space( dev ) ) + { + rx_status_from_peek = register_map->rx_status_peek; + /* Warning: try reading from port as peek is often zero. */ + rx_status_from_peek = register_map->rx_status_port; + + packet_size = GET_BIT_FIELD( rx_status_from_peek, + RX_FIFO_STATUS_PKT_LENGTH_MASK, + RX_FIFO_STATUS_PKT_LENGTH_POS ); + } + else + { + rx_status_from_peek = -1; + } + + return packet_size; +} diff --git a/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/smsc9220_eth_drv.h b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/smsc9220_eth_drv.h new file mode 100644 index 0000000..ea0e872 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/MPS2_AN385/ether_lan9118/smsc9220_eth_drv.h @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2016-2019 Arm Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file smsc9220_drv.h + * \brief Generic driver for SMSC9220 Ethernet controller + */ + +#ifndef __SMSC9220_ETH_H__ + #define __SMSC9220_ETH_H__ + + #include + #include + + #ifdef __cplusplus + extern "C" { + #endif + +/** SMSC9220 device configuration structure */ + struct smsc9220_eth_dev_cfg_t + { + const uint32_t base; /*!< SMSC9220 base address */ + }; + +/** SMSC9220 device data structure */ + struct smsc9220_eth_dev_data_t + { + uint32_t state; /*!< Indicates if the SMSC9220 driver + * is initialized and enabled */ + void (* wait_ms) ( uint32_t ); /*!< function pointer to system's millisec delay + * function, will be used for delays */ + uint32_t ongoing_packet_length; /*!< size in bytes of the packet + * is being sent */ + uint32_t ongoing_packet_length_sent; /*!< size in bytes of the packet + * has been sent */ + uint32_t current_rx_size_words; /*!< Data length in words, + * currently is being read */ + }; + +/** SMSC9220 device structure */ + struct smsc9220_eth_dev_t + { + const struct smsc9220_eth_dev_cfg_t * const cfg; /*!< configuration */ + struct smsc9220_eth_dev_data_t * const data; /*!< data */ + }; + +/** + * \brief Error code definitions + * + */ + enum smsc9220_error_t + { + SMSC9220_ERROR_NONE = 0U, /*!< no error */ + SMSC9220_ERROR_TIMEOUT = 1U, /*!< timeout */ + SMSC9220_ERROR_BUSY = 2U, /*!< no error */ + SMSC9220_ERROR_PARAM = 3U, /*!< invalid parameter */ + SMSC9220_ERROR_INTERNAL = 4U /*!< internal error */ + }; + +/** + * \brief Interrupt source definitions + * + */ + enum smsc9220_interrupt_source + { + SMSC9220_INTERRUPT_GPIO0 = 0U, + SMSC9220_INTERRUPT_GPIO1 = 1U, + SMSC9220_INTERRUPT_GPIO2 = 2U, + SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL = 3U, + SMSC9220_INTERRUPT_RX_STATUS_FIFO_FULL = 4U, + /* 5 Reserved according to Datasheet */ + SMSC9220_INTERRUPT_RX_DROPPED_FRAME = 6U, + SMSC9220_INTERRUPT_TX_STATUS_FIFO_LEVEL = 7U, + SMSC9220_INTERRUPT_TX_STATUS_FIFO_FULL = 8U, + SMSC9220_INTERRUPT_TX_DATA_FIFO_AVAILABLE = 9U, + SMSC9220_INTERRUPT_TX_DATA_FIFO_OVERRUN = 10U, + /* 11, 12 Reserved according to Datasheet */ + SMSC9220_INTERRUPT_TX_ERROR = 13U, + SMSC9220_INTERRUPT_RX_ERROR = 14U, + SMSC9220_INTERRUPT_RX_WATCHDOG_TIMEOUT = 15U, + SMSC9220_INTERRUPT_TX_STATUS_OVERFLOW = 16U, + SMSC9220_INTERRUPT_TX_POWER_MANAGEMENT = 17U, + SMSC9220_INTERRUPT_PHY = 18U, + SMSC9220_INTERRUPT_GP_TIMER = 19U, + SMSC9220_INTERRUPT_RX_DMA = 20U, + SMSC9220_INTERRUPT_TX_IOC = 21U, + /* 22 Reserved according to Datasheet*/ + SMSC9220_INTERRUPT_RX_DROPPED_FRAME_HALF = 23U, + SMSC9220_INTERRUPT_RX_STOPPED = 24U, + SMSC9220_INTERRUPT_TX_STOPPED = 25U, + /* 26 - 30 Reserved according to Datasheet*/ + SMSC9220_INTERRUPT_SW = 31U + }; + +/** + * \brief MAC register offset definitions + * + */ + enum smsc9220_mac_reg_offsets_t + { + SMSC9220_MAC_REG_OFFSET_CR = 0x1U, + SMSC9220_MAC_REG_OFFSET_ADDRH = 0x2U, + SMSC9220_MAC_REG_OFFSET_ADDRL = 0x3U, + SMSC9220_MAC_REG_OFFSET_HASHH = 0x4U, + SMSC9220_MAC_REG_OFFSET_HASHL = 0x5U, + SMSC9220_MAC_REG_OFFSET_MII_ACC = 0x6U, + SMSC9220_MAC_REG_OFFSET_MII_DATA = 0x7U, + SMSC9220_MAC_REG_OFFSET_FLOW = 0x8U, + SMSC9220_MAC_REG_OFFSET_VLAN1 = 0x9U, + SMSC9220_MAC_REG_OFFSET_VLAN2 = 0xAU, + SMSC9220_MAC_REG_OFFSET_WUFF = 0xBU, + SMSC9220_MAC_REG_OFFSET_WUCSR = 0xCU, + SMSC9220_MAC_REG_OFFSET_COE_CR = 0xDU + }; + +/** + * \brief PHY register offset definitions + * + */ + enum phy_reg_offsets_t + { + SMSC9220_PHY_REG_OFFSET_BCTRL = 0U, + SMSC9220_PHY_REG_OFFSET_BSTATUS = 1U, + SMSC9220_PHY_REG_OFFSET_ID1 = 2U, + SMSC9220_PHY_REG_OFFSET_ID2 = 3U, + SMSC9220_PHY_REG_OFFSET_ANEG_ADV = 4U, + SMSC9220_PHY_REG_OFFSET_ANEG_LPA = 5U, + SMSC9220_PHY_REG_OFFSET_ANEG_EXP = 6U, + SMSC9220_PHY_REG_OFFSET_MCONTROL = 17U, + SMSC9220_PHY_REG_OFFSET_MSTATUS = 18U, + SMSC9220_PHY_REG_OFFSET_CSINDICATE = 27U, + SMSC9220_PHY_REG_OFFSET_INTSRC = 29U, + SMSC9220_PHY_REG_OFFSET_INTMASK = 30U, + SMSC9220_PHY_REG_OFFSET_CS = 31U + }; + +/* Bit definitions for PHY Basic Status Register */ + #define PHY_REG_BSTATUS_EXTENDED_CAPABILITIES_INDEX 0U + #define PHY_REG_BSTATUS_JABBER_DETECT_INDEX 1U + #define PHY_REG_BSTATUS_LINK_STATUS_INDEX 2U + #define PHY_REG_BSTATUS_AUTO_NEG_ABILITY_INDEX 3U + #define PHY_REG_BSTATUS_REMOTE_FAULT_INDEX 4U + #define PHY_REG_BSTATUS_AUTO_NEG_COMPLETE_INDEX 5U + #define PHY_REG_BSTATUS_10BASE_T_HALF_DUPLEX_INDEX 11U + #define PHY_REG_BSTATUS_10BASE_T_FULL_DUPLEX_INDEX 12U + #define PHY_REG_BSTATUS_100BASE_TX_HALF_DUPLEX_INDEX 13U + #define PHY_REG_BSTATUS_100BASE_TX_FULL_DUPLEX_INDEX 14U + #define PHY_REG_BSTATUS_100BASE_T4_INDEX 15U + +/** + * \brief FIFO Level Interrupt bit definitions + * + */ + enum smsc9220_fifo_level_irq_pos_t + { + SMSC9220_FIFO_LEVEL_IRQ_RX_STATUS_POS = 0U, + SMSC9220_FIFO_LEVEL_IRQ_TX_STATUS_POS = 16U, + SMSC9220_FIFO_LEVEL_IRQ_TX_DATA_POS = 24U + }; + +/** + * \brief FIFO Level Interrupt limits + * + */ + #define SMSC9220_FIFO_LEVEL_IRQ_MASK 0xFFU + #define SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN 0U + #define SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MAX SMSC9220_FIFO_LEVEL_IRQ_MASK + +/** + * \brief Initializes SMSC9220 Ethernet controller to a known default state: + * - device ID is checked + * - global interrupt is enabled, but all irq sources are disabled + * - all capabilities are advertised + * - 10Mbps able + * - 10Mbps with full duplex + * - 100Mbps Tx able + * - 100Mbps with full duplex + * - Symmetric Pause + * - Asymmetric Pause + * - Establish link enabled + * - Rx enabled + * - Tx enabled + * Init should be called prior to any other process and + * it's the caller's responsibility to follow proper call order. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] wait_ms_function function pointer to a millisec delay function + * for proper timing of some processes + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_init( const struct smsc9220_eth_dev_t * dev, + void ( * wait_ms_function )( uint32_t ) ); + +/** + * \brief Reads the MAC register. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] regoffset Register offset + * \param[in, out] data Pointer to register will be read + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_mac_regread( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_mac_reg_offsets_t regoffset, + uint32_t * data ); + +/** + * \brief Writes the MAC register. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] regoffset Register offset + * \param[in] data Register value to write + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_mac_regwrite( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_mac_reg_offsets_t regoffset, + uint32_t data ); + +/** + * \brief Reads the PHY register. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] regoffset Register offset + * \param[out] data Register value is read + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_phy_regread( const struct smsc9220_eth_dev_t * dev, + enum phy_reg_offsets_t, + uint32_t * data ); + +/** + * \brief Writes the PHY register. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] regoffset Register offset + * \param[in] data Register value to write + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_phy_regwrite( const struct smsc9220_eth_dev_t * dev, + enum phy_reg_offsets_t, + uint32_t data ); + +/** + * \brief Reads the Ethernet Controller's ID. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * + * \return ID number + */ + uint32_t smsc9220_read_id( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Initiates a soft reset, returns failure or success. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_soft_reset( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Sets the Maximum Transmission Unit by Tx fifo size. + * Note: The MTU will be smaller by 512 bytes, + * whis is used by the status. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] val Size of the fifo in kbytes + * \ref HW_CFG_REG_TX_FIFO_SIZE_MIN + * \ref HW_CFG_REG_TX_FIFO_SIZE_MAX + */ + void smsc9220_set_txfifo( const struct smsc9220_eth_dev_t * dev, + uint32_t val ); + +/** + * \brief Sets the FIFO level interrupt for a given source. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] irq_level_pos Bit position of the FIFO to set + * \ref smsc9220_fifo_level_irq_pos_t + * \param[in] level Level of the FIFO, when the FIFO used space is greater + * than this value, corresponding interrupt will be generated. + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_set_fifo_level_irq( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_fifo_level_irq_pos_t irq_level_pos, + uint32_t level ); + +/** + * \brief Waits for EEPROM to be ready to use. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_wait_eeprom( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Initializes irqs by clearing and disabling all interrupt sources + * and enable interrupts. Since all interrupt sources are disabled, + * interrupt won't be triggered, until interrupt sources won't be + * enabled by \ref smsc9220_enable_interrupt + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_init_irqs( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Checks PHY ID registers. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_check_phy( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Resets PHY. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_reset_phy( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Advertises all speeds and pauses capabilities. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_advertise_cap( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Enables transmission. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_enable_xmit( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Disables transmission. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_disable_xmit( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Enables MAC Transmitter. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_enable_mac_xmit( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Disables MAC Transmitter. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_disable_mac_xmit( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Enables receiving. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_enable_mac_recv( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Disables receiving. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_disable_mac_recv( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Enables the given interrupt source. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] source Enum of the interrupt source. + */ + void smsc9220_enable_interrupt( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_interrupt_source source ); + +/** + * \brief Disables the given interrupt source. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] source Enum of the interrupt source. + */ + void smsc9220_disable_interrupt( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_interrupt_source source ); + +/** + * \brief Disables all interrupt sources. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_disable_all_interrupts( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Clears the given interrupt source. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] source Enum of the interrupt source. + */ + void smsc9220_clear_interrupt( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_interrupt_source source ); + +/** + * \brief Clears all interrupt sources. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_clear_all_interrupts( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Gets the status of the given interrupt source. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] source Enum of the interrupt source. + * + * \return non-zero if the given interrupt source is triggered, zero otherwise + */ + int smsc9220_get_interrupt( const struct smsc9220_eth_dev_t * dev, + enum smsc9220_interrupt_source source ); + +/** + * \brief Establishes link + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + */ + void smsc9220_establish_link( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Reads the Ethernet Controller's MAC address from its EEPROM. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in,out] mac array will include the read MAC address in + * 6 bytes hexadecimal format. + * It should be allocated by the caller to 6 bytes. + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_read_mac_address( const struct smsc9220_eth_dev_t * dev, + char * mac ); + +/** + * \brief Check device ID. + * + * \return error code /ref smsc9220_error_t + */ + int smsc9220_check_id( const struct smsc9220_eth_dev_t * dev ); + +/** + * \brief Gets the data size of the Tx buffer, aka Maximum Trasmission Unit + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * + * \return Fifo data size in bytes + */ + uint32_t smsc9220_get_tx_data_fifo_size( const struct + smsc9220_eth_dev_t * dev ); + +/** + * \brief Sends data from the given buffer as an Ethernet packet. + * The full packet length must be specified at the beginning + * of a new packet transmission. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in] total_payload_length Length of the ethernet payload. + * Should be equal to the sum of passed buffers within a packet. + * \param[in] is_new_packet Should be set to true if the input buffer has to + * be sent as the start of a new packet or as a full packet. + * \param[in] data Pointer to the data buffer to be sent. + * \param[in] current_size Size of the data in bytes. + * + * \return error code /ref smsc9220_error_t + */ + enum smsc9220_error_t smsc9220_send_by_chunks( const struct smsc9220_eth_dev_t * dev, + uint32_t total_payload_length, + bool is_new_packet, + const char * data, + uint32_t current_size ); + +/** + * \brief Reads an incoming Ethernet packet into the given buffer. + * Stops reading at packet border. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * \param[in,out] data Pointer to a pre-allocated input buffer. + * Allocating sufficient memory space is the caller's + * responsibility, which is typically done by calling + * \ref smsc9220_peek_next_packet_size. + * \param[in] dlen Length of the allocated data in bytes. + * + * \return Number of bytes read from the Rx FIFO into the given buffer. + */ + uint32_t smsc9220_receive_by_chunks( const struct smsc9220_eth_dev_t * dev, + char * data, + uint32_t dlen ); + +/** + * \brief Get the used space of Rx fifo in bytes. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * + * \return Data received and waiting for read in bytes + */ + uint32_t smsc9220_get_rxfifo_data_used_space( const struct + smsc9220_eth_dev_t * dev ); + +/** + * \brief Gets the size of next unread packet in Rx buffer, using the peak + * register, which is not destructive so can be read asynchronously. + * Warning: In case of heavy receiving loads, this register may not + * be in perfect sync. + * + * \param[in] dev Ethernet device structure \ref smsc9220_eth_dev_t + * + * \return Size of the next packet in bytes, read from the Rx Peek register. + */ + uint32_t smsc9220_peek_next_packet_size( const struct + smsc9220_eth_dev_t * dev ); + + #ifdef __cplusplus + } + #endif + +#endif /* __SMSC9220_ETH_H__ */ diff --git a/FreeRTOS/source/portable/NetworkInterface/README_DRIVER_DISCLAIMER.txt b/FreeRTOS/source/portable/NetworkInterface/README_DRIVER_DISCLAIMER.txt new file mode 100644 index 0000000..d4dc868 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/README_DRIVER_DISCLAIMER.txt @@ -0,0 +1,10 @@ +Network drivers are provided as examples only, and do not form part of the +FreeRTOS+TCP stack itself. They: + + + May be based on driver code provided by the chip vendors, + + May not have been tested in all possible configurations, + + Will not necessarily be optimised. + + May have a dependency on a particular PHY part number. + + May not necessarily comply with any particular coding standard. + + May have dependencies on chip company libraries. + + May include other hardware board dependencies. diff --git a/FreeRTOS/source/portable/NetworkInterface/RX/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/RX/NetworkInterface.c new file mode 100644 index 0000000..118deb9 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/RX/NetworkInterface.c @@ -0,0 +1,571 @@ +/*********************************************************************************************************************** +* DISCLAIMER +* This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. No +* other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all +* applicable laws, including copyright laws. +* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING +* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. TO THE MAXIMUM +* EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES +* SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS +* SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +* Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability of +* this software. By using this software, you agree to the additional terms and conditions found by accessing the +* following link: +* http://www.renesas.com/disclaimer +* +* Copyright (C) 2020 Renesas Electronics Corporation. All rights reserved. +***********************************************************************************************************************/ + +/*********************************************************************************************************************** +* File Name : NetworkInterface.c +* Device(s) : RX +* Description : Interfaces FreeRTOS TCP/IP stack to RX Ethernet driver. +***********************************************************************************************************************/ + +/*********************************************************************************************************************** +* History : DD.MM.YYYY Version Description +* : 07.03.2018 0.1 Development +***********************************************************************************************************************/ + +/*********************************************************************************************************************** +* Includes , "Project Includes" +***********************************************************************************************************************/ +#include +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" +/*#include "FreeRTOS_DNS.h" */ +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "r_ether_rx_if.h" +#include "r_pinset.h" + +/*********************************************************************************************************************** + * Macro definitions + **********************************************************************************************************************/ +#define ETHER_BUFSIZE_MIN 60 + +#if defined( BSP_MCU_RX65N ) || defined( BSP_MCU_RX64M ) || defined( BSP_MCU_RX71M ) || defined( BSP_MCU_RX72M ) + #if ETHER_CFG_MODE_SEL == 0 + #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC0_MII() + #elif ETHER_CFG_MODE_SEL == 1 + #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC0_RMII() + #endif +#elif defined( BSP_MCU_RX63N ) + #if ETHER_CFG_MODE_SEL == 0 + #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC_MII() + #elif ETHER_CFG_MODE_SEL == 1 + #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC_RMII() + #endif +#endif /* if defined( BSP_MCU_RX65N ) || defined( BSP_MCU_RX64M ) || defined( BSP_MCU_RX71M ) */ + +#ifndef PHY_LS_HIGH_CHECK_TIME_MS + +/* Check if the LinkSStatus in the PHY is still high after 2 seconds of not + * receiving packets. */ + #define PHY_LS_HIGH_CHECK_TIME_MS 2000 +#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 +#endif + +/*********************************************************************************************************************** + * Private global variables and functions + **********************************************************************************************************************/ +typedef enum +{ + eMACInit, /* Must initialise MAC. */ + eMACPass, /* Initialisation was successful. */ + eMACFailed, /* Initialisation failed. */ +} eMAC_INIT_STATUS_TYPE; + +static TaskHandle_t ether_receive_check_task_handle = 0; +static TaskHandle_t xTaskToNotify = NULL; +static BaseType_t xPHYLinkStatus; +static BaseType_t xReportedStatus; +static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit; + +static int16_t SendData( uint8_t * pucBuffer, + size_t length ); +static int InitializeNetwork( void ); +static void prvEMACDeferredInterruptHandlerTask( void * pvParameters ); +static void clear_all_ether_rx_discriptors( uint32_t event ); + +int32_t callback_ether_regist( void ); +void EINT_Trig_isr( void * ); +void get_random_number( uint8_t * data, + uint32_t len ); + +void prvLinkStatusChange( BaseType_t xStatus ); + +/*********************************************************************************************************************** + * Function Name: xNetworkInterfaceInitialise () + * Description : Initialization of Ethernet driver. + * Arguments : none + * Return Value : pdPASS, pdFAIL + **********************************************************************************************************************/ +BaseType_t xNetworkInterfaceInitialise( void ) +{ + BaseType_t xReturn; + + if( xMacInitStatus == eMACInit ) + { + /* + * Perform the hardware specific network initialization here using the Ethernet driver library to initialize the + * Ethernet hardware, initialize DMA descriptors, and perform a PHY auto-negotiation to obtain a network link. + * + * InitialiseNetwork() uses Ethernet peripheral driver library function, and returns 0 if the initialization fails. + */ + if( InitializeNetwork() == pdFALSE ) + { + xMacInitStatus = eMACFailed; + } + else + { + /* Indicate that the MAC initialisation succeeded. */ + xMacInitStatus = eMACPass; + } + + FreeRTOS_printf( ( "InitializeNetwork returns %s\n", ( xMacInitStatus == eMACPass ) ? "OK" : " Fail" ) ); + } + + if( xMacInitStatus == eMACPass ) + { + xReturn = xPHYLinkStatus; + } + else + { + xReturn = pdFAIL; + } + + FreeRTOS_printf( ( "xNetworkInterfaceInitialise returns %d\n", xReturn ) ); + + return xReturn; +} /* End of function xNetworkInterfaceInitialise() */ + + +/*********************************************************************************************************************** + * Function Name: xNetworkInterfaceOutput () + * Description : Simple network output interface. + * Arguments : pxDescriptor, xReleaseAfterSend + * Return Value : pdTRUE, pdFALSE + **********************************************************************************************************************/ +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t xReleaseAfterSend ) +{ + BaseType_t xReturn = pdFALSE; + + /* Simple network interfaces (as opposed to more efficient zero copy network + * interfaces) just use Ethernet peripheral driver library functions to copy + * data from the FreeRTOS+TCP buffer into the peripheral driver's own buffer. + * This example assumes SendData() is a peripheral driver library function that + * takes a pointer to the start of the data to be sent and the length of the + * data to be sent as two separate parameters. The start of the data is located + * by pxDescriptor->pucEthernetBuffer. The length of the data is located + * by pxDescriptor->xDataLength. */ + if( xPHYLinkStatus != 0 ) + { + if( SendData( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ) >= 0 ) + { + xReturn = pdTRUE; + /* Call the standard trace macro to log the send event. */ + iptraceNETWORK_INTERFACE_TRANSMIT(); + } + } + else + { + /* As the PHY Link Status is low, it makes no sense trying to deliver a packet. */ + } + + if( xReleaseAfterSend != pdFALSE ) + { + /* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet + * buffer. The Ethernet buffer is therefore no longer needed, and must be + * freed for re-use. */ + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return xReturn; +} /* End of function xNetworkInterfaceOutput() */ + + +/*********************************************************************************************************************** + * Function Name: prvEMACDeferredInterruptHandlerTask () + * Description : The deferred interrupt handler is a standard RTOS task. + * Arguments : pvParameters + * Return Value : none + **********************************************************************************************************************/ +static void prvEMACDeferredInterruptHandlerTask( void * pvParameters ) +{ + NetworkBufferDescriptor_t * pxBufferDescriptor; + int32_t xBytesReceived = 0; + + /* Avoid compiler warning about unreferenced parameter. */ + ( void ) pvParameters; + + /* Used to indicate that xSendEventStructToIPTask() is being called because + * of an Ethernet receive event. */ + IPStackEvent_t xRxEvent; + + uint8_t * buffer_pointer; + + /* Some variables related to monitoring the PHY. */ + TimeOut_t xPhyTime; + TickType_t xPhyRemTime; + const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); + + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + + FreeRTOS_printf( ( "Deferred Interrupt Handler Task started\n" ) ); + xTaskToNotify = ether_receive_check_task_handle; + + for( ; ; ) + { + #if ( ipconfigHAS_PRINTF != 0 ) + { + /* Call a function that monitors resources: the amount of free network + * buffers and the amount of free space on the heap. See FreeRTOS_IP.c + * for more detailed comments. */ + vPrintResourceStats(); + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + + /* Wait for the Ethernet MAC interrupt to indicate that another packet + * has been received. */ + if( xBytesReceived <= 0 ) + { + ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); + } + + /* See how much data was received. */ + xBytesReceived = R_ETHER_Read_ZC2( ETHER_CHANNEL_0, ( void ** ) &buffer_pointer ); + + if( xBytesReceived < 0 ) + { + /* This is an error. Logged. */ + FreeRTOS_printf( ( "R_ETHER_Read_ZC2: rc = %d\n", xBytesReceived ) ); + } + else if( xBytesReceived > 0 ) + { + /* Allocate a network buffer descriptor that points to a buffer + * large enough to hold the received frame. As this is the simple + * rather than efficient example the received data will just be copied + * into this buffer. */ + pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( ( size_t ) xBytesReceived, 0 ); + + if( pxBufferDescriptor != NULL ) + { + /* pxBufferDescriptor->pucEthernetBuffer now points to an Ethernet + * buffer large enough to hold the received data. Copy the + * received data into pcNetworkBuffer->pucEthernetBuffer. Here it + * is assumed ReceiveData() is a peripheral driver function that + * copies the received data into a buffer passed in as the function's + * parameter. Remember! While is is a simple robust technique - + * it is not efficient. An example that uses a zero copy technique + * is provided further down this page. */ + memcpy( pxBufferDescriptor->pucEthernetBuffer, buffer_pointer, ( size_t ) xBytesReceived ); + /*ReceiveData( pxBufferDescriptor->pucEthernetBuffer ); */ + + /* Set the actual packet length, in case a larger buffer was returned. */ + pxBufferDescriptor->xDataLength = ( size_t ) xBytesReceived; + + R_ETHER_Read_ZC2_BufRelease( ETHER_CHANNEL_0 ); + + /* See if the data contained in the received Ethernet frame needs + * to be processed. NOTE! It is preferable to do this in + * the interrupt service routine itself, which would remove the need + * to unblock this task for packets that don't need processing. */ + if( eConsiderFrameForProcessing( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer ) + { + /* The event about to be sent to the TCP/IP is an Rx event. */ + xRxEvent.eEventType = eNetworkRxEvent; + + /* pvData is used to point to the network buffer descriptor that + * now references the received data. */ + xRxEvent.pvData = ( void * ) pxBufferDescriptor; + + /* Send the data to the TCP/IP stack. */ + if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) + { + /* The buffer could not be sent to the IP task so the buffer must be released. */ + vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); + + /* Make a call to the standard trace macro to log the occurrence. */ + iptraceETHERNET_RX_EVENT_LOST(); + clear_all_ether_rx_discriptors( 0 ); + } + else + { + /* The message was successfully sent to the TCP/IP stack. + * Call the standard trace macro to log the occurrence. */ + iptraceNETWORK_INTERFACE_RECEIVE(); + R_BSP_NOP(); + } + } + else + { + /* The Ethernet frame can be dropped, but the Ethernet buffer must be released. */ + vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); + } + } + else + { + /* The event was lost because a network buffer was not available. + * Call the standard trace macro to log the occurrence. */ + iptraceETHERNET_RX_EVENT_LOST(); + clear_all_ether_rx_discriptors( 1 ); + FreeRTOS_printf( ( "R_ETHER_Read_ZC2: Cleared descriptors\n" ) ); + } + } + + if( xBytesReceived > 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 ); + + /* Indicate that the Link Status is high, so that + * xNetworkInterfaceOutput() can send packets. */ + if( xPHYLinkStatus == 0 ) + { + xPHYLinkStatus = 1; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS assume %d\n", xPHYLinkStatus ) ); + } + } + else if( ( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) || ( FreeRTOS_IsNetworkUp() == pdFALSE ) ) + { + R_ETHER_LinkProcess( ETHER_CHANNEL_0 ); + + if( xPHYLinkStatus != xReportedStatus ) + { + xPHYLinkStatus = xReportedStatus; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", xPHYLinkStatus ) ); + } + + vTaskSetTimeOutState( &xPhyTime ); + + if( xPHYLinkStatus != 0 ) + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); + } + else + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + } + } + } +} /* End of function prvEMACDeferredInterruptHandlerTask() */ + + +/*********************************************************************************************************************** + * Function Name: vNetworkInterfaceAllocateRAMToBuffers () + * Description : . + * Arguments : pxNetworkBuffers + * Return Value : none + **********************************************************************************************************************/ +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + uint32_t ul; + uint8_t * buffer_address; + + R_BSP_SECTION_OPERATORS_INIT( B_ETHERNET_BUFFERS_1 ) + + buffer_address = R_BSP_SECTOP( B_ETHERNET_BUFFERS_1 ); + + for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ ) + { + pxNetworkBuffers[ ul ].pucEthernetBuffer = ( buffer_address + ( ETHER_CFG_BUFSIZE * ul ) ); + } +} /* End of function vNetworkInterfaceAllocateRAMToBuffers() */ + + +/*********************************************************************************************************************** + * Function Name: prvLinkStatusChange () + * Description : Function will be called when the Link Status of the phy has changed ( see ether_callback.c ) + * Arguments : xStatus : true when statyus has become high + * Return Value : void + **********************************************************************************************************************/ +void prvLinkStatusChange( BaseType_t xStatus ) +{ + if( xReportedStatus != xStatus ) + { + FreeRTOS_printf( ( "prvLinkStatusChange( %d )\n", xStatus ) ); + xReportedStatus = xStatus; + } +} + +/*********************************************************************************************************************** + * Function Name: InitializeNetwork () + * Description : + * Arguments : none + * Return Value : pdTRUE, pdFALSE + **********************************************************************************************************************/ +static int InitializeNetwork( void ) +{ + ether_return_t eth_ret; + BaseType_t return_code = pdFALSE; + ether_param_t param; + uint8_t myethaddr[ 6 ] = + { + configMAC_ADDR0, + configMAC_ADDR1, + configMAC_ADDR2, + configMAC_ADDR3, + configMAC_ADDR4, + configMAC_ADDR5 + }; /*XXX Fix me */ + + R_ETHER_PinSet_CHANNEL_0(); + R_ETHER_Initial(); + callback_ether_regist(); + + param.channel = ETHER_CHANNEL_0; + eth_ret = R_ETHER_Control( CONTROL_POWER_ON, param ); /* PHY mode settings, module stop cancellation */ + + if( ETHER_SUCCESS != eth_ret ) + { + return pdFALSE; + } + + eth_ret = R_ETHER_Open_ZC2( ETHER_CHANNEL_0, myethaddr, ETHER_FLAG_OFF ); + + if( ETHER_SUCCESS != eth_ret ) + { + return pdFALSE; + } + + return_code = xTaskCreate( prvEMACDeferredInterruptHandlerTask, + "ETHER_RECEIVE_CHECK_TASK", + 512u, + 0, + configMAX_PRIORITIES - 1, + ðer_receive_check_task_handle ); + + if( pdFALSE == return_code ) + { + return pdFALSE; + } + + return pdTRUE; +} /* End of function InitializeNetwork() */ + + +/*********************************************************************************************************************** + * Function Name: SendData () + * Description : + * Arguments : pucBuffer, length + * Return Value : 0 success, negative fail + **********************************************************************************************************************/ +static int16_t SendData( uint8_t * pucBuffer, + size_t length ) /*TODO complete stub function */ +{ + ether_return_t ret; + uint8_t * pwrite_buffer; + uint16_t write_buf_size; + + /* (1) Retrieve the transmit buffer location controlled by the descriptor. */ + ret = R_ETHER_Write_ZC2_GetBuf( ETHER_CHANNEL_0, ( void ** ) &pwrite_buffer, &write_buf_size ); + + if( ETHER_SUCCESS == ret ) + { + if( write_buf_size >= length ) + { + memcpy( pwrite_buffer, pucBuffer, length ); + } + + if( length < ETHER_BUFSIZE_MIN ) /*under minimum*/ + { + memset( ( pwrite_buffer + length ), 0, ( ETHER_BUFSIZE_MIN - length ) ); /*padding*/ + length = ETHER_BUFSIZE_MIN; /*resize*/ + } + + ret = R_ETHER_Write_ZC2_SetBuf( ETHER_CHANNEL_0, ( uint16_t ) length ); + ret = R_ETHER_CheckWrite( ETHER_CHANNEL_0 ); + } + + if( ETHER_SUCCESS != ret ) + { + return -5; /* XXX return meaningful value */ + } + else + { + return 0; + } +} /* End of function SendData() */ + + +/*********************************************************************************************************************** +* Function Name: EINT_Trig_isr +* Description : Standard frame received interrupt handler +* Arguments : ectrl - EDMAC and ETHERC control structure +* Return Value : None +* Note : This callback function is executed when EINT0 interrupt occurred. +***********************************************************************************************************************/ +void EINT_Trig_isr( void * ectrl ) +{ + ether_cb_arg_t * pdecode; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + pdecode = ( ether_cb_arg_t * ) ectrl; + + if( pdecode->status_eesr & 0x00040000 ) /* EDMAC FR (Frame Receive Event) interrupt */ + { + if( xTaskToNotify != NULL ) + { + vTaskNotifyGiveFromISR( ether_receive_check_task_handle, &xHigherPriorityTaskWoken ); + } + + /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch + * should be performed to ensure the interrupt returns directly to the highest + * priority task. The macro used for this purpose is dependent on the port in + * use and may be called portEND_SWITCHING_ISR(). */ + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + /*TODO complete interrupt handler for other events. */ + } +} /* End of function EINT_Trig_isr() */ + + +static void clear_all_ether_rx_discriptors( uint32_t event ) +{ + int32_t xBytesReceived; + uint8_t * buffer_pointer; + + /* Avoid compiler warning about unreferenced parameter. */ + ( void ) event; + + while( 1 ) + { + /* See how much data was received. */ + xBytesReceived = R_ETHER_Read_ZC2( ETHER_CHANNEL_0, ( void ** ) &buffer_pointer ); + + if( 0 > xBytesReceived ) + { + /* This is an error. Ignored. */ + } + else if( 0 < xBytesReceived ) + { + R_ETHER_Read_ZC2_BufRelease( ETHER_CHANNEL_0 ); + iptraceETHERNET_RX_EVENT_LOST(); + } + else + { + break; + } + } +} + +/*********************************************************************************************************************** + * End of file "NetworkInterface.c" + **********************************************************************************************************************/ diff --git a/FreeRTOS/source/portable/NetworkInterface/RX/ether_callback.c b/FreeRTOS/source/portable/NetworkInterface/RX/ether_callback.c new file mode 100644 index 0000000..dc54e0e --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/RX/ether_callback.c @@ -0,0 +1,182 @@ +/*********************************************************************************************************************** +* DISCLAIMER +* This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. No +* other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all +* applicable laws, including copyright laws. +* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING +* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. TO THE MAXIMUM +* EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES +* SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS +* SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +* Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability of +* this software. By using this software, you agree to the additional terms and conditions found by accessing the +* following link: +* http://www.renesas.com/disclaimer +* +* Copyright (C) 2015 Renesas Electronics Corporation. All rights reserved. +***********************************************************************************************************************/ + +/*********************************************************************************************************************** +* File Name : ether_callback.c +* Version : ---- +* Description : This module solves all the world's problems +***********************************************************************************************************************/ + +/********************************************************************************************************************** + * History : DD.MM.YYYY Version Description + * : 05.01.2015 ---- Clean up source code. + ***********************************************************************************************************************/ + +/*********************************************************************************************************************** +* Includes , "Project Includes" +***********************************************************************************************************************/ +#include "r_ether_rx_if.h" + +/*********************************************************************************************************************** +* Private global variables and functions +***********************************************************************************************************************/ +int32_t callback_ether_regist( void ); +void callback_ether( void * pparam ); +static void callback_wakeon_lan( uint32_t channel ); +static void callback_link_on( uint32_t channel ); +static void callback_link_off( uint32_t channel ); + +volatile uint8_t pause_enable = ETHER_FLAG_OFF; +volatile uint8_t magic_packet_detect[ ETHER_CHANNEL_MAX ]; +volatile uint8_t link_detect[ ETHER_CHANNEL_MAX ]; + +void EINT_Trig_isr( void * ); + +/* + * When that Link Status changes, the following function will be called: + */ +void prvLinkStatusChange( BaseType_t xStatus ); + +/*********************************************************************************************************************** +* Function Name: callback_ether +* Description : Regist of callback function +* Arguments : - +* Return Value : 0: success, -1:failed +***********************************************************************************************************************/ +int32_t callback_ether_regist( void ) +{ + ether_param_t param; + ether_cb_t cb_func; + + int32_t ret; + + /* Set the callback function (LAN cable connect/disconnect event) */ + cb_func.pcb_func = &callback_ether; + param.ether_callback = cb_func; + ret = R_ETHER_Control( CONTROL_SET_CALLBACK, param ); + + if( ETHER_SUCCESS != ret ) + { + return -1; + } + + /* Set the callback function (Ether interrupt event) */ + cb_func.pcb_int_hnd = &EINT_Trig_isr; + param.ether_callback = cb_func; + ret = R_ETHER_Control( CONTROL_SET_INT_HANDLER, param ); + + if( ETHER_SUCCESS != ret ) + { + return -1; + } + + return 0; +} /* End of function callback_ether_regist() */ + +/*********************************************************************************************************************** +* Function Name: callback_ether +* Description : Sample of the callback function +* Arguments : pparam - +* +* Return Value : none +***********************************************************************************************************************/ +void callback_ether( void * pparam ) +{ + ether_cb_arg_t * pdecode; + uint32_t channel; + + pdecode = ( ether_cb_arg_t * ) pparam; + channel = pdecode->channel; /* Get Ethernet channel number */ + + switch( pdecode->event_id ) + { + /* Callback function that notifies user to have detected magic packet. */ + case ETHER_CB_EVENT_ID_WAKEON_LAN: + callback_wakeon_lan( channel ); + break; + + /* Callback function that notifies user to have become Link up. */ + case ETHER_CB_EVENT_ID_LINK_ON: + callback_link_on( channel ); + break; + + /* Callback function that notifies user to have become Link down. */ + case ETHER_CB_EVENT_ID_LINK_OFF: + callback_link_off( channel ); + break; + + default: + break; + } +} /* End of function callback_ether() */ + +/*********************************************************************************************************************** +* Function Name: callback_wakeon_lan +* Description : +* Arguments : channel - +* Ethernet channel number +* Return Value : none +***********************************************************************************************************************/ +static void callback_wakeon_lan( uint32_t channel ) +{ + if( ETHER_CHANNEL_MAX > channel ) + { + magic_packet_detect[ channel ] = 1; + + /* Please add necessary processing when magic packet is detected. */ + } +} /* End of function callback_wakeon_lan() */ + +/*********************************************************************************************************************** +* Function Name: callback_link_on +* Description : +* Arguments : channel - +* Ethernet channel number +* Return Value : none +***********************************************************************************************************************/ +static void callback_link_on( uint32_t channel ) +{ + if( ETHER_CHANNEL_MAX > channel ) + { + link_detect[ channel ] = ETHER_FLAG_ON_LINK_ON; + + /* Please add necessary processing when becoming Link up. */ + prvLinkStatusChange( 1 ); + } +} /* End of function callback_link_on() */ + +/*********************************************************************************************************************** +* Function Name: callback_link_off +* Description : +* Arguments : channel - +* Ethernet channel number +* Return Value : none +***********************************************************************************************************************/ +static void callback_link_off( uint32_t channel ) +{ + if( ETHER_CHANNEL_MAX > channel ) + { + link_detect[ channel ] = ETHER_FLAG_ON_LINK_OFF; + + /* Please add necessary processing when becoming Link down. */ + prvLinkStatusChange( 0 ); + } +} /* End of function ether_cb_link_off() */ + +/* End of File */ diff --git a/FreeRTOS/source/portable/NetworkInterface/SH2A/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/SH2A/NetworkInterface.c new file mode 100644 index 0000000..14bba32 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/SH2A/NetworkInterface.c @@ -0,0 +1,118 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* Standard includes. */ +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_Sockets.h" +#include "NetworkBufferManagement.h" + +/* Hardware includes. */ +#include "hwEthernet.h" + +/* Demo includes. */ +#include "NetworkInterface.h" + +#if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES != 1 + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +/* When a packet is ready to be sent, if it cannot be sent immediately then the + * task performing the transmit will block for niTX_BUFFER_FREE_WAIT + * milliseconds. It will do this a maximum of niMAX_TX_ATTEMPTS before giving + * up. */ +#define niTX_BUFFER_FREE_WAIT ( ( TickType_t ) 2UL / portTICK_PERIOD_MS ) +#define niMAX_TX_ATTEMPTS ( 5 ) + +/* The length of the queue used to send interrupt status words from the + * interrupt handler to the deferred handler task. */ +#define niINTERRUPT_QUEUE_LENGTH ( 10 ) + +/*-----------------------------------------------------------*/ + +/* + * A deferred interrupt handler task that processes + */ +extern void vEMACHandlerTask( void * pvParameters ); + +/*-----------------------------------------------------------*/ + +/* The queue used to communicate Ethernet events with the IP task. */ +extern QueueHandle_t xNetworkEventQueue; + +/* The semaphore used to wake the deferred interrupt handler task when an Rx + * interrupt is received. */ +SemaphoreHandle_t xEMACRxEventSemaphore = NULL; +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + BaseType_t xStatus, xReturn; + + /* Initialise the MAC. */ + vInitEmac(); + + while( lEMACWaitForLink() != pdPASS ) + { + vTaskDelay( 20 ); + } + + vSemaphoreCreateBinary( xEMACRxEventSemaphore ); + configASSERT( xEMACRxEventSemaphore ); + + /* The handler task is created at the highest possible priority to + * ensure the interrupt handler can return directly to it. */ + xTaskCreate( vEMACHandlerTask, "EMAC", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL ); + xReturn = pdPASS; + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ + extern void vEMACCopyWrite( uint8_t * pucBuffer, + uint16_t usLength ); + + vEMACCopyWrite( pxNetworkBuffer->pucBuffer, pxNetworkBuffer->xDataLength ); + + /* Finished with the network buffer. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + + return pdTRUE; +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/NetworkInterface.c new file mode 100644 index 0000000..f675b78 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/NetworkInterface.c @@ -0,0 +1,1319 @@ +/* + * Some constants, hardware definitions and comments taken from ST's HAL driver + * library, COPYRIGHT(c) 2015 STMicroelectronics. + */ + +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_DNS.h" +#include "FreeRTOS_ARP.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" +#include "phyHandling.h" + +#include "stm32fxx_hal_eth.h" + +/* ST includes. */ +#if defined( STM32F7xx ) + #include "stm32f7xx_hal.h" + #define CACHE_LINE_SIZE 32u +#elif defined( STM32F4xx ) + #include "stm32f4xx_hal.h" +#elif defined( STM32F2xx ) + #include "stm32f2xx_hal.h" +#elif defined( STM32F1xx ) + #include "stm32f1xx_hal.h" +#elif !defined( _lint ) /* Lint does not like an #error */ + #error What part? +#endif /* if defined( STM32F7xx ) */ + + +/* Interrupt events to process. Currently only the Rx event is processed + * although code for other events is included to allow for possible future + * expansion. */ +#define EMAC_IF_RX_EVENT 1UL +#define EMAC_IF_TX_EVENT 2UL +#define EMAC_IF_ERR_EVENT 4UL +#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) + +/* Calculate the maximum packet size that the DMA can receive. */ +#define EMAC_DMA_BUFFER_SIZE ( ( uint32_t ) ( ETH_MAX_PACKET_SIZE - ipBUFFER_PADDING ) ) + +#define ETH_DMA_ALL_INTS \ + ( ETH_DMA_IT_TST | ETH_DMA_IT_PMT | ETH_DMA_IT_MMC | ETH_DMA_IT_NIS | \ + ETH_DMA_IT_AIS | ETH_DMA_IT_ER | 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 ) + +#ifndef NETWORK_BUFFER_HEADER_SIZE + #define NETWORK_BUFFER_HEADER_SIZE ( ipBUFFER_PADDING ) +#endif + +#ifndef niEMAC_HANDLER_TASK_PRIORITY + #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1 +#endif + +#if ( ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) || ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) ) + #warning Consider enabling checksum offloading +#endif + +#ifndef niDESCRIPTOR_WAIT_TIME_MS + #define niDESCRIPTOR_WAIT_TIME_MS 250uL +#endif + +/* + * Most users will want a PHY that negotiates about + * the connection properties: speed, dmix and duplex. + * On some rare cases, you want to select what is being + * advertised, properties like MDIX and duplex. + */ + +#if !defined( ipconfigETHERNET_AN_ENABLE ) + /* Enable auto-negotiation */ + #define ipconfigETHERNET_AN_ENABLE 1 +#endif + +#if !defined( ipconfigETHERNET_AUTO_CROSS_ENABLE ) + #define ipconfigETHERNET_AUTO_CROSS_ENABLE 1 +#endif + +#if ( ipconfigETHERNET_AN_ENABLE == 0 ) + +/* + * The following three defines are only used in case there + * is no auto-negotiation. + */ + #if !defined( ipconfigETHERNET_CROSSED_LINK ) + #define ipconfigETHERNET_CROSSED_LINK 1 + #endif + + #if !defined( ipconfigETHERNET_USE_100MB ) + #define ipconfigETHERNET_USE_100MB 1 + #endif + + #if !defined( ipconfigETHERNET_USE_FULL_DUPLEX ) + #define ipconfigETHERNET_USE_FULL_DUPLEX 1 + #endif +#endif /* ipconfigETHERNET_AN_ENABLE == 0 */ + +/* Default the size of the stack used by the EMAC deferred handler task to twice + * the size of the stack used by the idle task - but allow this to be overridden in + * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE ) +#endif + +/* 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 + #warning Using RMII, make sure if this is correct + #else + #define ipconfigUSE_RMII 0 + #warning Using MII, make sure if this is correct + #endif /* STM32F7xx */ +#endif /* ipconfigUSE_RMII */ + +typedef enum +{ + eMACInit, /* Must initialise MAC. */ + eMACPass, /* Initialisation was successful. */ + eMACFailed, /* Initialisation failed. */ +} eMAC_INIT_STATUS_TYPE; + +static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit; + +/*-----------------------------------------------------------*/ + +/* + * A deferred interrupt handler task that processes + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/* + * Force a negotiation with the Switch or Router and wait for LS. + */ +static void prvEthernetUpdateConfig( BaseType_t xForce ); + +/* + * See if there is a new packet and forward it to the IP-task. + */ +static BaseType_t prvNetworkInterfaceInput( void ); + +#if ( ipconfigUSE_LLMNR != 0 ) || ( ipconfigUSE_MDNS != 0 ) + +/* + * For LLMNR, an extra MAC-address must be configured to + * be able to receive the multicast messages. + */ + static void prvMACAddressConfig( ETH_HandleTypeDef * heth, + uint32_t ulIndex, + uint8_t * Addr ); +#endif + +/* + * Check if a given packet should be accepted. + */ +static BaseType_t xMayAcceptPacket( uint8_t * pucEthernetBuffer ); + +/* + * Initialise the TX descriptors. + */ +static void prvDMATxDescListInit( void ); + +/* + * Initialise the RX descriptors. + */ +static void prvDMARxDescListInit( void ); + +/* After packets have been sent, the network + * buffers will be released. */ +static void vClearTXBuffers( void ); + +/*-----------------------------------------------------------*/ + +#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; + +/* 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 advised to define both + * + * #define ipconfigZERO_COPY_RX_DRIVER 1 + * #define ipconfigZERO_COPY_TX_DRIVER 1 + * + * The method using memcpy is slower and probably uses more RAM memory. + * The possibility is left in the code just for comparison. + * + * It is advised to define ETH_TXBUFNB at least 4. Note that no + * TX buffers are allocated in a zero-copy driver. + */ +/* MAC buffers: ---------------------------------------------------------*/ + +/* 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. + * See README.TXT in this folder. */ + +/* Ethernet Rx MA Descriptor */ +__attribute__( ( aligned( 32 ) ) ) +#if defined( STM32F7xx ) + __attribute__( ( section( ".first_data" ) ) ) +#endif +ETH_DMADescTypeDef DMARxDscrTab[ ETH_RXBUFNB ]; + +#if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) + /* Ethernet Receive Buffer */ + __ALIGN_BEGIN uint8_t Rx_Buff[ ETH_RXBUFNB ][ ETH_RX_BUF_SIZE ] __ALIGN_END; +#endif + +/* Ethernet Tx DMA Descriptor */ +__attribute__( ( aligned( 32 ) ) ) +#if defined( STM32F7xx ) + __attribute__( ( section( ".first_data" ) ) ) +#endif +ETH_DMADescTypeDef DMATxDscrTab[ ETH_TXBUFNB ]; + +#if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + /* Ethernet Transmit Buffer */ + __ALIGN_BEGIN uint8_t Tx_Buff[ ETH_TXBUFNB ][ ETH_TX_BUF_SIZE ] __ALIGN_END; +#endif + +/* DMATxDescToClear points to the next TX DMA descriptor + * that must be cleared by vClearTXBuffers(). */ +static __IO ETH_DMADescTypeDef * DMATxDescToClear; + +/* Holds the handle of the task used as a deferred interrupt processor. The + * handle is used so direct notifications can be sent to the task for all EMAC/DMA + * related interrupts. */ +static TaskHandle_t xEMACTaskHandle = NULL; + +/* For local use only: describe the PHY's properties: */ +const PhyProperties_t xPHYProperties = +{ + #if ( ipconfigETHERNET_AN_ENABLE != 0 ) + .ucSpeed = PHY_SPEED_AUTO, + .ucDuplex = PHY_DUPLEX_AUTO, + #else + #if ( ipconfigETHERNET_USE_100MB != 0 ) + .ucSpeed = PHY_SPEED_100, + #else + .ucSpeed = PHY_SPEED_10, + #endif + + #if ( ipconfigETHERNET_USE_FULL_DUPLEX != 0 ) + .ucDuplex = PHY_DUPLEX_FULL, + #else + .ucDuplex = PHY_DUPLEX_HALF, + #endif + #endif /* if ( ipconfigETHERNET_AN_ENABLE != 0 ) */ + + #if ( ipconfigETHERNET_AN_ENABLE != 0 ) && ( ipconfigETHERNET_AUTO_CROSS_ENABLE != 0 ) + .ucMDI_X = PHY_MDIX_AUTO, + #elif ( ipconfigETHERNET_CROSSED_LINK != 0 ) + .ucMDI_X = PHY_MDIX_CROSSED, + #else + .ucMDI_X = PHY_MDIX_DIRECT, + #endif +}; + +/*-----------------------------------------------------------*/ + +void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef * heth ) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + ( void ) heth; + + /* Pass an RX-event and wakeup the prvEMACHandlerTask. */ + if( xEMACTaskHandle != NULL ) + { + xTaskNotifyFromISR( xEMACTaskHandle, EMAC_IF_RX_EVENT, eSetBits, &( xHigherPriorityTaskWoken ) ); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + } +} +/*-----------------------------------------------------------*/ + +void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef * heth ) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + ( void ) heth; + + /* Pass a TX-event and wakeup the prvEMACHandlerTask. */ + if( xEMACTaskHandle != NULL ) + { + xTaskNotifyFromISR( xEMACTaskHandle, EMAC_IF_TX_EVENT, eSetBits, &( xHigherPriorityTaskWoken ) ); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + } +} +/*-----------------------------------------------------------*/ + +static void vClearTXBuffers() +{ + __IO ETH_DMADescTypeDef * txLastDescriptor = xETH.TxDesc; + size_t uxCount = ( ( UBaseType_t ) ETH_TXBUFNB ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + NetworkBufferDescriptor_t * pxNetworkBuffer; + uint8_t * ucPayLoad; + #endif + + /* This function is called after a TX-completion interrupt. + * It will release each Network Buffer used in xNetworkInterfaceOutput(). + * 'uxCount' represents the number of descriptors given to DMA for transmission. + * After sending a packet, the DMA will clear the 'ETH_DMATXDESC_OWN' bit. */ + while( ( uxCount > 0 ) && ( ( DMATxDescToClear->Status & ETH_DMATXDESC_OWN ) == 0 ) ) + { + if( ( DMATxDescToClear == txLastDescriptor ) && ( uxCount != ETH_TXBUFNB ) ) + { + break; + } + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + ucPayLoad = ( uint8_t * ) DMATxDescToClear->Buffer1Addr; + + if( ucPayLoad != NULL ) + { + pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad ); + + if( pxNetworkBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + DMATxDescToClear->Buffer1Addr = ( uint32_t ) 0u; + } + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + DMATxDescToClear = ( ETH_DMADescTypeDef * ) ( DMATxDescToClear->Buffer2NextDescAddr ); + + uxCount--; + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + HAL_StatusTypeDef hal_eth_init_status; + BaseType_t xResult; + + #if ( ipconfigUSE_LLMNR != 0 ) || ( ipconfigUSE_MDNS != 0 ) + BaseType_t xMACEntry = ETH_MAC_ADDRESS1; /* ETH_MAC_ADDRESS0 reserved for the primary MAC-address. */ + #endif + + if( xMacInitStatus == eMACInit ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ETH_TXBUFNB, ( UBaseType_t ) ETH_TXBUFNB ); + + if( xTXDescriptorSemaphore == NULL ) + { + xMacInitStatus = eMACFailed; + } + else + { + /* Initialise ETH */ + + xETH.Instance = ETH; + xETH.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; + xETH.Init.Speed = ETH_SPEED_100M; + xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX; + /* Value of PhyAddress doesn't matter, will be probed for. */ + xETH.Init.PhyAddress = 0; + + xETH.Init.MACAddr = ( uint8_t * ) FreeRTOS_GetMACAddress(); + xETH.Init.RxMode = ETH_RXINTERRUPT_MODE; + + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + { + /* using the ETH_CHECKSUM_BY_HARDWARE option: + * both the IP and the protocol checksums will be calculated + * by the peripheral. */ + xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; + } + #else + { + xETH.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE; + } + #endif + + #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. */ + ( void ) hal_eth_init_status; + + /* Set the TxDesc and RxDesc pointers. */ + xETH.TxDesc = DMATxDscrTab; + xETH.RxDesc = DMARxDscrTab; + + /* Make sure that all unused fields are cleared. */ + memset( &DMATxDscrTab, '\0', sizeof( DMATxDscrTab ) ); + memset( &DMARxDscrTab, '\0', sizeof( DMARxDscrTab ) ); + + /* Initialize Tx Descriptors list: Chain Mode */ + DMATxDescToClear = DMATxDscrTab; + + /* Initialise TX-descriptors. */ + prvDMATxDescListInit(); + + /* Initialise RX-descriptors. */ + prvDMARxDescListInit(); + + #if ( ipconfigUSE_MDNS == 1 ) + { + /* Program the MDNS address. */ + prvMACAddressConfig( &xETH, xMACEntry, ( uint8_t * ) xMDNS_MACAddressIPv4 ); + xMACEntry += 8; + } + #endif + #if ( ipconfigUSE_LLMNR == 1 ) + { + /* Program the LLMNR address. */ + prvMACAddressConfig( &xETH, xMACEntry, ( uint8_t * ) xLLMNR_MACAddress ); + xMACEntry += 8; + } + #endif + + /* Force a negotiation with the Switch or Router and wait for LS. */ + prvEthernetUpdateConfig( pdTRUE ); + + /* The deferred interrupt handler task is created at the highest + * possible priority to ensure the interrupt handler can return directly + * to it. The task's handle is stored in xEMACTaskHandle so interrupts can + * notify the task when there is something to process. */ + if( xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle ) == pdPASS ) + { + /* The xTXDescriptorSemaphore and the task are created successfully. */ + xMacInitStatus = eMACPass; + } + else + { + xMacInitStatus = eMACFailed; + } + } + } /* if( xMacInitStatus == eMACInit ) */ + + if( xMacInitStatus != eMACPass ) + { + /* EMAC initialisation failed, return pdFAIL. */ + xResult = pdFAIL; + } + else + { + if( xPhyObject.ulLinkStatusMask != 0U ) + { + xETH.Instance->DMAIER |= ETH_DMA_ALL_INTS; + xResult = pdPASS; + FreeRTOS_printf( ( "Link Status is high\n" ) ); + } + else + { + /* For now pdFAIL will be returned. But prvEMACHandlerTask() is running + * and it will keep on checking the PHY and set 'ulLinkStatusMask' when necessary. */ + xResult = pdFAIL; + } + } + + /* When returning non-zero, the stack will become active and + * start DHCP (in configured) */ + return xResult; +} +/*-----------------------------------------------------------*/ + +static void prvDMATxDescListInit() +{ + ETH_DMADescTypeDef * pxDMADescriptor; + BaseType_t xIndex; + + /* Get the pointer on the first member of the descriptor list */ + pxDMADescriptor = DMATxDscrTab; + + /* Fill each DMA descriptor with the right values */ + for( xIndex = 0; xIndex < ETH_TXBUFNB; xIndex++, pxDMADescriptor++ ) + { + /* Set Second Address Chained bit */ + pxDMADescriptor->Status = ETH_DMATXDESC_TCH; + + #if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + { + /* Set Buffer1 address pointer */ + pxDMADescriptor->Buffer1Addr = ( uint32_t ) ( Tx_Buff[ xIndex ] ); + } + #endif + + if( xETH.Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE ) + { + /* Set the DMA Tx descriptors checksum insertion for TCP, UDP, and ICMP */ + pxDMADescriptor->Status |= ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL; + } + else + { + pxDMADescriptor->Status &= ~( ( uint32_t ) ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL ); + } + + /* Initialize the next descriptor with the Next Descriptor Polling Enable */ + if( xIndex < ETH_TXBUFNB - 1 ) + { + /* Set next descriptor address register with next descriptor base address */ + pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) ( pxDMADescriptor + 1 ); + } + else + { + /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ + pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMATxDscrTab; + } + } + + /* Set Transmit Descriptor List Address Register */ + xETH.Instance->DMATDLAR = ( uint32_t ) DMATxDscrTab; +} +/*-----------------------------------------------------------*/ + +static void prvDMARxDescListInit() +{ + ETH_DMADescTypeDef * pxDMADescriptor; + BaseType_t xIndex; + + /* + * RX-descriptors. + */ + + /* Get the pointer on the first member of the descriptor list */ + pxDMADescriptor = DMARxDscrTab; + + /* Fill each DMA descriptor with the right values */ + for( xIndex = 0; xIndex < ETH_RXBUFNB; xIndex++, pxDMADescriptor++ ) + { + /* Set Buffer1 size and Second Address Chained bit */ + pxDMADescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | EMAC_DMA_BUFFER_SIZE; + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + /* Set Buffer1 address pointer */ + NetworkBufferDescriptor_t * pxBuffer; + + pxBuffer = pxGetNetworkBufferWithDescriptor( EMAC_DMA_BUFFER_SIZE, 100ul ); + + /* If the assert below fails, make sure that there are at least 'ETH_RXBUFNB' + * Network Buffers available during start-up ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ) */ + configASSERT( pxBuffer != NULL ); + + if( pxBuffer != NULL ) + { + pxDMADescriptor->Buffer1Addr = ( uint32_t ) pxBuffer->pucEthernetBuffer; + pxDMADescriptor->Status = ETH_DMARXDESC_OWN; + } + } + #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + { + /* Set Buffer1 address pointer */ + pxDMADescriptor->Buffer1Addr = ( uint32_t ) ( Rx_Buff[ xIndex ] ); + /* Set Own bit of the Rx descriptor Status */ + pxDMADescriptor->Status = ETH_DMARXDESC_OWN; + } + #endif /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + + /* Initialize the next descriptor with the Next Descriptor Polling Enable */ + if( xIndex < ETH_RXBUFNB - 1 ) + { + /* Set next descriptor address register with next descriptor base address */ + pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) ( pxDMADescriptor + 1 ); + } + else + { + /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ + pxDMADescriptor->Buffer2NextDescAddr = ( uint32_t ) DMARxDscrTab; + } + } + + /* Set Receive Descriptor List Address Register */ + xETH.Instance->DMARDLAR = ( uint32_t ) DMARxDscrTab; +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_LLMNR != 0 ) || ( ipconfigUSE_MDNS != 0 ) + static void prvMACAddressConfig( ETH_HandleTypeDef * heth, + uint32_t ulIndex, + uint8_t * Addr ) + { + uint32_t ulTempReg; + + ( void ) heth; + + /* Calculate the selected MAC address high register. */ + ulTempReg = 0x80000000ul | ( ( uint32_t ) Addr[ 5 ] << 8 ) | ( uint32_t ) Addr[ 4 ]; + + /* Load the selected MAC address high register. */ + ( *( __IO uint32_t * ) ( ( uint32_t ) ( ETH_MAC_ADDR_HBASE + ulIndex ) ) ) = ulTempReg; + + /* Calculate the selected MAC address low register. */ + ulTempReg = ( ( uint32_t ) Addr[ 3 ] << 24 ) | ( ( uint32_t ) Addr[ 2 ] << 16 ) | ( ( uint32_t ) Addr[ 1 ] << 8 ) | Addr[ 0 ]; + + /* Load the selected MAC address low register */ + ( *( __IO uint32_t * ) ( ( uint32_t ) ( ETH_MAC_ADDR_LBASE + ulIndex ) ) ) = ulTempReg; + } +#endif /* if ( ipconfigUSE_LLMNR != 0 ) || ( ipconfigUSE_MDNS != 0 ) */ +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t bReleaseAfterSend ) +{ + BaseType_t xReturn = pdFAIL; + uint32_t ulTransmitSize = 0; + __IO ETH_DMADescTypeDef * pxDmaTxDesc; +/* Do not wait too long for a free TX DMA buffer. */ + const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u ); + + /* Open a do {} while ( 0 ) loop to be able to call break. */ + do + { + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + { + ProtocolPacket_t * pxPacket; + + /* If the peripheral must calculate the checksum, it wants + * the protocol checksum to have a value of zero. */ + pxPacket = ( ProtocolPacket_t * ) ( pxDescriptor->pucEthernetBuffer ); + + if( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) + { + pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t ) 0u; + } + } + #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */ + + if( xPhyObject.ulLinkStatusMask != 0 ) + { + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + /* Time-out waiting for a free TX descriptor. */ + break; + } + + /* 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? */ + configASSERT( ( pxDmaTxDesc->Status & ETH_DMATXDESC_OWN ) == 0 ); + + { + /* Is this buffer available? */ + /* Get bytes in current buffer. */ + ulTransmitSize = pxDescriptor->xDataLength; + + if( ulTransmitSize > EMAC_DMA_BUFFER_SIZE ) + { + ulTransmitSize = EMAC_DMA_BUFFER_SIZE; + } + + #if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + { + /* Copy the bytes. */ + memcpy( ( void * ) pxDmaTxDesc->Buffer1Addr, pxDescriptor->pucEthernetBuffer, ulTransmitSize ); + } + #else + { + configASSERT( bReleaseAfterSend != 0 ); + + /* Move the buffer. */ + pxDmaTxDesc->Buffer1Addr = ( uint32_t ) pxDescriptor->pucEthernetBuffer; + /* 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.. */ + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + { + pxDmaTxDesc->Status |= ETH_DMATXDESC_CIC_TCPUDPICMP_FULL | ETH_DMATXDESC_IC; + } + #else + { + pxDmaTxDesc->Status &= ~( ( uint32_t ) ETH_DMATXDESC_CIC ); + pxDmaTxDesc->Status |= ETH_DMATXDESC_IC; + } + #endif + + + /* Prepare transmit descriptors to give to DMA. */ + + /* Set LAST and FIRST segment */ + pxDmaTxDesc->Status |= ETH_DMATXDESC_FS | ETH_DMATXDESC_LS; + /* Set frame size */ + pxDmaTxDesc->ControlBufferSize = ( ulTransmitSize & ETH_DMATXDESC_TBS1 ); + + #if ( NETWORK_BUFFERS_CACHED != 0 ) + { + BaseType_t xlength = CACHE_LINE_SIZE * ( ( ulTransmitSize + NETWORK_BUFFER_HEADER_SIZE + CACHE_LINE_SIZE - 1 ) / CACHE_LINE_SIZE ); + uint32_t * pulBuffer = ( uint32_t ) ( pxDescriptor->pucEthernetBuffer - NETWORK_BUFFER_HEADER_SIZE ); + cache_clean_invalidate_by_addr( pulBuffer, xlength ); + } + #endif + + /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ + pxDmaTxDesc->Status |= ETH_DMATXDESC_OWN; + + /* 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(); + xReturn = pdPASS; + } + } + else + { + /* The PHY has no Link Status, packet shall be dropped. */ + } + } while( 0 ); + + /* The buffer has been sent so can be released. */ + if( bReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xMayAcceptPacket( uint8_t * pucEthernetBuffer ) +{ + const ProtocolPacket_t * pxProtPacket = ( const ProtocolPacket_t * ) pucEthernetBuffer; + + switch( pxProtPacket->xTCPPacket.xEthernetHeader.usFrameType ) + { + case ipARP_FRAME_TYPE: + /* Check it later. */ + return pdTRUE; + + case ipIPv4_FRAME_TYPE: + /* Check it here. */ + break; + + default: + /* Refuse the packet. */ + return pdFALSE; + } + + #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) + { + const IPHeader_t * pxIPHeader = &( pxProtPacket->xTCPPacket.xIPHeader ); + uint32_t ulDestinationIPAddress; + + /* Ensure that the incoming packet is not fragmented (only outgoing packets + * can be fragmented) as these are the only handled IP frames currently. */ + if( ( pxIPHeader->usFragmentOffset & ipFRAGMENT_OFFSET_BIT_MASK ) != 0U ) + { + return pdFALSE; + } + + /* HT: Might want to make the following configurable because + * most IP messages have a standard length of 20 bytes */ + + /* 0x45 means: IPv4 with an IP header of 5 x 4 = 20 bytes + * 0x47 means: IPv4 with an IP header of 7 x 4 = 28 bytes */ + if( ( pxIPHeader->ucVersionHeaderLength < 0x45 ) || ( pxIPHeader->ucVersionHeaderLength > 0x4F ) ) + { + return pdFALSE; + } + + ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress; + + /* Is the packet for this node? */ + if( ( ulDestinationIPAddress != *ipLOCAL_IP_ADDRESS_POINTER ) && + /* Is it a broadcast address x.x.x.255 ? */ + ( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xff ) != 0xff ) && + #if ( ipconfigUSE_LLMNR == 1 ) + ( ulDestinationIPAddress != ipLLMNR_IP_ADDR ) && + #endif + ( *ipLOCAL_IP_ADDRESS_POINTER != 0 ) ) + { + FreeRTOS_printf( ( "Drop IP %lxip\n", FreeRTOS_ntohl( ulDestinationIPAddress ) ) ); + return pdFALSE; + } + + if( pxIPHeader->ucProtocol == ipPROTOCOL_UDP ) + { + #if ( ipconfigUSE_LLMNR == 1 ) || ( ipconfigUSE_MDNS == 1 ) || ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_DNS == 1 ) + uint16_t usSourcePort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usSourcePort ); + uint16_t usDestinationPort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort ); + #endif + + if( ( xPortHasUDPSocket( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort ) == pdFALSE ) + #if ipconfigUSE_LLMNR == 1 + && ( usDestinationPort != ipLLMNR_PORT ) && + ( usSourcePort != ipLLMNR_PORT ) + #endif + #if ipconfigUSE_MDNS == 1 + && ( usDestinationPort != ipMDNS_PORT ) && + ( usSourcePort != ipMDNS_PORT ) + #endif + #if ipconfigUSE_NBNS == 1 + && ( usDestinationPort != ipNBNS_PORT ) && + ( usSourcePort != ipNBNS_PORT ) + #endif + #if ipconfigUSE_DNS == 1 + && ( usSourcePort != ipDNS_PORT ) + #endif + ) + { + /* Drop this packet, not for this device. */ + /* FreeRTOS_printf( ( "Drop: UDP port %d -> %d\n", usSourcePort, usDestinationPort ) ); */ + return pdFALSE; + } + } + } + #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +static void prvPassEthMessages( NetworkBufferDescriptor_t * pxDescriptor ) +{ + IPStackEvent_t xRxEvent; + + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = ( void * ) pxDescriptor; + + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 1000 ) != pdPASS ) + { + /* The buffer could not be sent to the stack so must be released again. + * This is a deferred handler task, not a real interrupt, so it is ok to + * use the task level function here. */ + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + do + { + NetworkBufferDescriptor_t * pxNext = pxDescriptor->pxNextBuffer; + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + pxDescriptor = pxNext; + } while( pxDescriptor != NULL ); + } + #else + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "prvPassEthMessages: Can not queue return packet!\n" ) ); + } + else + { + iptraceNETWORK_INTERFACE_RECEIVE(); + } +} + +static BaseType_t prvNetworkInterfaceInput( void ) +{ + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + NetworkBufferDescriptor_t * pxFirstDescriptor = NULL; + NetworkBufferDescriptor_t * pxLastDescriptor = NULL; + #endif + BaseType_t xReceivedLength = 0; + __IO ETH_DMADescTypeDef * pxDMARxDescriptor; + const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( niDESCRIPTOR_WAIT_TIME_MS ); + uint8_t * pucBuffer; + + pxDMARxDescriptor = xETH.RxDesc; + + while( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_OWN ) == 0u ) + { + NetworkBufferDescriptor_t * pxCurDescriptor; + NetworkBufferDescriptor_t * pxNewDescriptor = NULL; + BaseType_t xAccepted = pdTRUE; + + /* Get the Frame Length of the received packet: subtract 4 bytes of the CRC */ + xReceivedLength = ( ( pxDMARxDescriptor->Status & ETH_DMARXDESC_FL ) >> ETH_DMARXDESC_FRAMELENGTHSHIFT ) - 4; + + pucBuffer = ( uint8_t * ) pxDMARxDescriptor->Buffer1Addr; + + /* Update the ETHERNET DMA global Rx descriptor with next Rx descriptor */ + /* Chained Mode */ + /* Selects the next DMA Rx descriptor list for next buffer to read */ + xETH.RxDesc = ( ETH_DMADescTypeDef * ) pxDMARxDescriptor->Buffer2NextDescAddr; + + /* In order to make the code easier and faster, only packets in a single buffer + * will be accepted. This can be done by making the buffers large enough to + * hold a complete Ethernet packet, minus ipBUFFER_PADDING. + * Therefore, two sanity checks: */ + configASSERT( xReceivedLength <= EMAC_DMA_BUFFER_SIZE ); + + if( ( pxDMARxDescriptor->Status & ( ETH_DMARXDESC_CE | ETH_DMARXDESC_IPV4HCE | ETH_DMARXDESC_FT ) ) != ETH_DMARXDESC_FT ) + { + /* Not an Ethernet frame-type or a checksum error. */ + xAccepted = pdFALSE; + } + else + { + /* See if this packet must be handled. */ + xAccepted = xMayAcceptPacket( pucBuffer ); + } + + if( xAccepted != pdFALSE ) + { + /* The packet will be accepted, but check first if a new Network Buffer can + * be obtained. If not, the packet will still be dropped. */ + pxNewDescriptor = pxGetNetworkBufferWithDescriptor( EMAC_DMA_BUFFER_SIZE, xDescriptorWaitTime ); + + if( pxNewDescriptor == NULL ) + { + /* A new descriptor can not be allocated now. This packet will be dropped. */ + xAccepted = pdFALSE; + } + } + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + /* Find out which Network Buffer was originally passed to the descriptor. */ + pxCurDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer ); + configASSERT( pxCurDescriptor != NULL ); + } + #else + { + /* In this mode, the two descriptors are the same. */ + pxCurDescriptor = pxNewDescriptor; + + if( pxNewDescriptor != NULL ) + { + /* The packet is accepted and a new Network Buffer was created, + * copy data to the Network Buffer. */ + memcpy( pxNewDescriptor->pucEthernetBuffer, pucBuffer, xReceivedLength ); + } + } + #endif /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + + if( xAccepted != pdFALSE ) + { + pxCurDescriptor->xDataLength = xReceivedLength; + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + pxCurDescriptor->pxNextBuffer = NULL; + + if( pxFirstDescriptor == NULL ) + { + /* Becomes the first message */ + pxFirstDescriptor = pxCurDescriptor; + } + else if( pxLastDescriptor != NULL ) + { + /* Add to the tail */ + pxLastDescriptor->pxNextBuffer = pxCurDescriptor; + } + + pxLastDescriptor = pxCurDescriptor; + } + #else /* if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) */ + { + prvPassEthMessages( pxCurDescriptor ); + } + #endif /* if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) */ + } + + /* Release descriptors to DMA */ + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + /* Set Buffer1 address pointer */ + if( pxNewDescriptor != NULL ) + { + pxDMARxDescriptor->Buffer1Addr = ( uint32_t ) pxNewDescriptor->pucEthernetBuffer; + } + else + { + /* The packet was dropped and the same Network + * Buffer will be used to receive a new packet. */ + } + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + /* Set Buffer1 size and Second Address Chained bit */ + pxDMARxDescriptor->ControlBufferSize = ETH_DMARXDESC_RCH | EMAC_DMA_BUFFER_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 ) + { + /* Clear RBUS ETHERNET DMA flag. */ + xETH.Instance->DMASR = ETH_DMASR_RBUS; + + /* Resume DMA reception. */ + xETH.Instance->DMARPDR = 0; + } + + pxDMARxDescriptor = xETH.RxDesc; + } + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + if( pxFirstDescriptor != NULL ) + { + prvPassEthMessages( pxFirstDescriptor ); + } + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + + return( xReceivedLength > 0 ); +} +/*-----------------------------------------------------------*/ + + +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 vMACBProbePhy( void ) +{ + vPhyInitialise( &xPhyObject, xSTM32_PhyRead, xSTM32_PhyWrite ); + xPhyDiscover( &xPhyObject ); + xPhyConfigure( &xPhyObject, &xPHYProperties ); +} +/*-----------------------------------------------------------*/ + +static void prvEthernetUpdateConfig( BaseType_t xForce ) +{ + FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS mask %02lX Force %d\n", + xPhyObject.ulLinkStatusMask, + ( int ) xForce ) ); + + if( ( xForce != pdFALSE ) || ( xPhyObject.ulLinkStatusMask != 0 ) ) + { + /* Restart the auto-negotiation. */ + if( xETH.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE ) + { + xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) ); + + /* Configure the MAC with the Duplex Mode fixed by the + * auto-negotiation process. */ + if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL ) + { + xETH.Init.DuplexMode = ETH_MODE_FULLDUPLEX; + } + else + { + xETH.Init.DuplexMode = ETH_MODE_HALFDUPLEX; + } + + /* Configure the MAC with the speed fixed by the + * auto-negotiation process. */ + if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 ) + { + xETH.Init.Speed = ETH_SPEED_10M; + } + else + { + xETH.Init.Speed = ETH_SPEED_100M; + } + } + else /* AutoNegotiation Disable */ + { + /* Check parameters */ + assert_param( IS_ETH_SPEED( xETH.Init.Speed ) ); + assert_param( IS_ETH_DUPLEX_MODE( xETH.Init.DuplexMode ) ); + + 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 */ + HAL_ETH_ConfigMAC( &xETH, ( ETH_MACInitTypeDef * ) NULL ); + + /* Restart MAC interface */ + HAL_ETH_Start( &xETH ); + } + else + { + /* Stop MAC interface */ + HAL_ETH_Stop( &xETH ); + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ + BaseType_t xReturn; + + if( xPhyObject.ulLinkStatusMask != 0 ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/* Uncomment this in case BufferAllocation_1.c is used. */ + +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + static + #if defined( STM32F7xx ) + __attribute__( ( section( ".first_data" ) ) ) + #endif + uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * ETH_MAX_PACKET_SIZE ] __attribute__( ( aligned( 32 ) ) ); + 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 += ETH_MAX_PACKET_SIZE; + } +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void * pvParameters ) +{ + UBaseType_t uxCurrentCount; + BaseType_t xResult; + const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); + uint32_t ulISREvents = 0U; + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + for( ; ; ) + { + xResult = 0; + + #if ( ipconfigHAS_PRINTF != 0 ) + { + /* Call a function that monitors resources: the amount of free network + * buffers and the amount of free space on the heap. See FreeRTOS_IP.c + * for more detailed comments. */ + vPrintResourceStats(); + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + + if( xTXDescriptorSemaphore != NULL ) + { + static UBaseType_t uxLowestSemCount = ( UBaseType_t ) ETH_TXBUFNB - 1; + + uxCurrentCount = uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + if( uxLowestSemCount > uxCurrentCount ) + { + uxLowestSemCount = uxCurrentCount; + FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) ); + } + } + + /* Wait for a new event or a time-out. */ + xTaskNotifyWait( 0U, /* ulBitsToClearOnEntry */ + EMAC_IF_ALL_EVENT, /* ulBitsToClearOnExit */ + &( ulISREvents ), /* pulNotificationValue */ + ulMaxBlockTime ); + + if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 ) + { + xResult = prvNetworkInterfaceInput(); + } + + if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) + { + /* Code to release TX buffers in case zero-copy is used. */ + /* Check if DMA packets have been delivered. */ + vClearTXBuffers(); + } + + if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 ) + { + /* Future extension: logging about errors that occurred. */ + } + + if( xPhyCheckLinkStatus( &xPhyObject, xResult ) != 0 ) + { + /* Something has changed to a Link Status, need re-check. */ + prvEthernetUpdateConfig( pdFALSE ); + } + } +} +/*-----------------------------------------------------------*/ + +void ETH_IRQHandler( void ) +{ + HAL_ETH_IRQHandler( &xETH ); +} diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/readme.md b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/readme.md new file mode 100644 index 0000000..0b26687 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/readme.md @@ -0,0 +1,139 @@ +This is a FreeeRTOS+TCP driver that works for STM32Fxx parts. + + +CONFIGURATION AND RUNNING +========================= + +The code of stm32fxx_hal_eth.c is based on the ETH drivers as provided by ST. + +These modules should be included: +- portable/NetworkInterface/STM32Fxx/NetworkInterface.c +- portable/NetworkInterface/STM32Fxx/stm32fxx_hal_eth.c + +When initialising the EMAC, the driver will call the function `HAL_ETH_MspInit()`, which should do the following: + +- Enable the Ethernet interface clock using: +```cpp + __HAL_RCC_ETHMAC_CLK_ENABLE(); + __HAL_RCC_ETHMACTX_CLK_ENABLE(); + __HAL_RCC_ETHMACRX_CLK_ENABLE(); +``` + +- Initialize the related GPIO clocks + +- Configure Ethernet pin-out + Please check the schematic of your hardware to see which pins are used. + Also check if either MII or RMII is used ( define `ipconfigUSE_RMII` + as 0 or 1 ). + +- Configure Ethernet NVIC interrupt (IT mode) + Choose a proper interrupt priority, defined in FreeRTOSIPConfig.h as e.g. : + +```cpp + #define ipconfigMAC_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ) +``` + +The function `HAL_ETH_MspInit()` must be provided by the application. Make sure that your copy of the function is called, +and not a dummy defined as "weak". + +It is assumed that one of these macros is defined at the highest level: + + STM32F1xx + STM32F2xx + STM32F4xx + STM32F7xx + +For instance, you can pass it to the compiler with the `-D` option: + + gcc ... -D STM32F4xx=1 + +And sub-models may also be indicated, such as `STM32F401xC` or `STM32F407xx`. + +The driver has been tested on both Eval and Discovery boards with STM32F1, STM32F2, STM32F4 and STM32F7. The F1 and F2 boards +have only be tested by customers who reported about it on the FreeRTOS forum. + +Note that it is required to define `HAL_ETH_MODULE_ENABLED` in your STM32 configuration file. The name of this file is one out +of: + + stm32f1xx_hal_conf.h + stm32f2xx_hal_conf.h + stm32f4xx_hal_conf.h + stm32f7xx_hal_conf.h + +This configuration file defines the HAL modules that will be used. Here are some examples of the module macros: +~~~c +#define HAL_MODULE_ENABLED +#define HAL_ETH_MODULE_ENABLED /* <= this one is needed to get Ethernet. */ +#define HAL_SRAM_MODULE_ENABLED +#define HAL_RNG_MODULE_ENABLED +#define HAL_RTC_MODULE_ENABLED +/* etc. */ +~~~ + +Recommended settings for STM32Fxx Network Interface: + + +**Defined in FreeRTOSIPConfig.h** +```cpp +#define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES 1 +#define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 +#define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 +#define ipconfigZERO_COPY_RX_DRIVER 1 +#define ipconfigZERO_COPY_TX_DRIVER 1 +#define ipconfigUSE_LINKED_RX_MESSAGES 1 +``` + +**Defined in stm32f4xx_hal_conf.h** +```cpp +#define ETH_RXBUFNB 3 or 4 +#define ETH_TXBUFNB 1 or 2 +#define ETH_RX_BUF_SIZE ( ipconfigNETWORK_MTU + 36 ) +#define ETH_TX_BUF_SIZE ( ipconfigNETWORK_MTU + 36 ) +``` + +The best size for `ETH_RXBUFNB` and `ETH_TXBUFNB` depends on the speed of the CPU. These macro's define the number of DMA buffers +for reception and for transmission. In general, if the CPU is very fast, you will need less buffers. You can obtain an estimate +empirically. + +The optimal value of `ETH_RX_BUF_SIZE` and `ETH_TX_BUF_SIZE` depends on the actual value of `ipconfigNETWORK_MTU`. +When MTU is 1500, MTU+36 becomes a well-aligned buffer of 1536 bytes ( 0x600 ). +When MTU is 1200, MTU+48 will make 1248 ( 0x4E0 ), which is also well aligned. + +Having well aligned buffers is important for CPU with memory cache. Often the caching system divides memory in blocks of 32 bytes. +When two buffers share the same cache buffer, you are bound to see data errors. + +Without memory caching, let the size be at least a multiple of 8 ( for DMA ), and make it at least "ipconfigNETWORK_MTU + 14". + +STM32F7xx only: + +NetworkInterface.c will place the 2 DMA tables in a special section called 'first_data'. +In case 'BufferAllocation_1.c' is used, the network packets will also be declared in this section 'first_data'. +As long as the part has no caching, this section can be placed anywhere in RAM. +On an STM32F7 with an L1 data cache, it shall be placed in the first 64KB of RAM, which is always uncached. +The linker script must be changed for this, for instance as follows: + +```assembly + .data : + { + . = ALIGN(4); + _sdata = .; // create a global symbol at data start ++ *(.first_data) // .first_data sections + *(.data) // .data sections + *(.data*) // .data* sections + + . = ALIGN(4); + _edata = .; // define a global symbol at data end + } >RAM AT> FLASH +``` + +The driver contains these files: +- NetworkInterface.c +- stm32fxx_hal_eth.c +- stm32f2xx_hal_eth.h +- stm32f4xx_hal_eth.h +- stm32f7xx_hal_eth.h +- stm32fxx_hal_eth.h + +These files are copied from ST's HAL library. These work both for STM32F4 and STM32F7. +Please remove or rename these files from the HAL distribution that you are using. + diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32f2xx_hal_eth.h b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32f2xx_hal_eth.h new file mode 100644 index 0000000..2251c67 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32f2xx_hal_eth.h @@ -0,0 +1,6 @@ +/* + * The Ethernet header files for STM32F2, STM32F4 and STM32F7 have been merged to + * a single module that works for both parts: "stm32fxx_hal_eth" + */ + +#include "stm32fxx_hal_eth.h" diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32f4xx_hal_eth.h b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32f4xx_hal_eth.h new file mode 100644 index 0000000..2251c67 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32f4xx_hal_eth.h @@ -0,0 +1,6 @@ +/* + * The Ethernet header files for STM32F2, STM32F4 and STM32F7 have been merged to + * a single module that works for both parts: "stm32fxx_hal_eth" + */ + +#include "stm32fxx_hal_eth.h" diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32f7xx_hal_eth.h b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32f7xx_hal_eth.h new file mode 100644 index 0000000..2251c67 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32f7xx_hal_eth.h @@ -0,0 +1,6 @@ +/* + * The Ethernet header files for STM32F2, STM32F4 and STM32F7 have been merged to + * a single module that works for both parts: "stm32fxx_hal_eth" + */ + +#include "stm32fxx_hal_eth.h" diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32fxx_hal_eth.c b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32fxx_hal_eth.c new file mode 100644 index 0000000..5cb3877 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32fxx_hal_eth.c @@ -0,0 +1,1534 @@ +/** + ****************************************************************************** + * @file stm32fxx_hal_eth.c + * @author MCD Application Team + * @version V1.3.2 + * @date 26-June-2015 + * @brief ETH HAL module driver. + * This file provides firmware functions to manage the following + * functionalities of the Ethernet (ETH) peripheral: + * + Initialization and de-initialization functions + * + IO operation functions + * + Peripheral Control functions + * + Peripheral State and Errors functions + * + * @verbatim + * ============================================================================== + ##### How to use this driver ##### + #####============================================================================== + #####[..] + #####(#)Declare a ETH_HandleTypeDef handle structure, for example: + ##### ETH_HandleTypeDef heth; + ##### + #####(#)Fill parameters of Init structure in heth handle + ##### + #####(#)Call HAL_ETH_Init() API to initialize the Ethernet peripheral (MAC, DMA, ...) + ##### + #####(#)Initialize the ETH low level resources through the HAL_ETH_MspInit() API: + ##### (##) Enable the Ethernet interface clock using + ##### (+++) __HAL_RCC_ETHMAC_CLK_ENABLE(); + ##### (+++) __HAL_RCC_ETHMACTX_CLK_ENABLE(); + ##### (+++) __HAL_RCC_ETHMACRX_CLK_ENABLE(); + ##### + ##### (##) Initialize the related GPIO clocks + ##### (##) Configure Ethernet pin-out + ##### (##) Configure Ethernet NVIC interrupt (IT mode) + ##### + #####(#)Initialize Ethernet DMA Descriptors in chain mode and point to allocated buffers: + ##### (##) HAL_ETH_DMATxDescListInit(); for Transmission process + ##### (##) HAL_ETH_DMARxDescListInit(); for Reception process + ##### + #####(#)Enable MAC and DMA transmission and reception: + ##### (##) HAL_ETH_Start(); + ##### + #####(#)Prepare ETH DMA TX Descriptors and give the hand to ETH DMA to transfer + ##### the frame to MAC TX FIFO: + ##### (##) HAL_ETH_TransmitFrame(); + ##### + #####(#)Poll for a received frame in ETH RX DMA Descriptors and get received + ##### frame parameters + ##### (##) HAL_ETH_GetReceivedFrame(); (should be called into an infinite loop) + ##### + #####(#) Get a received frame when an ETH RX interrupt occurs: + ##### (##) HAL_ETH_GetReceivedFrame_IT(); (called in IT mode only) + ##### + #####(#) Communicate with external PHY device: + ##### (##) Read a specific register from the PHY + ##### HAL_ETH_ReadPHYRegister(); + ##### (##) Write data to a specific RHY register: + ##### HAL_ETH_WritePHYRegister(); + ##### + #####(#) Configure the Ethernet MAC after ETH peripheral initialization + ##### HAL_ETH_ConfigMAC(); all MAC parameters should be filled. + ##### + #####(#) Configure the Ethernet DMA after ETH peripheral initialization + ##### HAL_ETH_ConfigDMA(); all DMA parameters should be filled. + ##### + #####-@- The PTP protocol and the DMA descriptors ring mode are not supported + ##### in this driver + ##### + #####@endverbatim + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 STMicroelectronics

+ * + * 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. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32fxx_hal_eth.h" + +#if defined( STM32F7xx ) + #include "stm32f7xx_hal.h" + #define stm_is_F7 1 +#elif defined( STM32F4xx ) + #include "stm32f4xx_hal.h" + #define stm_is_F4 1 +#elif defined( STM32F2xx ) + #include "stm32f2xx_hal.h" + #define stm_is_F2 1 +#elif defined( STM32F1xx ) + #include "stm32f1xx_hal.h" + #define stm_is_F1 1 +#else /* if defined( STM32F7xx ) */ + #error For what part should this be compiled? +#endif /* if defined( STM32F7xx ) */ + +/** @addtogroup STM32F4xx_HAL_Driver + * @{ + */ + +/** @defgroup ETH ETH + * @brief ETH HAL module driver + * @{ + */ + +#if !defined( ARRAY_SIZE ) + #define ARRAY_SIZE( x ) ( sizeof( x ) / sizeof( x )[ 0 ] ) +#endif + +#ifdef HAL_ETH_MODULE_ENABLED + + #if ( stm_is_F1 != 0 || stm_is_F2 != 0 || stm_is_F4 != 0 || stm_is_F7 ) + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ + +/** @defgroup ETH_Private_Constants ETH Private Constants + * @{ + */ +/* Some macros have been renamed through time. */ + #ifndef ETH_MACMIIAR_CR_Div16 + #define ETH_MACMIIAR_CR_Div16 ETH_MACMIIAR_CR_DIV16 + #define ETH_MACMIIAR_CR_Div26 ETH_MACMIIAR_CR_DIV26 + #define ETH_MACMIIAR_CR_Div42 ETH_MACMIIAR_CR_DIV42 + #endif + +/** + * @} + */ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ + +/** @defgroup ETH_Private_Functions ETH Private Functions + * @{ + */ + static void ETH_MACDMAConfig( ETH_HandleTypeDef * heth, + uint32_t err ); + static void ETH_MACAddressConfig( ETH_HandleTypeDef * heth, + uint32_t MacAddr, + uint8_t * Addr ); + static void ETH_MACReceptionEnable( ETH_HandleTypeDef * heth ); + static void ETH_MACReceptionDisable( ETH_HandleTypeDef * heth ); + static void ETH_MACTransmissionEnable( ETH_HandleTypeDef * heth ); + static void ETH_MACTransmissionDisable( ETH_HandleTypeDef * heth ); + static void ETH_DMATransmissionEnable( ETH_HandleTypeDef * heth ); + static void ETH_DMATransmissionDisable( ETH_HandleTypeDef * heth ); + static void ETH_DMAReceptionEnable( ETH_HandleTypeDef * heth ); + static void ETH_DMAReceptionDisable( ETH_HandleTypeDef * heth ); + static void ETH_FlushTransmitFIFO( ETH_HandleTypeDef * heth ); + +/** + * @} + */ +/* Private functions ---------------------------------------------------------*/ + +/** @defgroup ETH_Exported_Functions ETH Exported Functions + * @{ + */ + +/** @defgroup ETH_Exported_Functions_Group1 Initialization and de-initialization functions + * @brief Initialization and Configuration functions + * + * @verbatim + * =============================================================================== + ##### Initialization and de-initialization functions ##### + #####=============================================================================== + #####[..] This section provides functions allowing to: + #####(+) Initialize and configure the Ethernet peripheral + #####(+) De-initialize the Ethernet peripheral + ##### + #####@endverbatim + * @{ + */ + extern void vMACBProbePhy( void ); + +/** + * @brief Initializes the Ethernet MAC and DMA according to default + * parameters. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Init( ETH_HandleTypeDef * heth ) + { + uint32_t tmpreg = 0uL; + uint32_t hclk = 60000000uL; + uint32_t err = ETH_SUCCESS; + + /* Check the ETH peripheral state */ + if( heth == NULL ) + { + return HAL_ERROR; + } + + /* Check parameters */ + assert_param( IS_ETH_AUTONEGOTIATION( heth->Init.AutoNegotiation ) ); + assert_param( IS_ETH_RX_MODE( heth->Init.RxMode ) ); + assert_param( IS_ETH_CHECKSUM_MODE( heth->Init.ChecksumMode ) ); + assert_param( IS_ETH_MEDIA_INTERFACE( heth->Init.MediaInterface ) ); + + if( heth->State == HAL_ETH_STATE_RESET ) + { + /* Init the low level hardware : GPIO, CLOCK, NVIC. */ + HAL_ETH_MspInit( heth ); + } + + /* Enable SYSCFG Clock */ + __HAL_RCC_SYSCFG_CLK_ENABLE(); + + /* Select MII or RMII Mode*/ + SYSCFG->PMC &= ~( SYSCFG_PMC_MII_RMII_SEL ); + SYSCFG->PMC |= ( uint32_t ) heth->Init.MediaInterface; + + /* Ethernet Software reset */ + /* Set the SWR bit: resets all MAC subsystem internal registers and logic */ + /* After reset all the registers holds their respective reset values */ + /* Also enable EDFE: Enhanced descriptor format enable. */ + heth->Instance->DMABMR |= ETH_DMABMR_SR | ETH_DMABMR_EDE; + + /* Wait for software reset */ + while( ( heth->Instance->DMABMR & ETH_DMABMR_SR ) != ( uint32_t ) RESET ) + { + /* If your program hangs here, please check the value of 'ipconfigUSE_RMII'. */ + } + + /*-------------------------------- MAC Initialization ----------------------*/ + /* Get the ETHERNET MACMIIAR value */ + tmpreg = heth->Instance->MACMIIAR; + /* Clear CSR Clock Range CR[2:0] bits */ + tmpreg &= ETH_MACMIIAR_CR_MASK; + + /* Get hclk frequency value (e.g. 168,000,000) */ + hclk = HAL_RCC_GetHCLKFreq(); + #if !defined( STM32F2xx ) + /* Set CR bits depending on hclk value */ + if( ( hclk >= 20000000uL ) && ( hclk < 35000000uL ) ) + { + /* CSR Clock Range between 20-35 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div16; + } + else if( ( hclk >= 35000000uL ) && ( hclk < 60000000uL ) ) + { + /* CSR Clock Range between 35-60 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div26; + } + else + { + #if ( stm_is_F1 != 0 ) + { + /* The STM32F1xx has a frequency up to 72 MHz. */ + /* CSR Clock Range between 60-72 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div42; + } + #else + { + if( ( hclk >= 60000000uL ) && ( hclk < 100000000uL ) ) + { + /* CSR Clock Range between 60-100 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div42; + } + else if( ( hclk >= 100000000uL ) && ( hclk < 150000000uL ) ) + { + /* CSR Clock Range between 100-150 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div62; + } + else /* ( ( hclk >= 150000000uL ) && ( hclk <= 183000000uL ) ) */ + { + /* CSR Clock Range between 150-183 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div102; + } + } + #endif /* if ( stm_is_F1 != 0 ) */ + } + #else /* if !defined( STM32F2xx ) */ + /* Clock settings for STM32F2 only. */ + /* Set CR bits depending on hclk value */ + if( ( hclk >= 20000000U ) && ( hclk < 35000000U ) ) + { + /* CSR Clock Range between 20-35 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div16; + } + else if( ( hclk >= 35000000U ) && ( hclk < 60000000U ) ) + { + /* CSR Clock Range between 35-60 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div26; + } + else if( ( hclk >= 60000000U ) && ( hclk < 100000000U ) ) + { + /* CSR Clock Range between 60-100 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div42; + } + else /* ((hclk >= 100000000)&&(hclk < 120000000)) */ + { + /* CSR Clock Range between 100-120 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMIIAR_CR_Div62; + } + #endif /* defined(STM32F2xx) */ + /* Write to ETHERNET MAC MIIAR: Configure the ETHERNET CSR Clock Range */ + heth->Instance->MACMIIAR = ( uint32_t ) tmpreg; + + /* Initialise the MACB and set all PHY properties */ + vMACBProbePhy(); + + /* Config MAC and DMA */ + ETH_MACDMAConfig( heth, err ); + + /* Set ETH HAL State to Ready */ + heth->State = HAL_ETH_STATE_READY; + + /* Return function status */ + return HAL_OK; + } + +/** + * @brief De-Initializes the ETH peripheral. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_DeInit( ETH_HandleTypeDef * heth ) + { + /* Set the ETH peripheral state to BUSY */ + heth->State = HAL_ETH_STATE_BUSY; + + /* De-Init the low level hardware : GPIO, CLOCK, NVIC. */ + HAL_ETH_MspDeInit( heth ); + + /* Set ETH HAL state to Disabled */ + heth->State = HAL_ETH_STATE_RESET; + + /* Release Lock */ + __HAL_UNLOCK( heth ); + + /* Return function status */ + return HAL_OK; + } + +/** + * @brief Initializes the ETH MSP. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_MspInit( ETH_HandleTypeDef * heth ) + { + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_MspInit could be implemented in the user file + */ + ( void ) heth; + } + +/** + * @brief DeInitializes ETH MSP. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_MspDeInit( ETH_HandleTypeDef * heth ) + { + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_MspDeInit could be implemented in the user file + */ + ( void ) heth; + } + +/** + * @} + */ + +/** @defgroup ETH_Exported_Functions_Group2 IO operation functions + * @brief Data transfers functions + * + * @verbatim + * ============================================================================== + ##### IO operation functions ##### + #####============================================================================== + #####[..] This section provides functions allowing to: + ##### (+) Transmit a frame + ##### HAL_ETH_TransmitFrame(); + ##### (+) Receive a frame + ##### HAL_ETH_GetReceivedFrame(); + ##### HAL_ETH_GetReceivedFrame_IT(); + ##### (+) Read from an External PHY register + ##### HAL_ETH_ReadPHYRegister(); + ##### (+) Write to an External PHY register + ##### HAL_ETH_WritePHYRegister(); + ##### + #####@endverbatim + ##### + * @{ + */ + + #define ETH_DMA_ALL_INTS \ + ( ETH_DMA_IT_TST | ETH_DMA_IT_PMT | ETH_DMA_IT_MMC | ETH_DMA_IT_NIS | ETH_DMA_IT_AIS | ETH_DMA_IT_ER | \ + ETH_DMA_IT_FBE | ETH_DMA_IT_ET | 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 ) + + #define INT_MASK ( ( uint32_t ) ~( ETH_DMA_IT_TBU ) ) + void HAL_ETH_IRQHandler( ETH_HandleTypeDef * heth ) + { + uint32_t dmasr; + + dmasr = heth->Instance->DMASR & ETH_DMA_ALL_INTS; + heth->Instance->DMASR = dmasr; + + /* Frame received */ + if( ( dmasr & ( ETH_DMA_FLAG_R | ETH_DMA_IT_RBU ) ) != 0 ) + { + /* Receive complete callback */ + HAL_ETH_RxCpltCallback( heth ); + } + + /* Frame transmitted */ + if( ( dmasr & ( ETH_DMA_FLAG_T ) ) != 0 ) + { + /* Transfer complete callback */ + HAL_ETH_TxCpltCallback( heth ); + } + + /* ETH DMA Error */ + if( ( dmasr & ( ETH_DMA_FLAG_AIS ) ) != 0 ) + { + /* Ethernet Error callback */ + HAL_ETH_ErrorCallback( heth ); + } + } + +/** + * @brief Tx Transfer completed callbacks. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef * heth ) + { + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_TxCpltCallback could be implemented in the user file + */ + ( void ) heth; + } + +/** + * @brief Rx Transfer completed callbacks. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef * heth ) + { + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_TxCpltCallback could be implemented in the user file + */ + ( void ) heth; + } + +/** + * @brief Ethernet transfer error callbacks + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_ErrorCallback( ETH_HandleTypeDef * heth ) + { + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_TxCpltCallback could be implemented in the user file + */ + ( void ) heth; + } + +/** + * @brief Reads a PHY register + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param PHYReg: PHY register address, is the index of one of the 32 PHY register. + * This parameter can be one of the following values: + * PHY_BCR: Transceiver Basic Control Register, + * PHY_BSR: Transceiver Basic Status Register. + * More PHY register could be read depending on the used PHY + * @param RegValue: PHY register value + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_ReadPHYRegister( ETH_HandleTypeDef * heth, + uint16_t PHYReg, + uint32_t * RegValue ) + { + uint32_t tmpreg = 0uL; + uint32_t tickstart = 0uL; + HAL_StatusTypeDef xResult; + + /* Check parameters */ + assert_param( IS_ETH_PHY_ADDRESS( heth->Init.PhyAddress ) ); + + /* Check the ETH peripheral state */ + if( heth->State == HAL_ETH_STATE_BUSY_RD ) + { + xResult = HAL_BUSY; + } + else + { + __HAL_LOCK( heth ); + + /* Set ETH HAL State to BUSY_RD */ + heth->State = HAL_ETH_STATE_BUSY_RD; + + /* Get the ETHERNET MACMIIAR value */ + tmpreg = heth->Instance->MACMIIAR; + + /* Keep only the CSR Clock Range CR[2:0] bits value */ + tmpreg &= ~ETH_MACMIIAR_CR_MASK; + + /* Prepare the MII address register value */ + tmpreg |= ( ( ( uint32_t ) heth->Init.PhyAddress << 11 ) & ETH_MACMIIAR_PA ); /* Set the PHY device address */ + tmpreg |= ( ( ( uint32_t ) PHYReg << 6 ) & ETH_MACMIIAR_MR ); /* Set the PHY register address */ + tmpreg &= ~ETH_MACMIIAR_MW; /* Set the read mode */ + tmpreg |= ETH_MACMIIAR_MB; /* Set the MII Busy bit */ + + /* Write the result value into the MII Address register */ + heth->Instance->MACMIIAR = tmpreg; + + /* Get tick */ + tickstart = HAL_GetTick(); + + /* Check for the Busy flag */ + while( 1 ) + { + tmpreg = heth->Instance->MACMIIAR; + + if( ( tmpreg & ETH_MACMIIAR_MB ) == 0uL ) + { + /* Get MACMIIDR value */ + *RegValue = ( uint32_t ) heth->Instance->MACMIIDR; + xResult = HAL_OK; + break; + } + + /* Check for the Timeout */ + if( ( HAL_GetTick() - tickstart ) > PHY_READ_TO ) + { + xResult = HAL_TIMEOUT; + break; + } + } + + /* Set ETH HAL State to READY */ + heth->State = HAL_ETH_STATE_READY; + + /* Process Unlocked */ + __HAL_UNLOCK( heth ); + } + + /* Return function status */ + return xResult; + } + +/** + * @brief Writes to a PHY register. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param PHYReg: PHY register address, is the index of one of the 32 PHY register. + * This parameter can be one of the following values: + * PHY_BCR: Transceiver Control Register. + * More PHY register could be written depending on the used PHY + * @param RegValue: the value to write + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_WritePHYRegister( ETH_HandleTypeDef * heth, + uint16_t PHYReg, + uint32_t RegValue ) + { + uint32_t tmpreg = 0; + uint32_t tickstart = 0; + HAL_StatusTypeDef xResult; + + /* Check parameters */ + assert_param( IS_ETH_PHY_ADDRESS( heth->Init.PhyAddress ) ); + + /* Check the ETH peripheral state */ + if( heth->State == HAL_ETH_STATE_BUSY_WR ) + { + xResult = HAL_BUSY; + } + else + { + __HAL_LOCK( heth ); + + /* Set ETH HAL State to BUSY_WR */ + heth->State = HAL_ETH_STATE_BUSY_WR; + + /* Get the ETHERNET MACMIIAR value */ + tmpreg = heth->Instance->MACMIIAR; + + /* Keep only the CSR Clock Range CR[2:0] bits value */ + tmpreg &= ~ETH_MACMIIAR_CR_MASK; + + /* Prepare the MII register address value */ + tmpreg |= ( ( ( uint32_t ) heth->Init.PhyAddress << 11 ) & ETH_MACMIIAR_PA ); /* Set the PHY device address */ + tmpreg |= ( ( ( uint32_t ) PHYReg << 6 ) & ETH_MACMIIAR_MR ); /* Set the PHY register address */ + tmpreg |= ETH_MACMIIAR_MW; /* Set the write mode */ + tmpreg |= ETH_MACMIIAR_MB; /* Set the MII Busy bit */ + + /* Give the value to the MII data register */ + heth->Instance->MACMIIDR = ( uint16_t ) RegValue; + + /* Write the result value into the MII Address register */ + heth->Instance->MACMIIAR = tmpreg; + + /* Get tick */ + tickstart = HAL_GetTick(); + + /* Check for the Busy flag */ + while( 1 ) + { + tmpreg = heth->Instance->MACMIIAR; + + if( ( tmpreg & ETH_MACMIIAR_MB ) == 0ul ) + { + xResult = HAL_OK; + break; + } + + /* Check for the Timeout */ + if( ( HAL_GetTick() - tickstart ) > PHY_WRITE_TO ) + { + xResult = HAL_TIMEOUT; + break; + } + } + + /* Set ETH HAL State to READY */ + heth->State = HAL_ETH_STATE_READY; + /* Process Unlocked */ + __HAL_UNLOCK( heth ); + } + + /* Return function status */ + return xResult; + } + +/** + * @} + */ + +/** @defgroup ETH_Exported_Functions_Group3 Peripheral Control functions + * @brief Peripheral Control functions + * + * @verbatim + * =============================================================================== + ##### Peripheral Control functions ##### + #####=============================================================================== + #####[..] This section provides functions allowing to: + #####(+) Enable MAC and DMA transmission and reception. + ##### HAL_ETH_Start(); + #####(+) Disable MAC and DMA transmission and reception. + ##### HAL_ETH_Stop(); + #####(+) Set the MAC configuration in runtime mode + ##### HAL_ETH_ConfigMAC(); + #####(+) Set the DMA configuration in runtime mode + ##### HAL_ETH_ConfigDMA(); + ##### + #####@endverbatim + * @{ + */ + +/** + * @brief Enables Ethernet MAC and DMA reception/transmission + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Start( ETH_HandleTypeDef * heth ) + { + /* Process Locked */ + __HAL_LOCK( heth ); + + /* Set the ETH peripheral state to BUSY */ + heth->State = HAL_ETH_STATE_BUSY; + + /* Enable transmit state machine of the MAC for transmission on the MII */ + ETH_MACTransmissionEnable( heth ); + + /* Enable receive state machine of the MAC for reception from the MII */ + ETH_MACReceptionEnable( heth ); + + /* Flush Transmit FIFO */ + ETH_FlushTransmitFIFO( heth ); + + /* Start DMA transmission */ + ETH_DMATransmissionEnable( heth ); + + /* Start DMA reception */ + ETH_DMAReceptionEnable( heth ); + + /* Set the ETH state to READY*/ + heth->State = HAL_ETH_STATE_READY; + + /* Process Unlocked */ + __HAL_UNLOCK( heth ); + + /* Return function status */ + return HAL_OK; + } + +/** + * @brief Stop Ethernet MAC and DMA reception/transmission + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Stop( ETH_HandleTypeDef * heth ) + { + /* Process Locked */ + __HAL_LOCK( heth ); + + /* Set the ETH peripheral state to BUSY */ + heth->State = HAL_ETH_STATE_BUSY; + + /* Stop DMA transmission */ + ETH_DMATransmissionDisable( heth ); + + /* Stop DMA reception */ + ETH_DMAReceptionDisable( heth ); + + /* Disable receive state machine of the MAC for reception from the MII */ + ETH_MACReceptionDisable( heth ); + + /* Flush Transmit FIFO */ + ETH_FlushTransmitFIFO( heth ); + + /* Disable transmit state machine of the MAC for transmission on the MII */ + ETH_MACTransmissionDisable( heth ); + + /* Set the ETH state*/ + heth->State = HAL_ETH_STATE_READY; + + /* Process Unlocked */ + __HAL_UNLOCK( heth ); + + /* Return function status */ + return HAL_OK; + } + + static void vRegisterDelay() + { + uint32_t uxCount; + + /* + * Regarding the HAL delay functions, I noticed that HAL delay is being used to workaround the + * "Successive write operations to the same register might not be fully taken into account" errata. + * The workaround requires a delay of four TX_CLK/RX_CLK clock cycles. For a 10 Mbit connection, + * these clocks are running at 2.5 MHz, so this delay would be at most 1.6 microseconds. + * 180 Mhz = 288 loops + * 168 Mhz = 269 loops + * 100 Mhz = 160 loops + * 84 Mhz = 134 loops + */ + #define WAIT_TIME_NS 1600uL /* 1.6 microseconds */ + #define CPU_MAX_FREQ SystemCoreClock /* 84, 100, 168 or 180 MHz */ + uint32_t NOP_COUNT = ( WAIT_TIME_NS * ( CPU_MAX_FREQ / 1000uL ) ) / 1000000uL; + + for( uxCount = NOP_COUNT; uxCount > 0uL; uxCount-- ) + { + __NOP(); + } + } + + static void prvWriteMACFCR( ETH_HandleTypeDef * heth, + uint32_t ulValue ) + { + /* Enable the MAC transmission */ + heth->Instance->MACFCR = ulValue; + + /* Wait until the write operation will be taken into account: + * at least four TX_CLK/RX_CLK clock cycles. + * Read it back, wait a ms and */ + ( void ) heth->Instance->MACFCR; + + vRegisterDelay(); + + heth->Instance->MACFCR = ulValue; + } + + static void prvWriteDMAOMR( ETH_HandleTypeDef * heth, + uint32_t ulValue ) + { + /* Enable the MAC transmission */ + heth->Instance->DMAOMR = ulValue; + + /* Wait until the write operation will be taken into account: + * at least four TX_CLK/RX_CLK clock cycles. + * Read it back, wait a ms and */ + ( void ) heth->Instance->DMAOMR; + + vRegisterDelay(); + + heth->Instance->DMAOMR = ulValue; + } + + static void prvWriteMACCR( ETH_HandleTypeDef * heth, + uint32_t ulValue ) + { + /* Enable the MAC transmission */ + heth->Instance->MACCR = ulValue; + + /* Wait until the write operation will be taken into account: + * at least four TX_CLK/RX_CLK clock cycles. + * Read it back, wait a ms and */ + ( void ) heth->Instance->MACCR; + + vRegisterDelay(); + + heth->Instance->MACCR = ulValue; + } + +/** + * @brief Set ETH MAC Configuration. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param macconf: MAC Configuration structure + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_ConfigMAC( ETH_HandleTypeDef * heth, + ETH_MACInitTypeDef * macconf ) + { + uint32_t tmpreg = 0uL; + + /* Process Locked */ + __HAL_LOCK( heth ); + + /* Set the ETH peripheral state to BUSY */ + heth->State = HAL_ETH_STATE_BUSY; + + assert_param( IS_ETH_SPEED( heth->Init.Speed ) ); + assert_param( IS_ETH_DUPLEX_MODE( heth->Init.DuplexMode ) ); + + if( macconf != NULL ) + { + /* Check the parameters */ + assert_param( IS_ETH_WATCHDOG( macconf->Watchdog ) ); + assert_param( IS_ETH_JABBER( macconf->Jabber ) ); + assert_param( IS_ETH_INTER_FRAME_GAP( macconf->InterFrameGap ) ); + assert_param( IS_ETH_CARRIER_SENSE( macconf->CarrierSense ) ); + assert_param( IS_ETH_RECEIVE_OWN( macconf->ReceiveOwn ) ); + assert_param( IS_ETH_LOOPBACK_MODE( macconf->LoopbackMode ) ); + assert_param( IS_ETH_CHECKSUM_OFFLOAD( macconf->ChecksumOffload ) ); + assert_param( IS_ETH_RETRY_TRANSMISSION( macconf->RetryTransmission ) ); + assert_param( IS_ETH_AUTOMATIC_PADCRC_STRIP( macconf->AutomaticPadCRCStrip ) ); + assert_param( IS_ETH_BACKOFF_LIMIT( macconf->BackOffLimit ) ); + assert_param( IS_ETH_DEFERRAL_CHECK( macconf->DeferralCheck ) ); + assert_param( IS_ETH_RECEIVE_ALL( macconf->ReceiveAll ) ); + assert_param( IS_ETH_SOURCE_ADDR_FILTER( macconf->SourceAddrFilter ) ); + assert_param( IS_ETH_CONTROL_FRAMES( macconf->PassControlFrames ) ); + assert_param( IS_ETH_BROADCAST_FRAMES_RECEPTION( macconf->BroadcastFramesReception ) ); + assert_param( IS_ETH_DESTINATION_ADDR_FILTER( macconf->DestinationAddrFilter ) ); + assert_param( IS_ETH_PROMISCUOUS_MODE( macconf->PromiscuousMode ) ); + assert_param( IS_ETH_MULTICAST_FRAMES_FILTER( macconf->MulticastFramesFilter ) ); + assert_param( IS_ETH_UNICAST_FRAMES_FILTER( macconf->UnicastFramesFilter ) ); + assert_param( IS_ETH_PAUSE_TIME( macconf->PauseTime ) ); + assert_param( IS_ETH_ZEROQUANTA_PAUSE( macconf->ZeroQuantaPause ) ); + assert_param( IS_ETH_PAUSE_LOW_THRESHOLD( macconf->PauseLowThreshold ) ); + assert_param( IS_ETH_UNICAST_PAUSE_FRAME_DETECT( macconf->UnicastPauseFrameDetect ) ); + assert_param( IS_ETH_RECEIVE_FLOWCONTROL( macconf->ReceiveFlowControl ) ); + assert_param( IS_ETH_TRANSMIT_FLOWCONTROL( macconf->TransmitFlowControl ) ); + assert_param( IS_ETH_VLAN_TAG_COMPARISON( macconf->VLANTagComparison ) ); + assert_param( IS_ETH_VLAN_TAG_IDENTIFIER( macconf->VLANTagIdentifier ) ); + + /*------------------------ ETHERNET MACCR Configuration --------------------*/ + /* Get the ETHERNET MACCR value */ + tmpreg = heth->Instance->MACCR; + /* Clear WD, PCE, PS, TE and RE bits */ + tmpreg &= ETH_MACCR_CLEAR_MASK; + + tmpreg |= ( uint32_t ) ( + macconf->Watchdog | + macconf->Jabber | + macconf->InterFrameGap | + macconf->CarrierSense | + heth->Init.Speed | + macconf->ReceiveOwn | + macconf->LoopbackMode | + heth->Init.DuplexMode | + macconf->ChecksumOffload | + macconf->RetryTransmission | + macconf->AutomaticPadCRCStrip | + macconf->BackOffLimit | + macconf->DeferralCheck ); + + /* Write to ETHERNET MACCR */ + prvWriteMACCR( heth, tmpreg ); + + /*----------------------- ETHERNET MACFFR Configuration --------------------*/ + /* Write to ETHERNET MACFFR */ + heth->Instance->MACFFR = ( uint32_t ) ( + macconf->ReceiveAll | + macconf->SourceAddrFilter | + macconf->PassControlFrames | + macconf->BroadcastFramesReception | + macconf->DestinationAddrFilter | + macconf->PromiscuousMode | + macconf->MulticastFramesFilter | + macconf->UnicastFramesFilter ); + + /* Wait until the write operation will be taken into account : + * at least four TX_CLK/RX_CLK clock cycles */ + tmpreg = heth->Instance->MACFFR; + vRegisterDelay(); + heth->Instance->MACFFR = tmpreg; + + /*--------------- ETHERNET MACHTHR and MACHTLR Configuration ---------------*/ + /* Write to ETHERNET MACHTHR */ + heth->Instance->MACHTHR = ( uint32_t ) macconf->HashTableHigh; + + /* Write to ETHERNET MACHTLR */ + heth->Instance->MACHTLR = ( uint32_t ) macconf->HashTableLow; + /*----------------------- ETHERNET MACFCR Configuration --------------------*/ + + /* Get the ETHERNET MACFCR value */ + tmpreg = heth->Instance->MACFCR; + /* Clear xx bits */ + tmpreg &= ETH_MACFCR_CLEAR_MASK; + + tmpreg |= ( uint32_t ) ( ( + macconf->PauseTime << 16 ) | + macconf->ZeroQuantaPause | + macconf->PauseLowThreshold | + macconf->UnicastPauseFrameDetect | + macconf->ReceiveFlowControl | + macconf->TransmitFlowControl ); + + /* Write to ETHERNET MACFCR */ + prvWriteMACFCR( heth, tmpreg ); + + /*----------------------- ETHERNET MACVLANTR Configuration -----------------*/ + heth->Instance->MACVLANTR = ( uint32_t ) ( macconf->VLANTagComparison | + macconf->VLANTagIdentifier ); + + /* Wait until the write operation will be taken into account : + * at least four TX_CLK/RX_CLK clock cycles */ + tmpreg = heth->Instance->MACVLANTR; + vRegisterDelay(); + heth->Instance->MACVLANTR = tmpreg; + } + else /* macconf == NULL : here we just configure Speed and Duplex mode */ + { + /*------------------------ ETHERNET MACCR Configuration --------------------*/ + /* Get the ETHERNET MACCR value */ + tmpreg = heth->Instance->MACCR; + + /* Clear FES and DM bits */ + tmpreg &= ~( ( uint32_t ) 0x00004800uL ); + + tmpreg |= ( uint32_t ) ( heth->Init.Speed | heth->Init.DuplexMode ); + + /* Write to ETHERNET MACCR */ + prvWriteMACCR( heth, tmpreg ); + } + + /* Set the ETH state to Ready */ + heth->State = HAL_ETH_STATE_READY; + + /* Process Unlocked */ + __HAL_UNLOCK( heth ); + + /* Return function status */ + return HAL_OK; + } + +/** + * @brief Sets ETH DMA Configuration. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param dmaconf: DMA Configuration structure + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_ConfigDMA( ETH_HandleTypeDef * heth, + ETH_DMAInitTypeDef * dmaconf ) + { + uint32_t tmpreg = 0uL; + + /* Process Locked */ + __HAL_LOCK( heth ); + + /* Set the ETH peripheral state to BUSY */ + heth->State = HAL_ETH_STATE_BUSY; + + /* Check parameters */ + assert_param( IS_ETH_DROP_TCPIP_CHECKSUM_FRAME( dmaconf->DropTCPIPChecksumErrorFrame ) ); + assert_param( IS_ETH_RECEIVE_STORE_FORWARD( dmaconf->ReceiveStoreForward ) ); + assert_param( IS_ETH_FLUSH_RECEIVE_FRAME( dmaconf->FlushReceivedFrame ) ); + assert_param( IS_ETH_TRANSMIT_STORE_FORWARD( dmaconf->TransmitStoreForward ) ); + assert_param( IS_ETH_TRANSMIT_THRESHOLD_CONTROL( dmaconf->TransmitThresholdControl ) ); + assert_param( IS_ETH_FORWARD_ERROR_FRAMES( dmaconf->ForwardErrorFrames ) ); + assert_param( IS_ETH_FORWARD_UNDERSIZED_GOOD_FRAMES( dmaconf->ForwardUndersizedGoodFrames ) ); + assert_param( IS_ETH_RECEIVE_THRESHOLD_CONTROL( dmaconf->ReceiveThresholdControl ) ); + assert_param( IS_ETH_SECOND_FRAME_OPERATE( dmaconf->SecondFrameOperate ) ); + assert_param( IS_ETH_ADDRESS_ALIGNED_BEATS( dmaconf->AddressAlignedBeats ) ); + assert_param( IS_ETH_FIXED_BURST( dmaconf->FixedBurst ) ); + assert_param( IS_ETH_RXDMA_BURST_LENGTH( dmaconf->RxDMABurstLength ) ); + assert_param( IS_ETH_TXDMA_BURST_LENGTH( dmaconf->TxDMABurstLength ) ); + assert_param( IS_ETH_ENHANCED_DESCRIPTOR_FORMAT( dmaconf->EnhancedDescriptorFormat ) ); + assert_param( IS_ETH_DMA_DESC_SKIP_LENGTH( dmaconf->DescriptorSkipLength ) ); + assert_param( IS_ETH_DMA_ARBITRATION_ROUNDROBIN_RXTX( dmaconf->DMAArbitration ) ); + + /*----------------------- ETHERNET DMAOMR Configuration --------------------*/ + /* Get the ETHERNET DMAOMR value */ + tmpreg = heth->Instance->DMAOMR; + /* Clear xx bits */ + tmpreg &= ETH_DMAOMR_CLEAR_MASK; + + tmpreg |= ( uint32_t ) ( + dmaconf->DropTCPIPChecksumErrorFrame | + dmaconf->ReceiveStoreForward | + dmaconf->FlushReceivedFrame | + dmaconf->TransmitStoreForward | + dmaconf->TransmitThresholdControl | + dmaconf->ForwardErrorFrames | + dmaconf->ForwardUndersizedGoodFrames | + dmaconf->ReceiveThresholdControl | + dmaconf->SecondFrameOperate ); + + /* Write to ETHERNET DMAOMR */ + prvWriteDMAOMR( heth, tmpreg ); + + /*----------------------- ETHERNET DMABMR Configuration --------------------*/ + heth->Instance->DMABMR = ( uint32_t ) ( dmaconf->AddressAlignedBeats | + dmaconf->FixedBurst | + dmaconf->RxDMABurstLength | /* !! if 4xPBL is selected for Tx or Rx it is applied for the other */ + dmaconf->TxDMABurstLength | + dmaconf->EnhancedDescriptorFormat | + ( dmaconf->DescriptorSkipLength << 2 ) | + dmaconf->DMAArbitration | + ETH_DMABMR_USP ); /* Enable use of separate PBL for Rx and Tx */ + + /* Wait until the write operation will be taken into account: + * at least four TX_CLK/RX_CLK clock cycles */ + tmpreg = heth->Instance->DMABMR; + vRegisterDelay(); + heth->Instance->DMABMR = tmpreg; + + /* Set the ETH state to Ready */ + heth->State = HAL_ETH_STATE_READY; + + /* Process Unlocked */ + __HAL_UNLOCK( heth ); + + /* Return function status */ + return HAL_OK; + } + +/** + * @} + */ + +/** @defgroup ETH_Exported_Functions_Group4 Peripheral State functions + * @brief Peripheral State functions + * + * @verbatim + * =============================================================================== + ##### Peripheral State functions ##### + #####=============================================================================== + #####[..] + #####This subsection permits to get in run-time the status of the peripheral + #####and the data flow. + ##### (+) Get the ETH handle state: + ##### HAL_ETH_GetState(); + ##### + ##### + #####@endverbatim + * @{ + */ + +/** + * @brief Return the ETH HAL state + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL state + */ + HAL_ETH_StateTypeDef HAL_ETH_GetState( ETH_HandleTypeDef * heth ) + { + /* Return ETH state */ + return heth->State; + } + +/** + * @} + */ + +/** + * @} + */ + +/** @addtogroup ETH_Private_Functions + * @{ + */ + +/** + * @brief Configures Ethernet MAC and DMA with default parameters. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param err: Ethernet Init error + * @retval HAL status + */ + static void ETH_MACDMAConfig( ETH_HandleTypeDef * heth, + uint32_t err ) + { + ETH_MACInitTypeDef macinit; + ETH_DMAInitTypeDef dmainit; + uint32_t tmpreg = 0uL; + + if( err != ETH_SUCCESS ) /* Auto-negotiation failed */ + { + /* Set Ethernet duplex mode to Full-duplex */ + heth->Init.DuplexMode = ETH_MODE_FULLDUPLEX; + + /* Set Ethernet speed to 100M */ + heth->Init.Speed = ETH_SPEED_100M; + } + + /* Ethernet MAC default initialization **************************************/ + macinit.Watchdog = ETH_WATCHDOG_ENABLE; + macinit.Jabber = ETH_JABBER_ENABLE; + macinit.InterFrameGap = ETH_INTERFRAMEGAP_96BIT; + macinit.CarrierSense = ETH_CARRIERSENCE_ENABLE; + macinit.ReceiveOwn = ETH_RECEIVEOWN_ENABLE; + macinit.LoopbackMode = ETH_LOOPBACKMODE_DISABLE; + + if( heth->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE ) + { + macinit.ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE; + } + else + { + macinit.ChecksumOffload = ETH_CHECKSUMOFFLAOD_DISABLE; + } + + macinit.RetryTransmission = ETH_RETRYTRANSMISSION_DISABLE; + macinit.AutomaticPadCRCStrip = ETH_AUTOMATICPADCRCSTRIP_DISABLE; + macinit.BackOffLimit = ETH_BACKOFFLIMIT_10; + macinit.DeferralCheck = ETH_DEFFERRALCHECK_DISABLE; + macinit.ReceiveAll = ETH_RECEIVEAll_DISABLE; + macinit.SourceAddrFilter = ETH_SOURCEADDRFILTER_DISABLE; + macinit.PassControlFrames = ETH_PASSCONTROLFRAMES_BLOCKALL; + macinit.BroadcastFramesReception = ETH_BROADCASTFRAMESRECEPTION_ENABLE; + macinit.DestinationAddrFilter = ETH_DESTINATIONADDRFILTER_NORMAL; + macinit.PromiscuousMode = ETH_PROMISCUOUS_MODE_DISABLE; + macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_PERFECT; + macinit.UnicastFramesFilter = ETH_UNICASTFRAMESFILTER_PERFECT; + macinit.HashTableHigh = 0x0uL; + macinit.HashTableLow = 0x0uL; + macinit.PauseTime = 0x0uL; + macinit.ZeroQuantaPause = ETH_ZEROQUANTAPAUSE_DISABLE; + macinit.PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS4; + macinit.UnicastPauseFrameDetect = ETH_UNICASTPAUSEFRAMEDETECT_DISABLE; + macinit.ReceiveFlowControl = ETH_RECEIVEFLOWCONTROL_DISABLE; + macinit.TransmitFlowControl = ETH_TRANSMITFLOWCONTROL_DISABLE; + macinit.VLANTagComparison = ETH_VLANTAGCOMPARISON_16BIT; + macinit.VLANTagIdentifier = 0x0uL; + + /*------------------------ ETHERNET MACCR Configuration --------------------*/ + /* Get the ETHERNET MACCR value */ + tmpreg = heth->Instance->MACCR; + /* Clear WD, PCE, PS, TE and RE bits */ + tmpreg &= ETH_MACCR_CLEAR_MASK; + /* Set the WD bit according to ETH Watchdog value */ + /* Set the JD: bit according to ETH Jabber value */ + /* Set the IFG bit according to ETH InterFrameGap value */ + /* Set the DCRS bit according to ETH CarrierSense value */ + /* Set the FES bit according to ETH Speed value */ + /* Set the DO bit according to ETH ReceiveOwn value */ + /* Set the LM bit according to ETH LoopbackMode value */ + /* Set the DM bit according to ETH Mode value */ + /* Set the IPCO bit according to ETH ChecksumOffload value */ + /* Set the DR bit according to ETH RetryTransmission value */ + /* Set the ACS bit according to ETH AutomaticPadCRCStrip value */ + /* Set the BL bit according to ETH BackOffLimit value */ + /* Set the DC bit according to ETH DeferralCheck value */ + tmpreg |= ( uint32_t ) ( macinit.Watchdog | + macinit.Jabber | + macinit.InterFrameGap | + macinit.CarrierSense | + heth->Init.Speed | + macinit.ReceiveOwn | + macinit.LoopbackMode | + heth->Init.DuplexMode | + macinit.ChecksumOffload | + macinit.RetryTransmission | + macinit.AutomaticPadCRCStrip | + macinit.BackOffLimit | + macinit.DeferralCheck ); + + /* Write to ETHERNET MACCR */ + prvWriteMACCR( heth, tmpreg ); + + /*----------------------- ETHERNET MACFFR Configuration --------------------*/ + /* Set the RA bit according to ETH ReceiveAll value */ + /* Set the SAF and SAIF bits according to ETH SourceAddrFilter value */ + /* Set the PCF bit according to ETH PassControlFrames value */ + /* Set the DBF bit according to ETH BroadcastFramesReception value */ + /* Set the DAIF bit according to ETH DestinationAddrFilter value */ + /* Set the PR bit according to ETH PromiscuousMode value */ + /* Set the PM, HMC and HPF bits according to ETH MulticastFramesFilter value */ + /* Set the HUC and HPF bits according to ETH UnicastFramesFilter value */ + /* Write to ETHERNET MACFFR */ + heth->Instance->MACFFR = ( uint32_t ) ( macinit.ReceiveAll | + macinit.SourceAddrFilter | + macinit.PassControlFrames | + macinit.BroadcastFramesReception | + macinit.DestinationAddrFilter | + macinit.PromiscuousMode | + macinit.MulticastFramesFilter | + macinit.UnicastFramesFilter ); + + /* Wait until the write operation will be taken into account: + * at least four TX_CLK/RX_CLK clock cycles */ + tmpreg = heth->Instance->MACFFR; + vRegisterDelay(); + heth->Instance->MACFFR = tmpreg; + + /*--------------- ETHERNET MACHTHR and MACHTLR Configuration --------------*/ + /* Write to ETHERNET MACHTHR */ + heth->Instance->MACHTHR = ( uint32_t ) macinit.HashTableHigh; + + /* Write to ETHERNET MACHTLR */ + heth->Instance->MACHTLR = ( uint32_t ) macinit.HashTableLow; + /*----------------------- ETHERNET MACFCR Configuration -------------------*/ + + /* Get the ETHERNET MACFCR value */ + tmpreg = heth->Instance->MACFCR; + /* Clear xx bits */ + tmpreg &= ETH_MACFCR_CLEAR_MASK; + + /* Set the PT bit according to ETH PauseTime value */ + /* Set the DZPQ bit according to ETH ZeroQuantaPause value */ + /* Set the PLT bit according to ETH PauseLowThreshold value */ + /* Set the UP bit according to ETH UnicastPauseFrameDetect value */ + /* Set the RFE bit according to ETH ReceiveFlowControl value */ + /* Set the TFE bit according to ETH TransmitFlowControl value */ + tmpreg |= ( uint32_t ) ( ( macinit.PauseTime << 16 ) | + macinit.ZeroQuantaPause | + macinit.PauseLowThreshold | + macinit.UnicastPauseFrameDetect | + macinit.ReceiveFlowControl | + macinit.TransmitFlowControl ); + + /* Write to ETHERNET MACFCR */ + prvWriteMACFCR( heth, tmpreg ); + + /*----------------------- ETHERNET MACVLANTR Configuration ----------------*/ + /* Set the ETV bit according to ETH VLANTagComparison value */ + /* Set the VL bit according to ETH VLANTagIdentifier value */ + heth->Instance->MACVLANTR = ( uint32_t ) ( macinit.VLANTagComparison | + macinit.VLANTagIdentifier ); + + /* Wait until the write operation will be taken into account: + * at least four TX_CLK/RX_CLK clock cycles */ + tmpreg = heth->Instance->MACVLANTR; + vRegisterDelay(); + heth->Instance->MACVLANTR = tmpreg; + + /* Ethernet DMA default initialization ************************************/ + dmainit.DropTCPIPChecksumErrorFrame = ETH_DROPTCPIPCHECKSUMERRORFRAME_ENABLE; + dmainit.ReceiveStoreForward = ETH_RECEIVESTOREFORWARD_ENABLE; + dmainit.FlushReceivedFrame = ETH_FLUSHRECEIVEDFRAME_ENABLE; + dmainit.TransmitStoreForward = ETH_TRANSMITSTOREFORWARD_ENABLE; + dmainit.TransmitThresholdControl = ETH_TRANSMITTHRESHOLDCONTROL_64BYTES; + dmainit.ForwardErrorFrames = ETH_FORWARDERRORFRAMES_DISABLE; + dmainit.ForwardUndersizedGoodFrames = ETH_FORWARDUNDERSIZEDGOODFRAMES_DISABLE; + dmainit.ReceiveThresholdControl = ETH_RECEIVEDTHRESHOLDCONTROL_64BYTES; + dmainit.SecondFrameOperate = ETH_SECONDFRAMEOPERARTE_ENABLE; + dmainit.AddressAlignedBeats = ETH_ADDRESSALIGNEDBEATS_ENABLE; + dmainit.FixedBurst = ETH_FIXEDBURST_ENABLE; + dmainit.RxDMABurstLength = ETH_RXDMABURSTLENGTH_32BEAT; + dmainit.TxDMABurstLength = ETH_TXDMABURSTLENGTH_32BEAT; + dmainit.EnhancedDescriptorFormat = ETH_DMAENHANCEDDESCRIPTOR_ENABLE; + dmainit.DescriptorSkipLength = 0x0uL; + dmainit.DMAArbitration = ETH_DMAARBITRATION_ROUNDROBIN_RXTX_1_1; + + /* Get the ETHERNET DMAOMR value */ + tmpreg = heth->Instance->DMAOMR; + /* Clear xx bits */ + tmpreg &= ETH_DMAOMR_CLEAR_MASK; + + /* Set the DT bit according to ETH DropTCPIPChecksumErrorFrame value */ + /* Set the RSF bit according to ETH ReceiveStoreForward value */ + /* Set the DFF bit according to ETH FlushReceivedFrame value */ + /* Set the TSF bit according to ETH TransmitStoreForward value */ + /* Set the TTC bit according to ETH TransmitThresholdControl value */ + /* Set the FEF bit according to ETH ForwardErrorFrames value */ + /* Set the FUF bit according to ETH ForwardUndersizedGoodFrames value */ + /* Set the RTC bit according to ETH ReceiveThresholdControl value */ + /* Set the OSF bit according to ETH SecondFrameOperate value */ + tmpreg |= ( uint32_t ) ( dmainit.DropTCPIPChecksumErrorFrame | + dmainit.ReceiveStoreForward | + dmainit.FlushReceivedFrame | + dmainit.TransmitStoreForward | + dmainit.TransmitThresholdControl | + dmainit.ForwardErrorFrames | + dmainit.ForwardUndersizedGoodFrames | + dmainit.ReceiveThresholdControl | + dmainit.SecondFrameOperate ); + + /* Write to ETHERNET DMAOMR */ + prvWriteDMAOMR( heth, tmpreg ); + + /*----------------------- ETHERNET DMABMR Configuration ------------------*/ + /* Set the AAL bit according to ETH AddressAlignedBeats value */ + /* Set the FB bit according to ETH FixedBurst value */ + /* Set the RPBL and 4*PBL bits according to ETH RxDMABurstLength value */ + /* Set the PBL and 4*PBL bits according to ETH TxDMABurstLength value */ + /* Set the Enhanced DMA descriptors bit according to ETH EnhancedDescriptorFormat value*/ + /* Set the DSL bit according to ETH DesciptorSkipLength value */ + /* Set the PR and DA bits according to ETH DMAArbitration value */ + heth->Instance->DMABMR = ( uint32_t ) ( dmainit.AddressAlignedBeats | + dmainit.FixedBurst | + dmainit.RxDMABurstLength | /* !! if 4xPBL is selected for Tx or Rx it is applied for the other */ + dmainit.TxDMABurstLength | + dmainit.EnhancedDescriptorFormat | + ( dmainit.DescriptorSkipLength << 2 ) | + dmainit.DMAArbitration | + ETH_DMABMR_USP ); /* Enable use of separate PBL for Rx and Tx */ + + /* Wait until the write operation will be taken into account: + * at least four TX_CLK/RX_CLK clock cycles */ + tmpreg = heth->Instance->DMABMR; + vRegisterDelay(); + heth->Instance->DMABMR = tmpreg; + + if( heth->Init.RxMode == ETH_RXINTERRUPT_MODE ) + { + /* Enable the Ethernet Rx Interrupt */ + __HAL_ETH_DMA_ENABLE_IT( ( heth ), ETH_DMA_IT_NIS | ETH_DMA_IT_R ); + } + + /* Initialize MAC address in ethernet MAC */ + ETH_MACAddressConfig( heth, ETH_MAC_ADDRESS0, heth->Init.MACAddr ); + } + +/** + * @brief Configures the selected MAC address. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param MacAddr: The MAC address to configure + * This parameter can be one of the following values: + * @arg ETH_MAC_Address0: MAC Address0 + * @arg ETH_MAC_Address1: MAC Address1 + * @arg ETH_MAC_Address2: MAC Address2 + * @arg ETH_MAC_Address3: MAC Address3 + * @param Addr: Pointer to MAC address buffer data (6 bytes) + * @retval HAL status + */ + static void ETH_MACAddressConfig( ETH_HandleTypeDef * heth, + uint32_t MacAddr, + uint8_t * Addr ) + { + uint32_t tmpreg; + + ( void ) heth; + + /* Check the parameters */ + assert_param( IS_ETH_MAC_ADDRESS0123( MacAddr ) ); + + /* Calculate the selected MAC address high register */ + /* Register ETH_MACA0HR: Bit 31 MO: Always 1. */ + tmpreg = 0x80000000uL | ( ( uint32_t ) Addr[ 5 ] << 8 ) | ( uint32_t ) Addr[ 4 ]; + /* Load the selected MAC address high register */ + ( *( __IO uint32_t * ) ( ( uint32_t ) ( ETH_MAC_ADDR_HBASE + MacAddr ) ) ) = tmpreg; + /* Calculate the selected MAC address low register */ + tmpreg = ( ( uint32_t ) Addr[ 3 ] << 24 ) | ( ( uint32_t ) Addr[ 2 ] << 16 ) | ( ( uint32_t ) Addr[ 1 ] << 8 ) | Addr[ 0 ]; + + /* Load the selected MAC address low register */ + ( *( __IO uint32_t * ) ( ( uint32_t ) ( ETH_MAC_ADDR_LBASE + MacAddr ) ) ) = tmpreg; + } + +/** + * @brief Enables the MAC transmission. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_MACTransmissionEnable( ETH_HandleTypeDef * heth ) + { + uint32_t tmpreg = heth->Instance->MACCR | ETH_MACCR_TE; + + prvWriteMACCR( heth, tmpreg ); + } + +/** + * @brief Disables the MAC transmission. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_MACTransmissionDisable( ETH_HandleTypeDef * heth ) + { + uint32_t tmpreg = heth->Instance->MACCR & ~( ETH_MACCR_TE ); + + prvWriteMACCR( heth, tmpreg ); + } + +/** + * @brief Enables the MAC reception. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_MACReceptionEnable( ETH_HandleTypeDef * heth ) + { + __IO uint32_t tmpreg = heth->Instance->MACCR | ETH_MACCR_RE; + + prvWriteMACCR( heth, tmpreg ); + } + +/** + * @brief Disables the MAC reception. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_MACReceptionDisable( ETH_HandleTypeDef * heth ) + { + __IO uint32_t tmpreg = heth->Instance->MACCR & ~( ETH_MACCR_RE ); + + prvWriteMACCR( heth, tmpreg ); + } + +/** + * @brief Enables the DMA transmission. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_DMATransmissionEnable( ETH_HandleTypeDef * heth ) + { + /* Enable the DMA transmission */ + __IO uint32_t tmpreg = heth->Instance->DMAOMR | ETH_DMAOMR_ST; + + prvWriteDMAOMR( heth, tmpreg ); + } + +/** + * @brief Disables the DMA transmission. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_DMATransmissionDisable( ETH_HandleTypeDef * heth ) + { + /* Disable the DMA transmission */ + __IO uint32_t tmpreg = heth->Instance->DMAOMR & ~( ETH_DMAOMR_ST ); + + prvWriteDMAOMR( heth, tmpreg ); + } + +/** + * @brief Enables the DMA reception. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_DMAReceptionEnable( ETH_HandleTypeDef * heth ) + { + /* Enable the DMA reception */ + __IO uint32_t tmpreg = heth->Instance->DMAOMR | ETH_DMAOMR_SR; + + prvWriteDMAOMR( heth, tmpreg ); + } + +/** + * @brief Disables the DMA reception. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_DMAReceptionDisable( ETH_HandleTypeDef * heth ) + { + /* Disable the DMA reception */ + __IO uint32_t tmpreg = heth->Instance->DMAOMR & ~( ETH_DMAOMR_SR ); + + prvWriteDMAOMR( heth, tmpreg ); + } + +/** + * @brief Clears the ETHERNET transmit FIFO. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_FlushTransmitFIFO( ETH_HandleTypeDef * heth ) + { + /* Set the Flush Transmit FIFO bit */ + __IO uint32_t tmpreg = heth->Instance->DMAOMR | ETH_DMAOMR_FTF; + + prvWriteDMAOMR( heth, tmpreg ); + } + +/** + * @} + */ + #endif /* stm_is_F1 != 0 || stm_is_F2 != 0 || stm_is_F4 != 0 || stm_is_F7 */ + +#endif /* HAL_ETH_MODULE_ENABLED */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32fxx_hal_eth.h b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32fxx_hal_eth.h new file mode 100644 index 0000000..32af048 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Fxx/stm32fxx_hal_eth.h @@ -0,0 +1,2413 @@ +/** + ****************************************************************************** + * @file stm32fxx_hal_eth.h + * @author MCD Application Team + * @version V1.2.2 + * @date 14-April-2017 + * @brief Header file of ETH HAL module. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2017 STMicroelectronics

+ * + * 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. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32Fxx_HAL_ETH_H + #define __STM32Fxx_HAL_ETH_H + +/* make sure that the original ETH headers files won't be included after this. */ + #define __STM32F2xx_HAL_ETH_H + #define __STM32F4xx_HAL_ETH_H + #define __STM32F7xx_HAL_ETH_H + + #if defined( STM32F7xx ) + #include "stm32f7xx_hal.h" + #elif defined( STM32F407xx ) || defined( STM32F417xx ) || defined( STM32F427xx ) || defined( STM32F437xx ) || defined( STM32F429xx ) || defined( STM32F439xx ) + #include "stm32f4xx_hal.h" + #elif defined( STM32F2xx ) + #include "stm32f2xx_hal.h" + #endif + + #ifdef __cplusplus + extern "C" { + #endif + +/** @addtogroup STM32Fxx_HAL_Driver + * @{ + */ + +/** @addtogroup ETH + * @{ + */ + +/** @addtogroup ETH_Private_Macros + * @{ + */ + #define IS_ETH_PHY_ADDRESS( ADDRESS ) ( ( ADDRESS ) <= 0x20 ) + #define IS_ETH_AUTONEGOTIATION( CMD ) \ + ( ( ( CMD ) == ETH_AUTONEGOTIATION_ENABLE ) || \ + ( ( CMD ) == ETH_AUTONEGOTIATION_DISABLE ) ) + #define IS_ETH_SPEED( SPEED ) \ + ( ( ( SPEED ) == ETH_SPEED_10M ) || \ + ( ( SPEED ) == ETH_SPEED_100M ) ) + #define IS_ETH_DUPLEX_MODE( MODE ) \ + ( ( ( MODE ) == ETH_MODE_FULLDUPLEX ) || \ + ( ( MODE ) == ETH_MODE_HALFDUPLEX ) ) + #define IS_ETH_DUPLEX_MODE( MODE ) \ + ( ( ( MODE ) == ETH_MODE_FULLDUPLEX ) || \ + ( ( MODE ) == ETH_MODE_HALFDUPLEX ) ) + #define IS_ETH_RX_MODE( MODE ) \ + ( ( ( MODE ) == ETH_RXPOLLING_MODE ) || \ + ( ( MODE ) == ETH_RXINTERRUPT_MODE ) ) + #define IS_ETH_RX_MODE( MODE ) \ + ( ( ( MODE ) == ETH_RXPOLLING_MODE ) || \ + ( ( MODE ) == ETH_RXINTERRUPT_MODE ) ) + #define IS_ETH_RX_MODE( MODE ) \ + ( ( ( MODE ) == ETH_RXPOLLING_MODE ) || \ + ( ( MODE ) == ETH_RXINTERRUPT_MODE ) ) + #define IS_ETH_CHECKSUM_MODE( MODE ) \ + ( ( ( MODE ) == ETH_CHECKSUM_BY_HARDWARE ) || \ + ( ( MODE ) == ETH_CHECKSUM_BY_SOFTWARE ) ) + #define IS_ETH_MEDIA_INTERFACE( MODE ) \ + ( ( ( MODE ) == ETH_MEDIA_INTERFACE_MII ) || \ + ( ( MODE ) == ETH_MEDIA_INTERFACE_RMII ) ) + #define IS_ETH_WATCHDOG( CMD ) \ + ( ( ( CMD ) == ETH_WATCHDOG_ENABLE ) || \ + ( ( CMD ) == ETH_WATCHDOG_DISABLE ) ) + #define IS_ETH_JABBER( CMD ) \ + ( ( ( CMD ) == ETH_JABBER_ENABLE ) || \ + ( ( CMD ) == ETH_JABBER_DISABLE ) ) + #define IS_ETH_INTER_FRAME_GAP( GAP ) \ + ( ( ( GAP ) == ETH_INTERFRAMEGAP_96BIT ) || \ + ( ( GAP ) == ETH_INTERFRAMEGAP_88BIT ) || \ + ( ( GAP ) == ETH_INTERFRAMEGAP_80BIT ) || \ + ( ( GAP ) == ETH_INTERFRAMEGAP_72BIT ) || \ + ( ( GAP ) == ETH_INTERFRAMEGAP_64BIT ) || \ + ( ( GAP ) == ETH_INTERFRAMEGAP_56BIT ) || \ + ( ( GAP ) == ETH_INTERFRAMEGAP_48BIT ) || \ + ( ( GAP ) == ETH_INTERFRAMEGAP_40BIT ) ) + #define IS_ETH_CARRIER_SENSE( CMD ) \ + ( ( ( CMD ) == ETH_CARRIERSENCE_ENABLE ) || \ + ( ( CMD ) == ETH_CARRIERSENCE_DISABLE ) ) + #define IS_ETH_RECEIVE_OWN( CMD ) \ + ( ( ( CMD ) == ETH_RECEIVEOWN_ENABLE ) || \ + ( ( CMD ) == ETH_RECEIVEOWN_DISABLE ) ) + #define IS_ETH_LOOPBACK_MODE( CMD ) \ + ( ( ( CMD ) == ETH_LOOPBACKMODE_ENABLE ) || \ + ( ( CMD ) == ETH_LOOPBACKMODE_DISABLE ) ) + #define IS_ETH_CHECKSUM_OFFLOAD( CMD ) \ + ( ( ( CMD ) == ETH_CHECKSUMOFFLAOD_ENABLE ) || \ + ( ( CMD ) == ETH_CHECKSUMOFFLAOD_DISABLE ) ) + #define IS_ETH_RETRY_TRANSMISSION( CMD ) \ + ( ( ( CMD ) == ETH_RETRYTRANSMISSION_ENABLE ) || \ + ( ( CMD ) == ETH_RETRYTRANSMISSION_DISABLE ) ) + #define IS_ETH_AUTOMATIC_PADCRC_STRIP( CMD ) \ + ( ( ( CMD ) == ETH_AUTOMATICPADCRCSTRIP_ENABLE ) || \ + ( ( CMD ) == ETH_AUTOMATICPADCRCSTRIP_DISABLE ) ) + #define IS_ETH_BACKOFF_LIMIT( LIMIT ) \ + ( ( ( LIMIT ) == ETH_BACKOFFLIMIT_10 ) || \ + ( ( LIMIT ) == ETH_BACKOFFLIMIT_8 ) || \ + ( ( LIMIT ) == ETH_BACKOFFLIMIT_4 ) || \ + ( ( LIMIT ) == ETH_BACKOFFLIMIT_1 ) ) + #define IS_ETH_DEFERRAL_CHECK( CMD ) \ + ( ( ( CMD ) == ETH_DEFFERRALCHECK_ENABLE ) || \ + ( ( CMD ) == ETH_DEFFERRALCHECK_DISABLE ) ) + #define IS_ETH_RECEIVE_ALL( CMD ) \ + ( ( ( CMD ) == ETH_RECEIVEALL_ENABLE ) || \ + ( ( CMD ) == ETH_RECEIVEAll_DISABLE ) ) + #define IS_ETH_SOURCE_ADDR_FILTER( CMD ) \ + ( ( ( CMD ) == ETH_SOURCEADDRFILTER_NORMAL_ENABLE ) || \ + ( ( CMD ) == ETH_SOURCEADDRFILTER_INVERSE_ENABLE ) || \ + ( ( CMD ) == ETH_SOURCEADDRFILTER_DISABLE ) ) + #define IS_ETH_CONTROL_FRAMES( PASS ) \ + ( ( ( PASS ) == ETH_PASSCONTROLFRAMES_BLOCKALL ) || \ + ( ( PASS ) == ETH_PASSCONTROLFRAMES_FORWARDALL ) || \ + ( ( PASS ) == ETH_PASSCONTROLFRAMES_FORWARDPASSEDADDRFILTER ) ) + #define IS_ETH_BROADCAST_FRAMES_RECEPTION( CMD ) \ + ( ( ( CMD ) == ETH_BROADCASTFRAMESRECEPTION_ENABLE ) || \ + ( ( CMD ) == ETH_BROADCASTFRAMESRECEPTION_DISABLE ) ) + #define IS_ETH_DESTINATION_ADDR_FILTER( FILTER ) \ + ( ( ( FILTER ) == ETH_DESTINATIONADDRFILTER_NORMAL ) || \ + ( ( FILTER ) == ETH_DESTINATIONADDRFILTER_INVERSE ) ) + #define IS_ETH_PROMISCUOUS_MODE( CMD ) \ + ( ( ( CMD ) == ETH_PROMISCUOUS_MODE_ENABLE ) || \ + ( ( CMD ) == ETH_PROMISCUOUS_MODE_DISABLE ) ) + #define IS_ETH_MULTICAST_FRAMES_FILTER( FILTER ) \ + ( ( ( FILTER ) == ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE ) || \ + ( ( FILTER ) == ETH_MULTICASTFRAMESFILTER_HASHTABLE ) || \ + ( ( FILTER ) == ETH_MULTICASTFRAMESFILTER_PERFECT ) || \ + ( ( FILTER ) == ETH_MULTICASTFRAMESFILTER_NONE ) ) + #define IS_ETH_UNICAST_FRAMES_FILTER( FILTER ) \ + ( ( ( FILTER ) == ETH_UNICASTFRAMESFILTER_PERFECTHASHTABLE ) || \ + ( ( FILTER ) == ETH_UNICASTFRAMESFILTER_HASHTABLE ) || \ + ( ( FILTER ) == ETH_UNICASTFRAMESFILTER_PERFECT ) ) + #define IS_ETH_PAUSE_TIME( TIME ) ( ( TIME ) <= 0xFFFF ) + #define IS_ETH_ZEROQUANTA_PAUSE( CMD ) \ + ( ( ( CMD ) == ETH_ZEROQUANTAPAUSE_ENABLE ) || \ + ( ( CMD ) == ETH_ZEROQUANTAPAUSE_DISABLE ) ) + #define IS_ETH_PAUSE_LOW_THRESHOLD( THRESHOLD ) \ + ( ( ( THRESHOLD ) == ETH_PAUSELOWTHRESHOLD_MINUS4 ) || \ + ( ( THRESHOLD ) == ETH_PAUSELOWTHRESHOLD_MINUS28 ) || \ + ( ( THRESHOLD ) == ETH_PAUSELOWTHRESHOLD_MINUS144 ) || \ + ( ( THRESHOLD ) == ETH_PAUSELOWTHRESHOLD_MINUS256 ) ) + #define IS_ETH_UNICAST_PAUSE_FRAME_DETECT( CMD ) \ + ( ( ( CMD ) == ETH_UNICASTPAUSEFRAMEDETECT_ENABLE ) || \ + ( ( CMD ) == ETH_UNICASTPAUSEFRAMEDETECT_DISABLE ) ) + #define IS_ETH_RECEIVE_FLOWCONTROL( CMD ) \ + ( ( ( CMD ) == ETH_RECEIVEFLOWCONTROL_ENABLE ) || \ + ( ( CMD ) == ETH_RECEIVEFLOWCONTROL_DISABLE ) ) + #define IS_ETH_TRANSMIT_FLOWCONTROL( CMD ) \ + ( ( ( CMD ) == ETH_TRANSMITFLOWCONTROL_ENABLE ) || \ + ( ( CMD ) == ETH_TRANSMITFLOWCONTROL_DISABLE ) ) + #define IS_ETH_VLAN_TAG_COMPARISON( COMPARISON ) \ + ( ( ( COMPARISON ) == ETH_VLANTAGCOMPARISON_12BIT ) || \ + ( ( COMPARISON ) == ETH_VLANTAGCOMPARISON_16BIT ) ) + #define IS_ETH_VLAN_TAG_IDENTIFIER( IDENTIFIER ) ( ( IDENTIFIER ) <= 0xFFFF ) + #define IS_ETH_MAC_ADDRESS0123( ADDRESS ) \ + ( ( ( ADDRESS ) == ETH_MAC_ADDRESS0 ) || \ + ( ( ADDRESS ) == ETH_MAC_ADDRESS1 ) || \ + ( ( ADDRESS ) == ETH_MAC_ADDRESS2 ) || \ + ( ( ADDRESS ) == ETH_MAC_ADDRESS3 ) ) + #define IS_ETH_MAC_ADDRESS123( ADDRESS ) \ + ( ( ( ADDRESS ) == ETH_MAC_ADDRESS1 ) || \ + ( ( ADDRESS ) == ETH_MAC_ADDRESS2 ) || \ + ( ( ADDRESS ) == ETH_MAC_ADDRESS3 ) ) + #define IS_ETH_MAC_ADDRESS_FILTER( FILTER ) \ + ( ( ( FILTER ) == ETH_MAC_ADDRESSFILTER_SA ) || \ + ( ( FILTER ) == ETH_MAC_ADDRESSFILTER_DA ) ) + #define IS_ETH_MAC_ADDRESS_MASK( MASK ) \ + ( ( ( MASK ) == ETH_MAC_ADDRESSMASK_BYTE6 ) || \ + ( ( MASK ) == ETH_MAC_ADDRESSMASK_BYTE5 ) || \ + ( ( MASK ) == ETH_MAC_ADDRESSMASK_BYTE4 ) || \ + ( ( MASK ) == ETH_MAC_ADDRESSMASK_BYTE3 ) || \ + ( ( MASK ) == ETH_MAC_ADDRESSMASK_BYTE2 ) || \ + ( ( MASK ) == ETH_MAC_ADDRESSMASK_BYTE1 ) ) + #define IS_ETH_DROP_TCPIP_CHECKSUM_FRAME( CMD ) \ + ( ( ( CMD ) == ETH_DROPTCPIPCHECKSUMERRORFRAME_ENABLE ) || \ + ( ( CMD ) == ETH_DROPTCPIPCHECKSUMERRORFRAME_DISABLE ) ) + #define IS_ETH_RECEIVE_STORE_FORWARD( CMD ) \ + ( ( ( CMD ) == ETH_RECEIVESTOREFORWARD_ENABLE ) || \ + ( ( CMD ) == ETH_RECEIVESTOREFORWARD_DISABLE ) ) + #define IS_ETH_FLUSH_RECEIVE_FRAME( CMD ) \ + ( ( ( CMD ) == ETH_FLUSHRECEIVEDFRAME_ENABLE ) || \ + ( ( CMD ) == ETH_FLUSHRECEIVEDFRAME_DISABLE ) ) + #define IS_ETH_TRANSMIT_STORE_FORWARD( CMD ) \ + ( ( ( CMD ) == ETH_TRANSMITSTOREFORWARD_ENABLE ) || \ + ( ( CMD ) == ETH_TRANSMITSTOREFORWARD_DISABLE ) ) + #define IS_ETH_TRANSMIT_THRESHOLD_CONTROL( THRESHOLD ) \ + ( ( ( THRESHOLD ) == ETH_TRANSMITTHRESHOLDCONTROL_64BYTES ) || \ + ( ( THRESHOLD ) == ETH_TRANSMITTHRESHOLDCONTROL_128BYTES ) || \ + ( ( THRESHOLD ) == ETH_TRANSMITTHRESHOLDCONTROL_192BYTES ) || \ + ( ( THRESHOLD ) == ETH_TRANSMITTHRESHOLDCONTROL_256BYTES ) || \ + ( ( THRESHOLD ) == ETH_TRANSMITTHRESHOLDCONTROL_40BYTES ) || \ + ( ( THRESHOLD ) == ETH_TRANSMITTHRESHOLDCONTROL_32BYTES ) || \ + ( ( THRESHOLD ) == ETH_TRANSMITTHRESHOLDCONTROL_24BYTES ) || \ + ( ( THRESHOLD ) == ETH_TRANSMITTHRESHOLDCONTROL_16BYTES ) ) + #define IS_ETH_FORWARD_ERROR_FRAMES( CMD ) \ + ( ( ( CMD ) == ETH_FORWARDERRORFRAMES_ENABLE ) || \ + ( ( CMD ) == ETH_FORWARDERRORFRAMES_DISABLE ) ) + #define IS_ETH_FORWARD_UNDERSIZED_GOOD_FRAMES( CMD ) \ + ( ( ( CMD ) == ETH_FORWARDUNDERSIZEDGOODFRAMES_ENABLE ) || \ + ( ( CMD ) == ETH_FORWARDUNDERSIZEDGOODFRAMES_DISABLE ) ) + #define IS_ETH_RECEIVE_THRESHOLD_CONTROL( THRESHOLD ) \ + ( ( ( THRESHOLD ) == ETH_RECEIVEDTHRESHOLDCONTROL_64BYTES ) || \ + ( ( THRESHOLD ) == ETH_RECEIVEDTHRESHOLDCONTROL_32BYTES ) || \ + ( ( THRESHOLD ) == ETH_RECEIVEDTHRESHOLDCONTROL_96BYTES ) || \ + ( ( THRESHOLD ) == ETH_RECEIVEDTHRESHOLDCONTROL_128BYTES ) ) + #define IS_ETH_SECOND_FRAME_OPERATE( CMD ) \ + ( ( ( CMD ) == ETH_SECONDFRAMEOPERARTE_ENABLE ) || \ + ( ( CMD ) == ETH_SECONDFRAMEOPERARTE_DISABLE ) ) + #define IS_ETH_ADDRESS_ALIGNED_BEATS( CMD ) \ + ( ( ( CMD ) == ETH_ADDRESSALIGNEDBEATS_ENABLE ) || \ + ( ( CMD ) == ETH_ADDRESSALIGNEDBEATS_DISABLE ) ) + #define IS_ETH_FIXED_BURST( CMD ) \ + ( ( ( CMD ) == ETH_FIXEDBURST_ENABLE ) || \ + ( ( CMD ) == ETH_FIXEDBURST_DISABLE ) ) + #define IS_ETH_RXDMA_BURST_LENGTH( LENGTH ) \ + ( ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_1BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_2BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_4BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_8BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_16BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_32BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_4XPBL_4BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_4XPBL_8BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_4XPBL_16BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_4XPBL_32BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_4XPBL_64BEAT ) || \ + ( ( LENGTH ) == ETH_RXDMABURSTLENGTH_4XPBL_128BEAT ) ) + #define IS_ETH_TXDMA_BURST_LENGTH( LENGTH ) \ + ( ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_1BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_2BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_4BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_8BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_16BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_32BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_4XPBL_4BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_4XPBL_8BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_4XPBL_16BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_4XPBL_32BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_4XPBL_64BEAT ) || \ + ( ( LENGTH ) == ETH_TXDMABURSTLENGTH_4XPBL_128BEAT ) ) + #define IS_ETH_DMA_DESC_SKIP_LENGTH( LENGTH ) ( ( LENGTH ) <= 0x1F ) + #define IS_ETH_DMA_ARBITRATION_ROUNDROBIN_RXTX( RATIO ) \ + ( ( ( RATIO ) == ETH_DMAARBITRATION_ROUNDROBIN_RXTX_1_1 ) || \ + ( ( RATIO ) == ETH_DMAARBITRATION_ROUNDROBIN_RXTX_2_1 ) || \ + ( ( RATIO ) == ETH_DMAARBITRATION_ROUNDROBIN_RXTX_3_1 ) || \ + ( ( RATIO ) == ETH_DMAARBITRATION_ROUNDROBIN_RXTX_4_1 ) || \ + ( ( RATIO ) == ETH_DMAARBITRATION_RXPRIORTX ) ) + #define IS_ETH_DMATXDESC_GET_FLAG( FLAG ) \ + ( ( ( FLAG ) == ETH_DMATXDESC_OWN ) || \ + ( ( FLAG ) == ETH_DMATXDESC_IC ) || \ + ( ( FLAG ) == ETH_DMATXDESC_LS ) || \ + ( ( FLAG ) == ETH_DMATXDESC_FS ) || \ + ( ( FLAG ) == ETH_DMATXDESC_DC ) || \ + ( ( FLAG ) == ETH_DMATXDESC_DP ) || \ + ( ( FLAG ) == ETH_DMATXDESC_TTSE ) || \ + ( ( FLAG ) == ETH_DMATXDESC_TER ) || \ + ( ( FLAG ) == ETH_DMATXDESC_TCH ) || \ + ( ( FLAG ) == ETH_DMATXDESC_TTSS ) || \ + ( ( FLAG ) == ETH_DMATXDESC_IHE ) || \ + ( ( FLAG ) == ETH_DMATXDESC_ES ) || \ + ( ( FLAG ) == ETH_DMATXDESC_JT ) || \ + ( ( FLAG ) == ETH_DMATXDESC_FF ) || \ + ( ( FLAG ) == ETH_DMATXDESC_PCE ) || \ + ( ( FLAG ) == ETH_DMATXDESC_LCA ) || \ + ( ( FLAG ) == ETH_DMATXDESC_NC ) || \ + ( ( FLAG ) == ETH_DMATXDESC_LCO ) || \ + ( ( FLAG ) == ETH_DMATXDESC_EC ) || \ + ( ( FLAG ) == ETH_DMATXDESC_VF ) || \ + ( ( FLAG ) == ETH_DMATXDESC_CC ) || \ + ( ( FLAG ) == ETH_DMATXDESC_ED ) || \ + ( ( FLAG ) == ETH_DMATXDESC_UF ) || \ + ( ( FLAG ) == ETH_DMATXDESC_DB ) ) + #define IS_ETH_DMA_TXDESC_SEGMENT( SEGMENT ) \ + ( ( ( SEGMENT ) == ETH_DMATXDESC_LASTSEGMENTS ) || \ + ( ( SEGMENT ) == ETH_DMATXDESC_FIRSTSEGMENT ) ) + #define IS_ETH_DMA_TXDESC_CHECKSUM( CHECKSUM ) \ + ( ( ( CHECKSUM ) == ETH_DMATXDESC_CHECKSUMBYPASS ) || \ + ( ( CHECKSUM ) == ETH_DMATXDESC_CHECKSUMIPV4HEADER ) || \ + ( ( CHECKSUM ) == ETH_DMATXDESC_CHECKSUMTCPUDPICMPSEGMENT ) || \ + ( ( CHECKSUM ) == ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL ) ) + #define IS_ETH_DMATXDESC_BUFFER_SIZE( SIZE ) ( ( SIZE ) <= 0x1FFF ) + #define IS_ETH_DMARXDESC_GET_FLAG( FLAG ) \ + ( ( ( FLAG ) == ETH_DMARXDESC_OWN ) || \ + ( ( FLAG ) == ETH_DMARXDESC_AFM ) || \ + ( ( FLAG ) == ETH_DMARXDESC_ES ) || \ + ( ( FLAG ) == ETH_DMARXDESC_DE ) || \ + ( ( FLAG ) == ETH_DMARXDESC_SAF ) || \ + ( ( FLAG ) == ETH_DMARXDESC_LE ) || \ + ( ( FLAG ) == ETH_DMARXDESC_OE ) || \ + ( ( FLAG ) == ETH_DMARXDESC_VLAN ) || \ + ( ( FLAG ) == ETH_DMARXDESC_FS ) || \ + ( ( FLAG ) == ETH_DMARXDESC_LS ) || \ + ( ( FLAG ) == ETH_DMARXDESC_IPV4HCE ) || \ + ( ( FLAG ) == ETH_DMARXDESC_LC ) || \ + ( ( FLAG ) == ETH_DMARXDESC_FT ) || \ + ( ( FLAG ) == ETH_DMARXDESC_RWT ) || \ + ( ( FLAG ) == ETH_DMARXDESC_RE ) || \ + ( ( FLAG ) == ETH_DMARXDESC_DBE ) || \ + ( ( FLAG ) == ETH_DMARXDESC_CE ) || \ + ( ( FLAG ) == ETH_DMARXDESC_MAMPCE ) ) + #define IS_ETH_DMA_RXDESC_BUFFER( BUFFER ) \ + ( ( ( BUFFER ) == ETH_DMARXDESC_BUFFER1 ) || \ + ( ( BUFFER ) == ETH_DMARXDESC_BUFFER2 ) ) + #define IS_ETH_PMT_GET_FLAG( FLAG ) \ + ( ( ( FLAG ) == ETH_PMT_FLAG_WUFR ) || \ + ( ( FLAG ) == ETH_PMT_FLAG_MPR ) ) + #define IS_ETH_DMA_FLAG( FLAG ) ( ( ( ( FLAG ) &( uint32_t ) 0xC7FE1800 ) == 0x00 ) && ( ( FLAG ) != 0x00 ) ) + #define IS_ETH_DMA_GET_FLAG( FLAG ) \ + ( ( ( FLAG ) == ETH_DMA_FLAG_TST ) || ( ( FLAG ) == ETH_DMA_FLAG_PMT ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_MMC ) || ( ( FLAG ) == ETH_DMA_FLAG_DATATRANSFERERROR ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_READWRITEERROR ) || ( ( FLAG ) == ETH_DMA_FLAG_ACCESSERROR ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_NIS ) || ( ( FLAG ) == ETH_DMA_FLAG_AIS ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_ER ) || ( ( FLAG ) == ETH_DMA_FLAG_FBE ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_ET ) || ( ( FLAG ) == ETH_DMA_FLAG_RWT ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_RPS ) || ( ( FLAG ) == ETH_DMA_FLAG_RBU ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_R ) || ( ( FLAG ) == ETH_DMA_FLAG_TU ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_RO ) || ( ( FLAG ) == ETH_DMA_FLAG_TJT ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_TBU ) || ( ( FLAG ) == ETH_DMA_FLAG_TPS ) || \ + ( ( FLAG ) == ETH_DMA_FLAG_T ) ) + #define IS_ETH_MAC_IT( IT ) ( ( ( ( IT ) &( uint32_t ) 0xFFFFFDF1 ) == 0x00 ) && ( ( IT ) != 0x00 ) ) + #define IS_ETH_MAC_GET_IT( IT ) \ + ( ( ( IT ) == ETH_MAC_IT_TST ) || ( ( IT ) == ETH_MAC_IT_MMCT ) || \ + ( ( IT ) == ETH_MAC_IT_MMCR ) || ( ( IT ) == ETH_MAC_IT_MMC ) || \ + ( ( IT ) == ETH_MAC_IT_PMT ) ) + #define IS_ETH_MAC_GET_FLAG( FLAG ) \ + ( ( ( FLAG ) == ETH_MAC_FLAG_TST ) || ( ( FLAG ) == ETH_MAC_FLAG_MMCT ) || \ + ( ( FLAG ) == ETH_MAC_FLAG_MMCR ) || ( ( FLAG ) == ETH_MAC_FLAG_MMC ) || \ + ( ( FLAG ) == ETH_MAC_FLAG_PMT ) ) + #define IS_ETH_DMA_IT( IT ) ( ( ( ( IT ) &( uint32_t ) 0xC7FE1800 ) == 0x00 ) && ( ( IT ) != 0x00 ) ) + #define IS_ETH_DMA_GET_IT( IT ) \ + ( ( ( IT ) == ETH_DMA_IT_TST ) || ( ( IT ) == ETH_DMA_IT_PMT ) || \ + ( ( IT ) == ETH_DMA_IT_MMC ) || ( ( IT ) == ETH_DMA_IT_NIS ) || \ + ( ( IT ) == ETH_DMA_IT_AIS ) || ( ( IT ) == ETH_DMA_IT_ER ) || \ + ( ( IT ) == ETH_DMA_IT_FBE ) || ( ( IT ) == ETH_DMA_IT_ET ) || \ + ( ( IT ) == ETH_DMA_IT_RWT ) || ( ( IT ) == ETH_DMA_IT_RPS ) || \ + ( ( IT ) == ETH_DMA_IT_RBU ) || ( ( IT ) == ETH_DMA_IT_R ) || \ + ( ( IT ) == ETH_DMA_IT_TU ) || ( ( IT ) == ETH_DMA_IT_RO ) || \ + ( ( IT ) == ETH_DMA_IT_TJT ) || ( ( IT ) == ETH_DMA_IT_TBU ) || \ + ( ( IT ) == ETH_DMA_IT_TPS ) || ( ( IT ) == ETH_DMA_IT_T ) ) + #define IS_ETH_DMA_GET_OVERFLOW( OVERFLOW ) \ + ( ( ( OVERFLOW ) == ETH_DMA_OVERFLOW_RXFIFOCOUNTER ) || \ + ( ( OVERFLOW ) == ETH_DMA_OVERFLOW_MISSEDFRAMECOUNTER ) ) + #define IS_ETH_MMC_IT( IT ) \ + ( ( ( ( ( IT ) &( uint32_t ) 0xFFDF3FFF ) == 0x00 ) || ( ( ( IT ) &( uint32_t ) 0xEFFDFF9F ) == 0x00 ) ) && \ + ( ( IT ) != 0x00 ) ) + #define IS_ETH_MMC_GET_IT( IT ) \ + ( ( ( IT ) == ETH_MMC_IT_TGF ) || ( ( IT ) == ETH_MMC_IT_TGFMSC ) || \ + ( ( IT ) == ETH_MMC_IT_TGFSC ) || ( ( IT ) == ETH_MMC_IT_RGUF ) || \ + ( ( IT ) == ETH_MMC_IT_RFAE ) || ( ( IT ) == ETH_MMC_IT_RFCE ) ) + #define IS_ETH_ENHANCED_DESCRIPTOR_FORMAT( CMD ) \ + ( ( ( CMD ) == ETH_DMAENHANCEDDESCRIPTOR_ENABLE ) || \ + ( ( CMD ) == ETH_DMAENHANCEDDESCRIPTOR_DISABLE ) ) + + +/** + * @} + */ + +/** @addtogroup ETH_Private_Defines + * @{ + */ +/* Delay to wait when writing to some Ethernet registers */ + #define ETH_REG_WRITE_DELAY ( ( uint32_t ) 0x00000001U ) + +/* Ethernet Errors */ + #define ETH_SUCCESS ( ( uint32_t ) 0U ) + #define ETH_ERROR ( ( uint32_t ) 1U ) + +/* Ethernet DMA Tx descriptors Collision Count Shift */ + #define ETH_DMATXDESC_COLLISION_COUNTSHIFT ( ( uint32_t ) 3U ) + +/* Ethernet DMA Tx descriptors Buffer2 Size Shift */ + #define ETH_DMATXDESC_BUFFER2_SIZESHIFT ( ( uint32_t ) 16U ) + +/* Ethernet DMA Rx descriptors Frame Length Shift */ + #define ETH_DMARXDESC_FRAME_LENGTHSHIFT ( ( uint32_t ) 16U ) + +/* Ethernet DMA Rx descriptors Buffer2 Size Shift */ + #define ETH_DMARXDESC_BUFFER2_SIZESHIFT ( ( uint32_t ) 16U ) + +/* Ethernet DMA Rx descriptors Frame length Shift */ + #define ETH_DMARXDESC_FRAMELENGTHSHIFT ( ( uint32_t ) 16U ) + +/* Ethernet MAC address offsets */ + #define ETH_MAC_ADDR_HBASE ( uint32_t ) ( ETH_MAC_BASE + ( uint32_t ) 0x40U ) /* Ethernet MAC address high offset */ + #define ETH_MAC_ADDR_LBASE ( uint32_t ) ( ETH_MAC_BASE + ( uint32_t ) 0x44U ) /* Ethernet MAC address low offset */ + +/* Ethernet MACMIIAR register Mask */ + #define ETH_MACMIIAR_CR_MASK ( ( uint32_t ) 0xFFFFFFE3U ) + +/* Ethernet MACCR register Mask */ + #define ETH_MACCR_CLEAR_MASK ( ( uint32_t ) 0xFF20810FU ) + +/* Ethernet MACFCR register Mask */ + #define ETH_MACFCR_CLEAR_MASK ( ( uint32_t ) 0x0000FF41U ) + +/* Ethernet DMAOMR register Mask */ + #define ETH_DMAOMR_CLEAR_MASK ( ( uint32_t ) 0xF8DE3F23U ) + +/* Ethernet Remote Wake-up frame register length */ + #define ETH_WAKEUP_REGISTER_LENGTH 8U + +/* Ethernet Missed frames counter Shift */ + #define ETH_DMA_RX_OVERFLOW_MISSEDFRAMES_COUNTERSHIFT 17U + +/** + * @} + */ + + #ifdef _lint + #ifdef __IO + #undef __IO + #endif + #define __IO + + #ifdef ETH_TypeDef + #undef ETH_TypeDef + #endif + #define ETH_TypeDef void + + #ifdef HAL_LockTypeDef + #undef HAL_LockTypeDef + #endif + #define HAL_LockTypeDef unsigned + + #ifdef ETH_RX_BUF_SIZE + #undef ETH_RX_BUF_SIZE + #endif + #define ETH_RX_BUF_SIZE 1536 + + #ifdef ETH_TX_BUF_SIZE + #undef ETH_TX_BUF_SIZE + #endif + #define ETH_TX_BUF_SIZE 1536 + #endif /* ifdef _lint */ + +/* Exported types ------------------------------------------------------------*/ + +/** @defgroup ETH_Exported_Types ETH Exported Types + * @{ + */ + +/** + * @brief HAL State structures definition + */ + typedef enum + { + HAL_ETH_STATE_RESET = 0x00U, /*!< Peripheral not yet Initialized or disabled */ + HAL_ETH_STATE_READY = 0x01U, /*!< Peripheral Initialized and ready for use */ + HAL_ETH_STATE_BUSY = 0x02U, /*!< an internal process is ongoing */ + HAL_ETH_STATE_BUSY_TX = 0x12U, /*!< Data Transmission process is ongoing */ + HAL_ETH_STATE_BUSY_RX = 0x22U, /*!< Data Reception process is ongoing */ + HAL_ETH_STATE_BUSY_TX_RX = 0x32U, /*!< Data Transmission and Reception process is ongoing */ + HAL_ETH_STATE_BUSY_WR = 0x42U, /*!< Write process is ongoing */ + HAL_ETH_STATE_BUSY_RD = 0x82U, /*!< Read process is ongoing */ + HAL_ETH_STATE_TIMEOUT = 0x03U, /*!< Timeout state */ + HAL_ETH_STATE_ERROR = 0x04U /*!< Reception process is ongoing */ + } HAL_ETH_StateTypeDef; + +/** + * @brief ETH Init Structure definition + */ + + typedef struct + { + uint32_t AutoNegotiation; /*!< Selects or not the AutoNegotiation mode for the external PHY + * The AutoNegotiation allows an automatic setting of the Speed (10/100Mbps) + * and the mode (half/full-duplex). + * This parameter can be a value of @ref ETH_AutoNegotiation */ + + uint32_t Speed; /*!< Sets the Ethernet speed: 10/100 Mbps. + * This parameter can be a value of @ref ETH_Speed */ + + uint32_t DuplexMode; /*!< Selects the MAC duplex mode: Half-Duplex or Full-Duplex mode + * This parameter can be a value of @ref ETH_Duplex_Mode */ + + uint16_t PhyAddress; /*!< Ethernet PHY address. + * This parameter must be a number between Min_Data = 0 and Max_Data = 32 */ + + uint8_t * MACAddr; /*!< MAC Address of used Hardware: must be pointer on an array of 6 bytes */ + + uint32_t RxMode; /*!< Selects the Ethernet Rx mode: Polling mode, Interrupt mode. + * This parameter can be a value of @ref ETH_Rx_Mode */ + + uint32_t ChecksumMode; /*!< Selects if the checksum is check by hardware or by software. + * This parameter can be a value of @ref ETH_Checksum_Mode */ + + uint32_t MediaInterface; /*!< Selects the media-independent interface or the reduced media-independent interface. + * This parameter can be a value of @ref ETH_Media_Interface */ + } ETH_InitTypeDef; + + +/** + * @brief ETH MAC Configuration Structure definition + */ + + typedef struct + { + uint32_t Watchdog; /*!< Selects or not the Watchdog timer + * When enabled, the MAC allows no more then 2048 bytes to be received. + * When disabled, the MAC can receive up to 16384 bytes. + * This parameter can be a value of @ref ETH_Watchdog */ + + uint32_t Jabber; /*!< Selects or not Jabber timer + * When enabled, the MAC allows no more then 2048 bytes to be sent. + * When disabled, the MAC can send up to 16384 bytes. + * This parameter can be a value of @ref ETH_Jabber */ + + uint32_t InterFrameGap; /*!< Selects the minimum IFG between frames during transmission. + * This parameter can be a value of @ref ETH_Inter_Frame_Gap */ + + uint32_t CarrierSense; /*!< Selects or not the Carrier Sense. + * This parameter can be a value of @ref ETH_Carrier_Sense */ + + uint32_t ReceiveOwn; /*!< Selects or not the ReceiveOwn, + * ReceiveOwn allows the reception of frames when the TX_EN signal is asserted + * in Half-Duplex mode. + * This parameter can be a value of @ref ETH_Receive_Own */ + + uint32_t LoopbackMode; /*!< Selects or not the internal MAC MII Loopback mode. + * This parameter can be a value of @ref ETH_Loop_Back_Mode */ + + uint32_t ChecksumOffload; /*!< Selects or not the IPv4 checksum checking for received frame payloads' TCP/UDP/ICMP headers. + * This parameter can be a value of @ref ETH_Checksum_Offload */ + + uint32_t RetryTransmission; /*!< Selects or not the MAC attempt retries transmission, based on the settings of BL, + * when a collision occurs (Half-Duplex mode). + * This parameter can be a value of @ref ETH_Retry_Transmission */ + + uint32_t AutomaticPadCRCStrip; /*!< Selects or not the Automatic MAC Pad/CRC Stripping. + * This parameter can be a value of @ref ETH_Automatic_Pad_CRC_Strip */ + + uint32_t BackOffLimit; /*!< Selects the BackOff limit value. + * This parameter can be a value of @ref ETH_Back_Off_Limit */ + + uint32_t DeferralCheck; /*!< Selects or not the deferral check function (Half-Duplex mode). + * This parameter can be a value of @ref ETH_Deferral_Check */ + + uint32_t ReceiveAll; /*!< Selects or not all frames reception by the MAC (No filtering). + * This parameter can be a value of @ref ETH_Receive_All */ + + uint32_t SourceAddrFilter; /*!< Selects the Source Address Filter mode. + * This parameter can be a value of @ref ETH_Source_Addr_Filter */ + + uint32_t PassControlFrames; /*!< Sets the forwarding mode of the control frames (including unicast and multicast PAUSE frames) + * This parameter can be a value of @ref ETH_Pass_Control_Frames */ + + uint32_t BroadcastFramesReception; /*!< Selects or not the reception of Broadcast Frames. + * This parameter can be a value of @ref ETH_Broadcast_Frames_Reception */ + + uint32_t DestinationAddrFilter; /*!< Sets the destination filter mode for both unicast and multicast frames. + * This parameter can be a value of @ref ETH_Destination_Addr_Filter */ + + uint32_t PromiscuousMode; /*!< Selects or not the Promiscuous Mode + * This parameter can be a value of @ref ETH_Promiscuous_Mode */ + + uint32_t MulticastFramesFilter; /*!< Selects the Multicast Frames filter mode: None/HashTableFilter/PerfectFilter/PerfectHashTableFilter. + * This parameter can be a value of @ref ETH_Multicast_Frames_Filter */ + + uint32_t UnicastFramesFilter; /*!< Selects the Unicast Frames filter mode: HashTableFilter/PerfectFilter/PerfectHashTableFilter. + * This parameter can be a value of @ref ETH_Unicast_Frames_Filter */ + + uint32_t HashTableHigh; /*!< This field holds the higher 32 bits of Hash table. + * This parameter must be a number between Min_Data = 0x0 and Max_Data = 0xFFFFFFFF */ + + uint32_t HashTableLow; /*!< This field holds the lower 32 bits of Hash table. + * This parameter must be a number between Min_Data = 0x0 and Max_Data = 0xFFFFFFFF */ + + uint32_t PauseTime; /*!< This field holds the value to be used in the Pause Time field in the transmit control frame. + * This parameter must be a number between Min_Data = 0x0 and Max_Data = 0xFFFF */ + + uint32_t ZeroQuantaPause; /*!< Selects or not the automatic generation of Zero-Quanta Pause Control frames. + * This parameter can be a value of @ref ETH_Zero_Quanta_Pause */ + + uint32_t PauseLowThreshold; /*!< This field configures the threshold of the PAUSE to be checked for + * automatic retransmission of PAUSE Frame. + * This parameter can be a value of @ref ETH_Pause_Low_Threshold */ + + uint32_t UnicastPauseFrameDetect; /*!< Selects or not the MAC detection of the Pause frames (with MAC Address0 + * unicast address and unique multicast address). + * This parameter can be a value of @ref ETH_Unicast_Pause_Frame_Detect */ + + uint32_t ReceiveFlowControl; /*!< Enables or disables the MAC to decode the received Pause frame and + * disable its transmitter for a specified time (Pause Time) + * This parameter can be a value of @ref ETH_Receive_Flow_Control */ + + uint32_t TransmitFlowControl; /*!< Enables or disables the MAC to transmit Pause frames (Full-Duplex mode) + * or the MAC back-pressure operation (Half-Duplex mode) + * This parameter can be a value of @ref ETH_Transmit_Flow_Control */ + + uint32_t VLANTagComparison; /*!< Selects the 12-bit VLAN identifier or the complete 16-bit VLAN tag for + * comparison and filtering. + * This parameter can be a value of @ref ETH_VLAN_Tag_Comparison */ + + uint32_t VLANTagIdentifier; /*!< Holds the VLAN tag identifier for receive frames */ + } ETH_MACInitTypeDef; + + +/** + * @brief ETH DMA Configuration Structure definition + */ + + typedef struct + { + uint32_t DropTCPIPChecksumErrorFrame; /*!< Selects or not the Dropping of TCP/IP Checksum Error Frames. + * This parameter can be a value of @ref ETH_Drop_TCP_IP_Checksum_Error_Frame */ + + uint32_t ReceiveStoreForward; /*!< Enables or disables the Receive store and forward mode. + * This parameter can be a value of @ref ETH_Receive_Store_Forward */ + + uint32_t FlushReceivedFrame; /*!< Enables or disables the flushing of received frames. + * This parameter can be a value of @ref ETH_Flush_Received_Frame */ + + uint32_t TransmitStoreForward; /*!< Enables or disables Transmit store and forward mode. + * This parameter can be a value of @ref ETH_Transmit_Store_Forward */ + + uint32_t TransmitThresholdControl; /*!< Selects or not the Transmit Threshold Control. + * This parameter can be a value of @ref ETH_Transmit_Threshold_Control */ + + uint32_t ForwardErrorFrames; /*!< Selects or not the forward to the DMA of erroneous frames. + * This parameter can be a value of @ref ETH_Forward_Error_Frames */ + + uint32_t ForwardUndersizedGoodFrames; /*!< Enables or disables the Rx FIFO to forward Undersized frames (frames with no Error + * and length less than 64 bytes) including pad-bytes and CRC) + * This parameter can be a value of @ref ETH_Forward_Undersized_Good_Frames */ + + uint32_t ReceiveThresholdControl; /*!< Selects the threshold level of the Receive FIFO. + * This parameter can be a value of @ref ETH_Receive_Threshold_Control */ + + uint32_t SecondFrameOperate; /*!< Selects or not the Operate on second frame mode, which allows the DMA to process a second + * frame of Transmit data even before obtaining the status for the first frame. + * This parameter can be a value of @ref ETH_Second_Frame_Operate */ + + uint32_t AddressAlignedBeats; /*!< Enables or disables the Address Aligned Beats. + * This parameter can be a value of @ref ETH_Address_Aligned_Beats */ + + uint32_t FixedBurst; /*!< Enables or disables the AHB Master interface fixed burst transfers. + * This parameter can be a value of @ref ETH_Fixed_Burst */ + + uint32_t RxDMABurstLength; /*!< Indicates the maximum number of beats to be transferred in one Rx DMA transaction. + * This parameter can be a value of @ref ETH_Rx_DMA_Burst_Length */ + + uint32_t TxDMABurstLength; /*!< Indicates the maximum number of beats to be transferred in one Tx DMA transaction. + * This parameter can be a value of @ref ETH_Tx_DMA_Burst_Length */ + + uint32_t EnhancedDescriptorFormat; /*!< Enables the enhanced descriptor format. + * This parameter can be a value of @ref ETH_DMA_Enhanced_descriptor_format */ + + uint32_t DescriptorSkipLength; /*!< Specifies the number of word to skip between two unchained descriptors (Ring mode) + * This parameter must be a number between Min_Data = 0 and Max_Data = 32 */ + + uint32_t DMAArbitration; /*!< Selects the DMA Tx/Rx arbitration. + * This parameter can be a value of @ref ETH_DMA_Arbitration */ + } ETH_DMAInitTypeDef; + + +/** + * @brief ETH DMA Descriptors data structure definition + */ + + typedef struct + { + __IO uint32_t Status; /*!< Status */ + + uint32_t ControlBufferSize; /*!< Control and Buffer1, Buffer2 lengths */ + + uint32_t Buffer1Addr; /*!< Buffer1 address pointer */ + + uint32_t Buffer2NextDescAddr; /*!< Buffer2 or next descriptor address pointer */ + + /*!< Enhanced Ethernet DMA PTP Descriptors */ + uint32_t ExtendedStatus; /*!< Extended status for PTP receive descriptor */ + + uint32_t Reserved1; /*!< Reserved */ + + uint32_t TimeStampLow; /*!< Time Stamp Low value for transmit and receive */ + + uint32_t TimeStampHigh; /*!< Time Stamp High value for transmit and receive */ + } ETH_DMADescTypeDef; + + +/** + * @brief Received Frame Informations structure definition + */ + typedef struct + { + ETH_DMADescTypeDef * FSRxDesc; /*!< First Segment Rx Desc */ + + ETH_DMADescTypeDef * LSRxDesc; /*!< Last Segment Rx Desc */ + + uint32_t SegCount; /*!< Segment count */ + + uint32_t length; /*!< Frame length */ + + uint32_t buffer; /*!< Frame buffer */ + } ETH_DMARxFrameInfos; + + +/** + * @brief ETH Handle Structure definition + */ + + typedef struct + { + ETH_TypeDef * Instance; /*!< Register base address */ + + ETH_InitTypeDef Init; /*!< Ethernet Init Configuration */ + + uint32_t LinkStatus; /*!< Ethernet link status */ + + ETH_DMADescTypeDef * RxDesc; /*!< Rx descriptor to Get */ + + ETH_DMADescTypeDef * TxDesc; /*!< Tx descriptor to Set */ + + ETH_DMARxFrameInfos RxFrameInfos; /*!< last Rx frame infos */ + + __IO HAL_ETH_StateTypeDef State; /*!< ETH communication state */ + + HAL_LockTypeDef Lock; /*!< ETH Lock */ + } ETH_HandleTypeDef; + +/** + * @} + */ + +/* Exported constants --------------------------------------------------------*/ + +/** @defgroup ETH_Exported_Constants ETH Exported Constants + * @{ + */ + +/** @defgroup ETH_Buffers_setting ETH Buffers setting + * @{ + */ + #define ETH_MAX_PACKET_SIZE ( ( uint32_t ) 1536U ) /*!< ETH_HEADER + ETH_EXTRA + ETH_VLAN_TAG + ETH_MAX_ETH_PAYLOAD + ETH_CRC */ + #define ETH_HEADER ( ( uint32_t ) 14U ) /*!< 6 byte Dest addr, 6 byte Src addr, 2 byte length/type */ + #define ETH_CRC ( ( uint32_t ) 4U ) /*!< Ethernet CRC */ + #define ETH_EXTRA ( ( uint32_t ) 2U ) /*!< Extra bytes in some cases */ + #define ETH_VLAN_TAG ( ( uint32_t ) 4U ) /*!< optional 802.1q VLAN Tag */ + #define ETH_MIN_ETH_PAYLOAD ( ( uint32_t ) 46U ) /*!< Minimum Ethernet payload size */ + #define ETH_MAX_ETH_PAYLOAD ( ( uint32_t ) 1500U ) /*!< Maximum Ethernet payload size */ + #define ETH_JUMBO_FRAME_PAYLOAD ( ( uint32_t ) 9000U ) /*!< Jumbo frame payload size */ + +/* Ethernet driver receive buffers are organized in a chained linked-list, when + * an Ethernet packet is received, the Rx-DMA will transfer the packet from RxFIFO + * to the driver receive buffers memory. + * + * Depending on the size of the received Ethernet packet and the size of + * each Ethernet driver receive buffer, the received packet can take one or more + * Ethernet driver receive buffer. + * + * In below are defined the size of one Ethernet driver receive buffer ETH_RX_BUF_SIZE + * and the total count of the driver receive buffers ETH_RXBUFNB. + * + * The configured value for ETH_RX_BUF_SIZE and ETH_RXBUFNB are only provided as + * example, they can be reconfigured in the application layer to fit the application + * needs */ + +/* Here we configure each Ethernet driver receive buffer to fit the Max size Ethernet + * packet */ + #ifndef ETH_RX_BUF_SIZE + #error please define ETH_RX_BUF_SIZE + #define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE + #endif + +/* 5 Ethernet driver receive buffers are used (in a chained linked list)*/ + #ifndef ETH_RXBUFNB + #define ETH_RXBUFNB ( ( uint32_t ) 5U ) /* 5 Rx buffers of size ETH_RX_BUF_SIZE */ + #endif + + +/* Ethernet driver transmit buffers are organized in a chained linked-list, when + * an Ethernet packet is transmitted, Tx-DMA will transfer the packet from the + * driver transmit buffers memory to the TxFIFO. + * + * Depending on the size of the Ethernet packet to be transmitted and the size of + * each Ethernet driver transmit buffer, the packet to be transmitted can take + * one or more Ethernet driver transmit buffer. + * + * In below are defined the size of one Ethernet driver transmit buffer ETH_TX_BUF_SIZE + * and the total count of the driver transmit buffers ETH_TXBUFNB. + * + * The configured value for ETH_TX_BUF_SIZE and ETH_TXBUFNB are only provided as + * example, they can be reconfigured in the application layer to fit the application + * needs */ + +/* Here we configure each Ethernet driver transmit buffer to fit the Max size Ethernet + * packet */ + #ifndef ETH_TX_BUF_SIZE + #error please define ETH_TX_BUF_SIZE + #define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE + #endif + +/* 5 Ethernet driver transmit buffers are used (in a chained linked list)*/ + #ifndef ETH_TXBUFNB + #define ETH_TXBUFNB ( ( uint32_t ) 5U ) /* 5 Tx buffers of size ETH_TX_BUF_SIZE */ + #endif + +/** + * @} + */ + +/** @defgroup ETH_DMA_TX_Descriptor ETH DMA TX Descriptor + * @{ + */ + +/* + * DMA Tx Descriptor + * ----------------------------------------------------------------------------------------------- + * TDES0 | OWN(31) | CTRL[30:26] | Reserved[25:24] | CTRL[23:20] | Reserved[19:17] | Status[16:0] | + * ----------------------------------------------------------------------------------------------- + * TDES1 | Reserved[31:29] | Buffer2 ByteCount[28:16] | Reserved[15:13] | Buffer1 ByteCount[12:0] | + * ----------------------------------------------------------------------------------------------- + * TDES2 | Buffer1 Address [31:0] | + * ----------------------------------------------------------------------------------------------- + * TDES3 | Buffer2 Address [31:0] / Next Descriptor Address [31:0] | + * ----------------------------------------------------------------------------------------------- + */ + +/** + * @brief Bit definition of TDES0 register: DMA Tx descriptor status register + */ + #define ETH_DMATXDESC_OWN ( ( uint32_t ) 0x80000000U ) /*!< OWN bit: descriptor is owned by DMA engine */ + #define ETH_DMATXDESC_IC ( ( uint32_t ) 0x40000000U ) /*!< Interrupt on Completion */ + #define ETH_DMATXDESC_LS ( ( uint32_t ) 0x20000000U ) /*!< Last Segment */ + #define ETH_DMATXDESC_FS ( ( uint32_t ) 0x10000000U ) /*!< First Segment */ + #define ETH_DMATXDESC_DC ( ( uint32_t ) 0x08000000U ) /*!< Disable CRC */ + #define ETH_DMATXDESC_DP ( ( uint32_t ) 0x04000000U ) /*!< Disable Padding */ + #define ETH_DMATXDESC_TTSE ( ( uint32_t ) 0x02000000U ) /*!< Transmit Time Stamp Enable */ + #define ETH_DMATXDESC_CIC ( ( uint32_t ) 0x00C00000U ) /*!< Checksum Insertion Control: 4 cases */ + #define ETH_DMATXDESC_CIC_BYPASS ( ( uint32_t ) 0x00000000U ) /*!< Do Nothing: Checksum Engine is bypassed */ + #define ETH_DMATXDESC_CIC_IPV4HEADER ( ( uint32_t ) 0x00400000U ) /*!< IPV4 header Checksum Insertion */ + #define ETH_DMATXDESC_CIC_TCPUDPICMP_SEGMENT ( ( uint32_t ) 0x00800000U ) /*!< TCP/UDP/ICMP Checksum Insertion calculated over segment only */ + #define ETH_DMATXDESC_CIC_TCPUDPICMP_FULL ( ( uint32_t ) 0x00C00000U ) /*!< TCP/UDP/ICMP Checksum Insertion fully calculated */ + #define ETH_DMATXDESC_TER ( ( uint32_t ) 0x00200000U ) /*!< Transmit End of Ring */ + #define ETH_DMATXDESC_TCH ( ( uint32_t ) 0x00100000U ) /*!< Second Address Chained */ + #define ETH_DMATXDESC_TTSS ( ( uint32_t ) 0x00020000U ) /*!< Tx Time Stamp Status */ + #define ETH_DMATXDESC_IHE ( ( uint32_t ) 0x00010000U ) /*!< IP Header Error */ + #define ETH_DMATXDESC_ES ( ( uint32_t ) 0x00008000U ) /*!< Error summary: OR of the following bits: UE || ED || EC || LCO || NC || LCA || FF || JT */ + #define ETH_DMATXDESC_JT ( ( uint32_t ) 0x00004000U ) /*!< Jabber Timeout */ + #define ETH_DMATXDESC_FF ( ( uint32_t ) 0x00002000U ) /*!< Frame Flushed: DMA/MTL flushed the frame due to SW flush */ + #define ETH_DMATXDESC_PCE ( ( uint32_t ) 0x00001000U ) /*!< Payload Checksum Error */ + #define ETH_DMATXDESC_LCA ( ( uint32_t ) 0x00000800U ) /*!< Loss of Carrier: carrier lost during transmission */ + #define ETH_DMATXDESC_NC ( ( uint32_t ) 0x00000400U ) /*!< No Carrier: no carrier signal from the transceiver */ + #define ETH_DMATXDESC_LCO ( ( uint32_t ) 0x00000200U ) /*!< Late Collision: transmission aborted due to collision */ + #define ETH_DMATXDESC_EC ( ( uint32_t ) 0x00000100U ) /*!< Excessive Collision: transmission aborted after 16 collisions */ + #define ETH_DMATXDESC_VF ( ( uint32_t ) 0x00000080U ) /*!< VLAN Frame */ + #define ETH_DMATXDESC_CC ( ( uint32_t ) 0x00000078U ) /*!< Collision Count */ + #define ETH_DMATXDESC_ED ( ( uint32_t ) 0x00000004U ) /*!< Excessive Deferral */ + #define ETH_DMATXDESC_UF ( ( uint32_t ) 0x00000002U ) /*!< Underflow Error: late data arrival from the memory */ + #define ETH_DMATXDESC_DB ( ( uint32_t ) 0x00000001U ) /*!< Deferred Bit */ + +/** + * @brief Bit definition of TDES1 register + */ + #define ETH_DMATXDESC_TBS2 ( ( uint32_t ) 0x1FFF0000U ) /*!< Transmit Buffer2 Size */ + #define ETH_DMATXDESC_TBS1 ( ( uint32_t ) 0x00001FFFU ) /*!< Transmit Buffer1 Size */ + +/** + * @brief Bit definition of TDES2 register + */ + #define ETH_DMATXDESC_B1AP ( ( uint32_t ) 0xFFFFFFFFU ) /*!< Buffer1 Address Pointer */ + +/** + * @brief Bit definition of TDES3 register + */ + #define ETH_DMATXDESC_B2AP ( ( uint32_t ) 0xFFFFFFFFU ) /*!< Buffer2 Address Pointer */ + +/*--------------------------------------------------------------------------------------------- + * TDES6 | Transmit Time Stamp Low [31:0] | + * ----------------------------------------------------------------------------------------------- + * TDES7 | Transmit Time Stamp High [31:0] | + * ----------------------------------------------------------------------------------------------*/ + +/* Bit definition of TDES6 register */ + #define ETH_DMAPTPTXDESC_TTSL ( ( uint32_t ) 0xFFFFFFFFU ) /* Transmit Time Stamp Low */ + +/* Bit definition of TDES7 register */ + #define ETH_DMAPTPTXDESC_TTSH ( ( uint32_t ) 0xFFFFFFFFU ) /* Transmit Time Stamp High */ + +/** + * @} + */ + +/** @defgroup ETH_DMA_RX_Descriptor ETH DMA RX Descriptor + * @{ + */ + +/* + * DMA Rx Descriptor + * -------------------------------------------------------------------------------------------------------------------- + * RDES0 | OWN(31) | Status [30:0] | + * --------------------------------------------------------------------------------------------------------------------- + * RDES1 | CTRL(31) | Reserved[30:29] | Buffer2 ByteCount[28:16] | CTRL[15:14] | Reserved(13) | Buffer1 ByteCount[12:0] | + * --------------------------------------------------------------------------------------------------------------------- + * RDES2 | Buffer1 Address [31:0] | + * --------------------------------------------------------------------------------------------------------------------- + * RDES3 | Buffer2 Address [31:0] / Next Descriptor Address [31:0] | + * --------------------------------------------------------------------------------------------------------------------- + */ + +/** + * @brief Bit definition of RDES0 register: DMA Rx descriptor status register + */ + #define ETH_DMARXDESC_OWN ( ( uint32_t ) 0x80000000U ) /*!< OWN bit: descriptor is owned by DMA engine */ + #define ETH_DMARXDESC_AFM ( ( uint32_t ) 0x40000000U ) /*!< DA Filter Fail for the rx frame */ + #define ETH_DMARXDESC_FL ( ( uint32_t ) 0x3FFF0000U ) /*!< Receive descriptor frame length */ + #define ETH_DMARXDESC_ES ( ( uint32_t ) 0x00008000U ) /*!< Error summary: OR of the following bits: DE || OE || IPC || LC || RWT || RE || CE */ + #define ETH_DMARXDESC_DE ( ( uint32_t ) 0x00004000U ) /*!< Descriptor error: no more descriptors for receive frame */ + #define ETH_DMARXDESC_SAF ( ( uint32_t ) 0x00002000U ) /*!< SA Filter Fail for the received frame */ + #define ETH_DMARXDESC_LE ( ( uint32_t ) 0x00001000U ) /*!< Frame size not matching with length field */ + #define ETH_DMARXDESC_OE ( ( uint32_t ) 0x00000800U ) /*!< Overflow Error: Frame was damaged due to buffer overflow */ + #define ETH_DMARXDESC_VLAN ( ( uint32_t ) 0x00000400U ) /*!< VLAN Tag: received frame is a VLAN frame */ + #define ETH_DMARXDESC_FS ( ( uint32_t ) 0x00000200U ) /*!< First descriptor of the frame */ + #define ETH_DMARXDESC_LS ( ( uint32_t ) 0x00000100U ) /*!< Last descriptor of the frame */ + #define ETH_DMARXDESC_IPV4HCE ( ( uint32_t ) 0x00000080U ) /*!< IPC Checksum Error: Rx Ipv4 header checksum error */ + #define ETH_DMARXDESC_LC ( ( uint32_t ) 0x00000040U ) /*!< Late collision occurred during reception */ + #define ETH_DMARXDESC_FT ( ( uint32_t ) 0x00000020U ) /*!< Frame type - Ethernet, otherwise 802.3 */ + #define ETH_DMARXDESC_RWT ( ( uint32_t ) 0x00000010U ) /*!< Receive Watchdog Timeout: watchdog timer expired during reception */ + #define ETH_DMARXDESC_RE ( ( uint32_t ) 0x00000008U ) /*!< Receive error: error reported by MII interface */ + #define ETH_DMARXDESC_DBE ( ( uint32_t ) 0x00000004U ) /*!< Dribble bit error: frame contains non int multiple of 8 bits */ + #define ETH_DMARXDESC_CE ( ( uint32_t ) 0x00000002U ) /*!< CRC error */ + #define ETH_DMARXDESC_MAMPCE ( ( uint32_t ) 0x00000001U ) /*!< Rx MAC Address/Payload Checksum Error: Rx MAC address matched/ Rx Payload Checksum Error */ + +/** + * @brief Bit definition of RDES1 register + */ + #define ETH_DMARXDESC_DIC ( ( uint32_t ) 0x80000000U ) /*!< Disable Interrupt on Completion */ + #define ETH_DMARXDESC_RBS2 ( ( uint32_t ) 0x1FFF0000U ) /*!< Receive Buffer2 Size */ + #define ETH_DMARXDESC_RER ( ( uint32_t ) 0x00008000U ) /*!< Receive End of Ring */ + #define ETH_DMARXDESC_RCH ( ( uint32_t ) 0x00004000U ) /*!< Second Address Chained */ + #define ETH_DMARXDESC_RBS1 ( ( uint32_t ) 0x00001FFFU ) /*!< Receive Buffer1 Size */ + +/** + * @brief Bit definition of RDES2 register + */ + #define ETH_DMARXDESC_B1AP ( ( uint32_t ) 0xFFFFFFFFU ) /*!< Buffer1 Address Pointer */ + +/** + * @brief Bit definition of RDES3 register + */ + #define ETH_DMARXDESC_B2AP ( ( uint32_t ) 0xFFFFFFFFU ) /*!< Buffer2 Address Pointer */ + +/*--------------------------------------------------------------------------------------------------------------------- + * RDES4 | Reserved[31:15] | Extended Status [14:0] | + * --------------------------------------------------------------------------------------------------------------------- + * RDES5 | Reserved[31:0] | + * --------------------------------------------------------------------------------------------------------------------- + * RDES6 | Receive Time Stamp Low [31:0] | + * --------------------------------------------------------------------------------------------------------------------- + * RDES7 | Receive Time Stamp High [31:0] | + * --------------------------------------------------------------------------------------------------------------------*/ + +/* Bit definition of RDES4 register */ + #define ETH_DMAPTPRXDESC_PTPV ( ( uint32_t ) 0x00002000U ) /* PTP Version */ + #define ETH_DMAPTPRXDESC_PTPFT ( ( uint32_t ) 0x00001000U ) /* PTP Frame Type */ + #define ETH_DMAPTPRXDESC_PTPMT ( ( uint32_t ) 0x00000F00U ) /* PTP Message Type */ + #define ETH_DMAPTPRXDESC_PTPMT_SYNC ( ( uint32_t ) 0x00000100U ) /* SYNC message (all clock types) */ + #define ETH_DMAPTPRXDESC_PTPMT_FOLLOWUP ( ( uint32_t ) 0x00000200U ) /* FollowUp message (all clock types) */ + #define ETH_DMAPTPRXDESC_PTPMT_DELAYREQ ( ( uint32_t ) 0x00000300U ) /* DelayReq message (all clock types) */ + #define ETH_DMAPTPRXDESC_PTPMT_DELAYRESP ( ( uint32_t ) 0x00000400U ) /* DelayResp message (all clock types) */ + #define ETH_DMAPTPRXDESC_PTPMT_PDELAYREQ_ANNOUNCE ( ( uint32_t ) 0x00000500U ) /* PdelayReq message (peer-to-peer transparent clock) or Announce message (Ordinary or Boundary clock) */ + #define ETH_DMAPTPRXDESC_PTPMT_PDELAYRESP_MANAG ( ( uint32_t ) 0x00000600U ) /* PdelayResp message (peer-to-peer transparent clock) or Management message (Ordinary or Boundary clock) */ + #define ETH_DMAPTPRXDESC_PTPMT_PDELAYRESPFOLLOWUP_SIGNAL ( ( uint32_t ) 0x00000700U ) /* PdelayRespFollowUp message (peer-to-peer transparent clock) or Signaling message (Ordinary or Boundary clock) */ + #define ETH_DMAPTPRXDESC_IPV6PR ( ( uint32_t ) 0x00000080U ) /* IPv6 Packet Received */ + #define ETH_DMAPTPRXDESC_IPV4PR ( ( uint32_t ) 0x00000040U ) /* IPv4 Packet Received */ + #define ETH_DMAPTPRXDESC_IPCB ( ( uint32_t ) 0x00000020U ) /* IP Checksum Bypassed */ + #define ETH_DMAPTPRXDESC_IPPE ( ( uint32_t ) 0x00000010U ) /* IP Payload Error */ + #define ETH_DMAPTPRXDESC_IPHE ( ( uint32_t ) 0x00000008U ) /* IP Header Error */ + #define ETH_DMAPTPRXDESC_IPPT ( ( uint32_t ) 0x00000007U ) /* IP Payload Type */ + #define ETH_DMAPTPRXDESC_IPPT_UDP ( ( uint32_t ) 0x00000001U ) /* UDP payload encapsulated in the IP datagram */ + #define ETH_DMAPTPRXDESC_IPPT_TCP ( ( uint32_t ) 0x00000002U ) /* TCP payload encapsulated in the IP datagram */ + #define ETH_DMAPTPRXDESC_IPPT_ICMP ( ( uint32_t ) 0x00000003U ) /* ICMP payload encapsulated in the IP datagram */ + +/* Bit definition of RDES6 register */ + #define ETH_DMAPTPRXDESC_RTSL ( ( uint32_t ) 0xFFFFFFFFU ) /* Receive Time Stamp Low */ + +/* Bit definition of RDES7 register */ + #define ETH_DMAPTPRXDESC_RTSH ( ( uint32_t ) 0xFFFFFFFFU ) /* Receive Time Stamp High */ + +/** + * @} + */ + +/** @defgroup ETH_AutoNegotiation ETH AutoNegotiation + * @{ + */ + #define ETH_AUTONEGOTIATION_ENABLE ( ( uint32_t ) 0x00000001U ) + #define ETH_AUTONEGOTIATION_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Speed ETH Speed + * @{ + */ + #define ETH_SPEED_10M ( ( uint32_t ) 0x00000000U ) + #define ETH_SPEED_100M ( ( uint32_t ) 0x00004000U ) + +/** + * @} + */ + +/** @defgroup ETH_Duplex_Mode ETH Duplex Mode + * @{ + */ + #define ETH_MODE_FULLDUPLEX ( ( uint32_t ) 0x00000800U ) + #define ETH_MODE_HALFDUPLEX ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Rx_Mode ETH Rx Mode + * @{ + */ + #define ETH_RXPOLLING_MODE ( ( uint32_t ) 0x00000000U ) + #define ETH_RXINTERRUPT_MODE ( ( uint32_t ) 0x00000001U ) + +/** + * @} + */ + +/** @defgroup ETH_Checksum_Mode ETH Checksum Mode + * @{ + */ + #define ETH_CHECKSUM_BY_HARDWARE ( ( uint32_t ) 0x00000000U ) + #define ETH_CHECKSUM_BY_SOFTWARE ( ( uint32_t ) 0x00000001U ) + +/** + * @} + */ + +/** @defgroup ETH_Media_Interface ETH Media Interface + * @{ + */ + #define ETH_MEDIA_INTERFACE_MII ( ( uint32_t ) 0x00000000U ) + #define ETH_MEDIA_INTERFACE_RMII ( ( uint32_t ) SYSCFG_PMC_MII_RMII_SEL ) + +/** + * @} + */ + +/** @defgroup ETH_Watchdog ETH Watchdog + * @{ + */ + #define ETH_WATCHDOG_ENABLE ( ( uint32_t ) 0x00000000U ) + #define ETH_WATCHDOG_DISABLE ( ( uint32_t ) 0x00800000U ) + +/** + * @} + */ + +/** @defgroup ETH_Jabber ETH Jabber + * @{ + */ + #define ETH_JABBER_ENABLE ( ( uint32_t ) 0x00000000U ) + #define ETH_JABBER_DISABLE ( ( uint32_t ) 0x00400000U ) + +/** + * @} + */ + +/** @defgroup ETH_Inter_Frame_Gap ETH Inter Frame Gap + * @{ + */ + #define ETH_INTERFRAMEGAP_96BIT ( ( uint32_t ) 0x00000000U ) /*!< minimum IFG between frames during transmission is 96Bit */ + #define ETH_INTERFRAMEGAP_88BIT ( ( uint32_t ) 0x00020000U ) /*!< minimum IFG between frames during transmission is 88Bit */ + #define ETH_INTERFRAMEGAP_80BIT ( ( uint32_t ) 0x00040000U ) /*!< minimum IFG between frames during transmission is 80Bit */ + #define ETH_INTERFRAMEGAP_72BIT ( ( uint32_t ) 0x00060000U ) /*!< minimum IFG between frames during transmission is 72Bit */ + #define ETH_INTERFRAMEGAP_64BIT ( ( uint32_t ) 0x00080000U ) /*!< minimum IFG between frames during transmission is 64Bit */ + #define ETH_INTERFRAMEGAP_56BIT ( ( uint32_t ) 0x000A0000U ) /*!< minimum IFG between frames during transmission is 56Bit */ + #define ETH_INTERFRAMEGAP_48BIT ( ( uint32_t ) 0x000C0000U ) /*!< minimum IFG between frames during transmission is 48Bit */ + #define ETH_INTERFRAMEGAP_40BIT ( ( uint32_t ) 0x000E0000U ) /*!< minimum IFG between frames during transmission is 40Bit */ + +/** + * @} + */ + +/** @defgroup ETH_Carrier_Sense ETH Carrier Sense + * @{ + */ + #define ETH_CARRIERSENCE_ENABLE ( ( uint32_t ) 0x00000000U ) + #define ETH_CARRIERSENCE_DISABLE ( ( uint32_t ) 0x00010000U ) + +/** + * @} + */ + +/** @defgroup ETH_Receive_Own ETH Receive Own + * @{ + */ + #define ETH_RECEIVEOWN_ENABLE ( ( uint32_t ) 0x00000000U ) + #define ETH_RECEIVEOWN_DISABLE ( ( uint32_t ) 0x00002000U ) + +/** + * @} + */ + +/** @defgroup ETH_Loop_Back_Mode ETH Loop Back Mode + * @{ + */ + #define ETH_LOOPBACKMODE_ENABLE ( ( uint32_t ) 0x00001000U ) + #define ETH_LOOPBACKMODE_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Checksum_Offload ETH Checksum Offload + * @{ + */ + #define ETH_CHECKSUMOFFLAOD_ENABLE ( ( uint32_t ) 0x00000400U ) + #define ETH_CHECKSUMOFFLAOD_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Retry_Transmission ETH Retry Transmission + * @{ + */ + #define ETH_RETRYTRANSMISSION_ENABLE ( ( uint32_t ) 0x00000000U ) + #define ETH_RETRYTRANSMISSION_DISABLE ( ( uint32_t ) 0x00000200U ) + +/** + * @} + */ + +/** @defgroup ETH_Automatic_Pad_CRC_Strip ETH Automatic Pad CRC Strip + * @{ + */ + #define ETH_AUTOMATICPADCRCSTRIP_ENABLE ( ( uint32_t ) 0x00000080U ) + #define ETH_AUTOMATICPADCRCSTRIP_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Back_Off_Limit ETH Back Off Limit + * @{ + */ + #define ETH_BACKOFFLIMIT_10 ( ( uint32_t ) 0x00000000U ) + #define ETH_BACKOFFLIMIT_8 ( ( uint32_t ) 0x00000020U ) + #define ETH_BACKOFFLIMIT_4 ( ( uint32_t ) 0x00000040U ) + #define ETH_BACKOFFLIMIT_1 ( ( uint32_t ) 0x00000060U ) + +/** + * @} + */ + +/** @defgroup ETH_Deferral_Check ETH Deferral Check + * @{ + */ + #define ETH_DEFFERRALCHECK_ENABLE ( ( uint32_t ) 0x00000010U ) + #define ETH_DEFFERRALCHECK_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Receive_All ETH Receive All + * @{ + */ + #define ETH_RECEIVEALL_ENABLE ( ( uint32_t ) 0x80000000U ) + #define ETH_RECEIVEAll_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Source_Addr_Filter ETH Source Addr Filter + * @{ + */ + #define ETH_SOURCEADDRFILTER_NORMAL_ENABLE ( ( uint32_t ) 0x00000200U ) + #define ETH_SOURCEADDRFILTER_INVERSE_ENABLE ( ( uint32_t ) 0x00000300U ) + #define ETH_SOURCEADDRFILTER_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Pass_Control_Frames ETH Pass Control Frames + * @{ + */ + #define ETH_PASSCONTROLFRAMES_BLOCKALL ( ( uint32_t ) 0x00000040U ) /*!< MAC filters all control frames from reaching the application */ + #define ETH_PASSCONTROLFRAMES_FORWARDALL ( ( uint32_t ) 0x00000080U ) /*!< MAC forwards all control frames to application even if they fail the Address Filter */ + #define ETH_PASSCONTROLFRAMES_FORWARDPASSEDADDRFILTER ( ( uint32_t ) 0x000000C0U ) /*!< MAC forwards control frames that pass the Address Filter. */ + +/** + * @} + */ + +/** @defgroup ETH_Broadcast_Frames_Reception ETH Broadcast Frames Reception + * @{ + */ + #define ETH_BROADCASTFRAMESRECEPTION_ENABLE ( ( uint32_t ) 0x00000000U ) + #define ETH_BROADCASTFRAMESRECEPTION_DISABLE ( ( uint32_t ) 0x00000020U ) + +/** + * @} + */ + +/** @defgroup ETH_Destination_Addr_Filter ETH Destination Addr Filter + * @{ + */ + #define ETH_DESTINATIONADDRFILTER_NORMAL ( ( uint32_t ) 0x00000000U ) + #define ETH_DESTINATIONADDRFILTER_INVERSE ( ( uint32_t ) 0x00000008U ) + +/** + * @} + */ + +/** @defgroup ETH_Promiscuous_Mode ETH Promiscuous Mode + * @{ + */ + #define ETH_PROMISCUOUS_MODE_ENABLE ( ( uint32_t ) 0x00000001U ) + #define ETH_PROMISCUOUS_MODE_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Multicast_Frames_Filter ETH Multicast Frames Filter + * @{ + */ + #define ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE ( ( uint32_t ) 0x00000404U ) + #define ETH_MULTICASTFRAMESFILTER_HASHTABLE ( ( uint32_t ) 0x00000004U ) + #define ETH_MULTICASTFRAMESFILTER_PERFECT ( ( uint32_t ) 0x00000000U ) + #define ETH_MULTICASTFRAMESFILTER_NONE ( ( uint32_t ) 0x00000010U ) + +/** + * @} + */ + +/** @defgroup ETH_Unicast_Frames_Filter ETH Unicast Frames Filter + * @{ + */ + #define ETH_UNICASTFRAMESFILTER_PERFECTHASHTABLE ( ( uint32_t ) 0x00000402U ) + #define ETH_UNICASTFRAMESFILTER_HASHTABLE ( ( uint32_t ) 0x00000002U ) + #define ETH_UNICASTFRAMESFILTER_PERFECT ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Zero_Quanta_Pause ETH Zero Quanta Pause + * @{ + */ + #define ETH_ZEROQUANTAPAUSE_ENABLE ( ( uint32_t ) 0x00000000U ) + #define ETH_ZEROQUANTAPAUSE_DISABLE ( ( uint32_t ) 0x00000080U ) + +/** + * @} + */ + +/** @defgroup ETH_Pause_Low_Threshold ETH Pause Low Threshold + * @{ + */ + #define ETH_PAUSELOWTHRESHOLD_MINUS4 ( ( uint32_t ) 0x00000000U ) /*!< Pause time minus 4 slot times */ + #define ETH_PAUSELOWTHRESHOLD_MINUS28 ( ( uint32_t ) 0x00000010U ) /*!< Pause time minus 28 slot times */ + #define ETH_PAUSELOWTHRESHOLD_MINUS144 ( ( uint32_t ) 0x00000020U ) /*!< Pause time minus 144 slot times */ + #define ETH_PAUSELOWTHRESHOLD_MINUS256 ( ( uint32_t ) 0x00000030U ) /*!< Pause time minus 256 slot times */ + +/** + * @} + */ + +/** @defgroup ETH_Unicast_Pause_Frame_Detect ETH Unicast Pause Frame Detect + * @{ + */ + #define ETH_UNICASTPAUSEFRAMEDETECT_ENABLE ( ( uint32_t ) 0x00000008U ) + #define ETH_UNICASTPAUSEFRAMEDETECT_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Receive_Flow_Control ETH Receive Flow Control + * @{ + */ + #define ETH_RECEIVEFLOWCONTROL_ENABLE ( ( uint32_t ) 0x00000004U ) + #define ETH_RECEIVEFLOWCONTROL_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Transmit_Flow_Control ETH Transmit Flow Control + * @{ + */ + #define ETH_TRANSMITFLOWCONTROL_ENABLE ( ( uint32_t ) 0x00000002U ) + #define ETH_TRANSMITFLOWCONTROL_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_VLAN_Tag_Comparison ETH VLAN Tag Comparison + * @{ + */ + #define ETH_VLANTAGCOMPARISON_12BIT ( ( uint32_t ) 0x00010000U ) + #define ETH_VLANTAGCOMPARISON_16BIT ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_MAC_addresses ETH MAC addresses + * @{ + */ + #define ETH_MAC_ADDRESS0 ( ( uint32_t ) 0x00000000U ) + #define ETH_MAC_ADDRESS1 ( ( uint32_t ) 0x00000008U ) + #define ETH_MAC_ADDRESS2 ( ( uint32_t ) 0x00000010U ) + #define ETH_MAC_ADDRESS3 ( ( uint32_t ) 0x00000018U ) + +/** + * @} + */ + +/** @defgroup ETH_MAC_addresses_filter_SA_DA ETH MAC addresses filter SA DA + * @{ + */ + #define ETH_MAC_ADDRESSFILTER_SA ( ( uint32_t ) 0x00000000U ) + #define ETH_MAC_ADDRESSFILTER_DA ( ( uint32_t ) 0x00000008U ) + +/** + * @} + */ + +/** @defgroup ETH_MAC_addresses_filter_Mask_bytes ETH MAC addresses filter Mask bytes + * @{ + */ + #define ETH_MAC_ADDRESSMASK_BYTE6 ( ( uint32_t ) 0x20000000U ) /*!< Mask MAC Address high reg bits [15:8] */ + #define ETH_MAC_ADDRESSMASK_BYTE5 ( ( uint32_t ) 0x10000000U ) /*!< Mask MAC Address high reg bits [7:0] */ + #define ETH_MAC_ADDRESSMASK_BYTE4 ( ( uint32_t ) 0x08000000U ) /*!< Mask MAC Address low reg bits [31:24] */ + #define ETH_MAC_ADDRESSMASK_BYTE3 ( ( uint32_t ) 0x04000000U ) /*!< Mask MAC Address low reg bits [23:16] */ + #define ETH_MAC_ADDRESSMASK_BYTE2 ( ( uint32_t ) 0x02000000U ) /*!< Mask MAC Address low reg bits [15:8] */ + #define ETH_MAC_ADDRESSMASK_BYTE1 ( ( uint32_t ) 0x01000000U ) /*!< Mask MAC Address low reg bits [70] */ + +/** + * @} + */ + +/** @defgroup ETH_MAC_Debug_flags ETH MAC Debug flags + * @{ + */ + #ifndef ETH_MAC_TXFIFO_FULL + #define ETH_MAC_TXFIFO_FULL ( ( uint32_t ) 0x02000000 ) /* Tx FIFO full */ + #define ETH_MAC_TXFIFONOT_EMPTY ( ( uint32_t ) 0x01000000 ) /* Tx FIFO not empty */ + #define ETH_MAC_TXFIFO_WRITE_ACTIVE ( ( uint32_t ) 0x00400000 ) /* Tx FIFO write active */ + #define ETH_MAC_TXFIFO_IDLE ( ( uint32_t ) 0x00000000 ) /* Tx FIFO read status: Idle */ + #define ETH_MAC_TXFIFO_READ ( ( uint32_t ) 0x00100000 ) /* Tx FIFO read status: Read (transferring data to the MAC transmitter) */ + #define ETH_MAC_TXFIFO_WAITING ( ( uint32_t ) 0x00200000 ) /* Tx FIFO read status: Waiting for TxStatus from MAC transmitter */ + #define ETH_MAC_TXFIFO_WRITING ( ( uint32_t ) 0x00300000 ) /* Tx FIFO read status: Writing the received TxStatus or flushing the TxFIFO */ + #define ETH_MAC_TRANSMISSION_PAUSE ( ( uint32_t ) 0x00080000 ) /* MAC transmitter in pause */ + #define ETH_MAC_TRANSMITFRAMECONTROLLER_IDLE ( ( uint32_t ) 0x00000000 ) /* MAC transmit frame controller: Idle */ + #define ETH_MAC_TRANSMITFRAMECONTROLLER_WAITING ( ( uint32_t ) 0x00020000 ) /* MAC transmit frame controller: Waiting for Status of previous frame or IFG/backoff period to be over */ + #define ETH_MAC_TRANSMITFRAMECONTROLLER_GENRATING_PCF ( ( uint32_t ) 0x00040000 ) /* MAC transmit frame controller: Generating and transmitting a Pause control frame (in full duplex mode) */ + #define ETH_MAC_TRANSMITFRAMECONTROLLER_TRANSFERRING ( ( uint32_t ) 0x00060000 ) /* MAC transmit frame controller: Transferring input frame for transmission */ + #define ETH_MAC_MII_TRANSMIT_ACTIVE ( ( uint32_t ) 0x00010000 ) /* MAC MII transmit engine active */ + #define ETH_MAC_RXFIFO_EMPTY ( ( uint32_t ) 0x00000000 ) /* Rx FIFO fill level: empty */ + #define ETH_MAC_RXFIFO_BELOW_THRESHOLD ( ( uint32_t ) 0x00000100 ) /* Rx FIFO fill level: fill-level below flow-control de-activate threshold */ + #define ETH_MAC_RXFIFO_ABOVE_THRESHOLD ( ( uint32_t ) 0x00000200 ) /* Rx FIFO fill level: fill-level above flow-control activate threshold */ + #define ETH_MAC_RXFIFO_FULL ( ( uint32_t ) 0x00000300 ) /* Rx FIFO fill level: full */ + #define ETH_MAC_READCONTROLLER_IDLE ( ( uint32_t ) 0x00000060 ) /* Rx FIFO read controller IDLE state */ + #define ETH_MAC_READCONTROLLER_READING_DATA ( ( uint32_t ) 0x00000060 ) /* Rx FIFO read controller Reading frame data */ + #define ETH_MAC_READCONTROLLER_READING_STATUS ( ( uint32_t ) 0x00000060 ) /* Rx FIFO read controller Reading frame status (or time-stamp) */ + #define ETH_MAC_READCONTROLLER_ FLUSHING( ( uint32_t ) 0x00000060 ) /* Rx FIFO read controller Flushing the frame data and status */ + #define ETH_MAC_RXFIFO_WRITE_ACTIVE ( ( uint32_t ) 0x00000010 ) /* Rx FIFO write controller active */ + #define ETH_MAC_SMALL_FIFO_NOTACTIVE ( ( uint32_t ) 0x00000000 ) /* MAC small FIFO read / write controllers not active */ + #define ETH_MAC_SMALL_FIFO_READ_ACTIVE ( ( uint32_t ) 0x00000002 ) /* MAC small FIFO read controller active */ + #define ETH_MAC_SMALL_FIFO_WRITE_ACTIVE ( ( uint32_t ) 0x00000004 ) /* MAC small FIFO write controller active */ + #define ETH_MAC_SMALL_FIFO_RW_ACTIVE ( ( uint32_t ) 0x00000006 ) /* MAC small FIFO read / write controllers active */ + #define ETH_MAC_MII_RECEIVE_PROTOCOL_ACTIVE ( ( uint32_t ) 0x00000001 ) /* MAC MII receive protocol engine active */ + #else /* ifndef ETH_MAC_TXFIFO_FULL */ + /* stm32_hal_legacy.h has probably been included. That file defines 'ETH_MAC_TXFIFO_FULL' and all macro's here below. */ + #endif /* ifndef ETH_MAC_TXFIFO_FULL */ + +/** + * @} + */ + +/** @defgroup ETH_Drop_TCP_IP_Checksum_Error_Frame ETH Drop TCP IP Checksum Error Frame + * @{ + */ + #define ETH_DROPTCPIPCHECKSUMERRORFRAME_ENABLE ( ( uint32_t ) 0x00000000U ) + #define ETH_DROPTCPIPCHECKSUMERRORFRAME_DISABLE ( ( uint32_t ) 0x04000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Receive_Store_Forward ETH Receive Store Forward + * @{ + */ + #define ETH_RECEIVESTOREFORWARD_ENABLE ( ( uint32_t ) 0x02000000U ) + #define ETH_RECEIVESTOREFORWARD_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Flush_Received_Frame ETH Flush Received Frame + * @{ + */ + #define ETH_FLUSHRECEIVEDFRAME_ENABLE ( ( uint32_t ) 0x00000000U ) + #define ETH_FLUSHRECEIVEDFRAME_DISABLE ( ( uint32_t ) 0x01000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Transmit_Store_Forward ETH Transmit Store Forward + * @{ + */ + #define ETH_TRANSMITSTOREFORWARD_ENABLE ( ( uint32_t ) 0x00200000U ) + #define ETH_TRANSMITSTOREFORWARD_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Transmit_Threshold_Control ETH Transmit Threshold Control + * @{ + */ + #define ETH_TRANSMITTHRESHOLDCONTROL_64BYTES ( ( uint32_t ) 0x00000000U ) /*!< threshold level of the MTL Transmit FIFO is 64 Bytes */ + #define ETH_TRANSMITTHRESHOLDCONTROL_128BYTES ( ( uint32_t ) 0x00004000U ) /*!< threshold level of the MTL Transmit FIFO is 128 Bytes */ + #define ETH_TRANSMITTHRESHOLDCONTROL_192BYTES ( ( uint32_t ) 0x00008000U ) /*!< threshold level of the MTL Transmit FIFO is 192 Bytes */ + #define ETH_TRANSMITTHRESHOLDCONTROL_256BYTES ( ( uint32_t ) 0x0000C000U ) /*!< threshold level of the MTL Transmit FIFO is 256 Bytes */ + #define ETH_TRANSMITTHRESHOLDCONTROL_40BYTES ( ( uint32_t ) 0x00010000U ) /*!< threshold level of the MTL Transmit FIFO is 40 Bytes */ + #define ETH_TRANSMITTHRESHOLDCONTROL_32BYTES ( ( uint32_t ) 0x00014000U ) /*!< threshold level of the MTL Transmit FIFO is 32 Bytes */ + #define ETH_TRANSMITTHRESHOLDCONTROL_24BYTES ( ( uint32_t ) 0x00018000U ) /*!< threshold level of the MTL Transmit FIFO is 24 Bytes */ + #define ETH_TRANSMITTHRESHOLDCONTROL_16BYTES ( ( uint32_t ) 0x0001C000U ) /*!< threshold level of the MTL Transmit FIFO is 16 Bytes */ + +/** + * @} + */ + +/** @defgroup ETH_Forward_Error_Frames ETH Forward Error Frames + * @{ + */ + #define ETH_FORWARDERRORFRAMES_ENABLE ( ( uint32_t ) 0x00000080U ) + #define ETH_FORWARDERRORFRAMES_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Forward_Undersized_Good_Frames ETH Forward Undersized Good Frames + * @{ + */ + #define ETH_FORWARDUNDERSIZEDGOODFRAMES_ENABLE ( ( uint32_t ) 0x00000040U ) + #define ETH_FORWARDUNDERSIZEDGOODFRAMES_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Receive_Threshold_Control ETH Receive Threshold Control + * @{ + */ + #define ETH_RECEIVEDTHRESHOLDCONTROL_64BYTES ( ( uint32_t ) 0x00000000U ) /*!< threshold level of the MTL Receive FIFO is 64 Bytes */ + #define ETH_RECEIVEDTHRESHOLDCONTROL_32BYTES ( ( uint32_t ) 0x00000008U ) /*!< threshold level of the MTL Receive FIFO is 32 Bytes */ + #define ETH_RECEIVEDTHRESHOLDCONTROL_96BYTES ( ( uint32_t ) 0x00000010U ) /*!< threshold level of the MTL Receive FIFO is 96 Bytes */ + #define ETH_RECEIVEDTHRESHOLDCONTROL_128BYTES ( ( uint32_t ) 0x00000018U ) /*!< threshold level of the MTL Receive FIFO is 128 Bytes */ + +/** + * @} + */ + +/** @defgroup ETH_Second_Frame_Operate ETH Second Frame Operate + * @{ + */ + #define ETH_SECONDFRAMEOPERARTE_ENABLE ( ( uint32_t ) 0x00000004U ) + #define ETH_SECONDFRAMEOPERARTE_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Address_Aligned_Beats ETH Address Aligned Beats + * @{ + */ + #define ETH_ADDRESSALIGNEDBEATS_ENABLE ( ( uint32_t ) 0x02000000U ) + #define ETH_ADDRESSALIGNEDBEATS_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Fixed_Burst ETH Fixed Burst + * @{ + */ + #define ETH_FIXEDBURST_ENABLE ( ( uint32_t ) 0x00010000U ) + #define ETH_FIXEDBURST_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_Rx_DMA_Burst_Length ETH Rx DMA Burst Length + * @{ + */ + #define ETH_RXDMABURSTLENGTH_1BEAT ( ( uint32_t ) 0x00020000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 1 */ + #define ETH_RXDMABURSTLENGTH_2BEAT ( ( uint32_t ) 0x00040000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 2 */ + #define ETH_RXDMABURSTLENGTH_4BEAT ( ( uint32_t ) 0x00080000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 4 */ + #define ETH_RXDMABURSTLENGTH_8BEAT ( ( uint32_t ) 0x00100000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 8 */ + #define ETH_RXDMABURSTLENGTH_16BEAT ( ( uint32_t ) 0x00200000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 16 */ + #define ETH_RXDMABURSTLENGTH_32BEAT ( ( uint32_t ) 0x00400000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 32 */ + #define ETH_RXDMABURSTLENGTH_4XPBL_4BEAT ( ( uint32_t ) 0x01020000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 4 */ + #define ETH_RXDMABURSTLENGTH_4XPBL_8BEAT ( ( uint32_t ) 0x01040000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 8 */ + #define ETH_RXDMABURSTLENGTH_4XPBL_16BEAT ( ( uint32_t ) 0x01080000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 16 */ + #define ETH_RXDMABURSTLENGTH_4XPBL_32BEAT ( ( uint32_t ) 0x01100000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 32 */ + #define ETH_RXDMABURSTLENGTH_4XPBL_64BEAT ( ( uint32_t ) 0x01200000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 64 */ + #define ETH_RXDMABURSTLENGTH_4XPBL_128BEAT ( ( uint32_t ) 0x01400000U ) /*!< maximum number of beats to be transferred in one RxDMA transaction is 128 */ + +/** + * @} + */ + +/** @defgroup ETH_Tx_DMA_Burst_Length ETH Tx DMA Burst Length + * @{ + */ + #define ETH_TXDMABURSTLENGTH_1BEAT ( ( uint32_t ) 0x00000100U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 1 */ + #define ETH_TXDMABURSTLENGTH_2BEAT ( ( uint32_t ) 0x00000200U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 2 */ + #define ETH_TXDMABURSTLENGTH_4BEAT ( ( uint32_t ) 0x00000400U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 4 */ + #define ETH_TXDMABURSTLENGTH_8BEAT ( ( uint32_t ) 0x00000800U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 8 */ + #define ETH_TXDMABURSTLENGTH_16BEAT ( ( uint32_t ) 0x00001000U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 16 */ + #define ETH_TXDMABURSTLENGTH_32BEAT ( ( uint32_t ) 0x00002000U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 32 */ + #define ETH_TXDMABURSTLENGTH_4XPBL_4BEAT ( ( uint32_t ) 0x01000100U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 4 */ + #define ETH_TXDMABURSTLENGTH_4XPBL_8BEAT ( ( uint32_t ) 0x01000200U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 8 */ + #define ETH_TXDMABURSTLENGTH_4XPBL_16BEAT ( ( uint32_t ) 0x01000400U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 16 */ + #define ETH_TXDMABURSTLENGTH_4XPBL_32BEAT ( ( uint32_t ) 0x01000800U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 32 */ + #define ETH_TXDMABURSTLENGTH_4XPBL_64BEAT ( ( uint32_t ) 0x01001000U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 64 */ + #define ETH_TXDMABURSTLENGTH_4XPBL_128BEAT ( ( uint32_t ) 0x01002000U ) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 128 */ + +/** + * @} + */ + +/** @defgroup ETH_DMA_Enhanced_descriptor_format ETH DMA Enhanced descriptor format + * @{ + */ + #define ETH_DMAENHANCEDDESCRIPTOR_ENABLE ( ( uint32_t ) 0x00000080U ) + #define ETH_DMAENHANCEDDESCRIPTOR_DISABLE ( ( uint32_t ) 0x00000000U ) + +/** + * @} + */ + +/** @defgroup ETH_DMA_Arbitration ETH DMA Arbitration + * @{ + */ + #define ETH_DMAARBITRATION_ROUNDROBIN_RXTX_1_1 ( ( uint32_t ) 0x00000000U ) + #define ETH_DMAARBITRATION_ROUNDROBIN_RXTX_2_1 ( ( uint32_t ) 0x00004000U ) + #define ETH_DMAARBITRATION_ROUNDROBIN_RXTX_3_1 ( ( uint32_t ) 0x00008000U ) + #define ETH_DMAARBITRATION_ROUNDROBIN_RXTX_4_1 ( ( uint32_t ) 0x0000C000U ) + #define ETH_DMAARBITRATION_RXPRIORTX ( ( uint32_t ) 0x00000002U ) + +/** + * @} + */ + +/** @defgroup ETH_DMA_Tx_descriptor_segment ETH DMA Tx descriptor segment + * @{ + */ + #define ETH_DMATXDESC_LASTSEGMENTS ( ( uint32_t ) 0x40000000U ) /*!< Last Segment */ + #define ETH_DMATXDESC_FIRSTSEGMENT ( ( uint32_t ) 0x20000000U ) /*!< First Segment */ + +/** + * @} + */ + +/** @defgroup ETH_DMA_Tx_descriptor_Checksum_Insertion_Control ETH DMA Tx descriptor Checksum Insertion Control + * @{ + */ + #define ETH_DMATXDESC_CHECKSUMBYPASS ( ( uint32_t ) 0x00000000U ) /*!< Checksum engine bypass */ + #define ETH_DMATXDESC_CHECKSUMIPV4HEADER ( ( uint32_t ) 0x00400000U ) /*!< IPv4 header checksum insertion */ + #define ETH_DMATXDESC_CHECKSUMTCPUDPICMPSEGMENT ( ( uint32_t ) 0x00800000U ) /*!< TCP/UDP/ICMP checksum insertion. Pseudo header checksum is assumed to be present */ + #define ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL ( ( uint32_t ) 0x00C00000U ) /*!< TCP/UDP/ICMP checksum fully in hardware including pseudo header */ + +/** + * @} + */ + +/** @defgroup ETH_DMA_Rx_descriptor_buffers ETH DMA Rx descriptor buffers + * @{ + */ + #define ETH_DMARXDESC_BUFFER1 ( ( uint32_t ) 0x00000000U ) /*!< DMA Rx Desc Buffer1 */ + #define ETH_DMARXDESC_BUFFER2 ( ( uint32_t ) 0x00000001U ) /*!< DMA Rx Desc Buffer2 */ + +/** + * @} + */ + +/** @defgroup ETH_PMT_Flags ETH PMT Flags + * @{ + */ + #define ETH_PMT_FLAG_WUFFRPR ( ( uint32_t ) 0x80000000U ) /*!< Wake-Up Frame Filter Register Pointer Reset */ + #define ETH_PMT_FLAG_WUFR ( ( uint32_t ) 0x00000040U ) /*!< Wake-Up Frame Received */ + #define ETH_PMT_FLAG_MPR ( ( uint32_t ) 0x00000020U ) /*!< Magic Packet Received */ + +/** + * @} + */ + +/** @defgroup ETH_MMC_Tx_Interrupts ETH MMC Tx Interrupts + * @{ + */ + #define ETH_MMC_IT_TGF ( ( uint32_t ) 0x00200000U ) /*!< When Tx good frame counter reaches half the maximum value */ + #define ETH_MMC_IT_TGFMSC ( ( uint32_t ) 0x00008000U ) /*!< When Tx good multi col counter reaches half the maximum value */ + #define ETH_MMC_IT_TGFSC ( ( uint32_t ) 0x00004000U ) /*!< When Tx good single col counter reaches half the maximum value */ + +/** + * @} + */ + +/** @defgroup ETH_MMC_Rx_Interrupts ETH MMC Rx Interrupts + * @{ + */ + #define ETH_MMC_IT_RGUF ( ( uint32_t ) 0x10020000U ) /*!< When Rx good unicast frames counter reaches half the maximum value */ + #define ETH_MMC_IT_RFAE ( ( uint32_t ) 0x10000040U ) /*!< When Rx alignment error counter reaches half the maximum value */ + #define ETH_MMC_IT_RFCE ( ( uint32_t ) 0x10000020U ) /*!< When Rx crc error counter reaches half the maximum value */ + +/** + * @} + */ + +/** @defgroup ETH_MAC_Flags ETH MAC Flags + * @{ + */ + #define ETH_MAC_FLAG_TST ( ( uint32_t ) 0x00000200U ) /*!< Time stamp trigger flag (on MAC) */ + #define ETH_MAC_FLAG_MMCT ( ( uint32_t ) 0x00000040U ) /*!< MMC transmit flag */ + #define ETH_MAC_FLAG_MMCR ( ( uint32_t ) 0x00000020U ) /*!< MMC receive flag */ + #define ETH_MAC_FLAG_MMC ( ( uint32_t ) 0x00000010U ) /*!< MMC flag (on MAC) */ + #define ETH_MAC_FLAG_PMT ( ( uint32_t ) 0x00000008U ) /*!< PMT flag (on MAC) */ + +/** + * @} + */ + +/** @defgroup ETH_DMA_Flags ETH DMA Flags + * @{ + */ + #define ETH_DMA_FLAG_TST ( ( uint32_t ) 0x20000000U ) /*!< Time-stamp trigger interrupt (on DMA) */ + #define ETH_DMA_FLAG_PMT ( ( uint32_t ) 0x10000000U ) /*!< PMT interrupt (on DMA) */ + #define ETH_DMA_FLAG_MMC ( ( uint32_t ) 0x08000000U ) /*!< MMC interrupt (on DMA) */ + #define ETH_DMA_FLAG_DATATRANSFERERROR ( ( uint32_t ) 0x00800000U ) /*!< Error bits 0-Rx DMA, 1-Tx DMA */ + #define ETH_DMA_FLAG_READWRITEERROR ( ( uint32_t ) 0x01000000U ) /*!< Error bits 0-write transfer, 1-read transfer */ + #define ETH_DMA_FLAG_ACCESSERROR ( ( uint32_t ) 0x02000000U ) /*!< Error bits 0-data buffer, 1-desc. access */ + #define ETH_DMA_FLAG_NIS ( ( uint32_t ) 0x00010000U ) /*!< Normal interrupt summary flag */ + #define ETH_DMA_FLAG_AIS ( ( uint32_t ) 0x00008000U ) /*!< Abnormal interrupt summary flag */ + #define ETH_DMA_FLAG_ER ( ( uint32_t ) 0x00004000U ) /*!< Early receive flag */ + #define ETH_DMA_FLAG_FBE ( ( uint32_t ) 0x00002000U ) /*!< Fatal bus error flag */ + #define ETH_DMA_FLAG_ET ( ( uint32_t ) 0x00000400U ) /*!< Early transmit flag */ + #define ETH_DMA_FLAG_RWT ( ( uint32_t ) 0x00000200U ) /*!< Receive watchdog timeout flag */ + #define ETH_DMA_FLAG_RPS ( ( uint32_t ) 0x00000100U ) /*!< Receive process stopped flag */ + #define ETH_DMA_FLAG_RBU ( ( uint32_t ) 0x00000080U ) /*!< Receive buffer unavailable flag */ + #define ETH_DMA_FLAG_R ( ( uint32_t ) 0x00000040U ) /*!< Receive flag */ + #define ETH_DMA_FLAG_TU ( ( uint32_t ) 0x00000020U ) /*!< Underflow flag */ + #define ETH_DMA_FLAG_RO ( ( uint32_t ) 0x00000010U ) /*!< Overflow flag */ + #define ETH_DMA_FLAG_TJT ( ( uint32_t ) 0x00000008U ) /*!< Transmit jabber timeout flag */ + #define ETH_DMA_FLAG_TBU ( ( uint32_t ) 0x00000004U ) /*!< Transmit buffer unavailable flag */ + #define ETH_DMA_FLAG_TPS ( ( uint32_t ) 0x00000002U ) /*!< Transmit process stopped flag */ + #define ETH_DMA_FLAG_T ( ( uint32_t ) 0x00000001U ) /*!< Transmit flag */ + +/** + * @} + */ + +/** @defgroup ETH_MAC_Interrupts ETH MAC Interrupts + * @{ + */ + #define ETH_MAC_IT_TST ( ( uint32_t ) 0x00000200U ) /*!< Time stamp trigger interrupt (on MAC) */ + #define ETH_MAC_IT_MMCT ( ( uint32_t ) 0x00000040U ) /*!< MMC transmit interrupt */ + #define ETH_MAC_IT_MMCR ( ( uint32_t ) 0x00000020U ) /*!< MMC receive interrupt */ + #define ETH_MAC_IT_MMC ( ( uint32_t ) 0x00000010U ) /*!< MMC interrupt (on MAC) */ + #define ETH_MAC_IT_PMT ( ( uint32_t ) 0x00000008U ) /*!< PMT interrupt (on MAC) */ + +/** + * @} + */ + +/** @defgroup ETH_DMA_Interrupts ETH DMA Interrupts + * @{ + */ + #define ETH_DMA_IT_TST ( ( uint32_t ) 0x20000000U ) /*!< Time-stamp trigger interrupt (on DMA) */ + #define ETH_DMA_IT_PMT ( ( uint32_t ) 0x10000000U ) /*!< PMT interrupt (on DMA) */ + #define ETH_DMA_IT_MMC ( ( uint32_t ) 0x08000000U ) /*!< MMC interrupt (on DMA) */ + #define ETH_DMA_IT_NIS ( ( uint32_t ) 0x00010000U ) /*!< Normal interrupt summary */ + #define ETH_DMA_IT_AIS ( ( uint32_t ) 0x00008000U ) /*!< Abnormal interrupt summary */ + #define ETH_DMA_IT_ER ( ( uint32_t ) 0x00004000U ) /*!< Early receive interrupt */ + #define ETH_DMA_IT_FBE ( ( uint32_t ) 0x00002000U ) /*!< Fatal bus error interrupt */ + #define ETH_DMA_IT_ET ( ( uint32_t ) 0x00000400U ) /*!< Early transmit interrupt */ + #define ETH_DMA_IT_RWT ( ( uint32_t ) 0x00000200U ) /*!< Receive watchdog timeout interrupt */ + #define ETH_DMA_IT_RPS ( ( uint32_t ) 0x00000100U ) /*!< Receive process stopped interrupt */ + #define ETH_DMA_IT_RBU ( ( uint32_t ) 0x00000080U ) /*!< Receive buffer unavailable interrupt */ + #define ETH_DMA_IT_R ( ( uint32_t ) 0x00000040U ) /*!< Receive interrupt */ + #define ETH_DMA_IT_TU ( ( uint32_t ) 0x00000020U ) /*!< Underflow interrupt */ + #define ETH_DMA_IT_RO ( ( uint32_t ) 0x00000010U ) /*!< Overflow interrupt */ + #define ETH_DMA_IT_TJT ( ( uint32_t ) 0x00000008U ) /*!< Transmit jabber timeout interrupt */ + #define ETH_DMA_IT_TBU ( ( uint32_t ) 0x00000004U ) /*!< Transmit buffer unavailable interrupt */ + #define ETH_DMA_IT_TPS ( ( uint32_t ) 0x00000002U ) /*!< Transmit process stopped interrupt */ + #define ETH_DMA_IT_T ( ( uint32_t ) 0x00000001U ) /*!< Transmit interrupt */ + +/** + * @} + */ + +/** @defgroup ETH_DMA_transmit_process_state ETH DMA transmit process state + * @{ + */ + #define ETH_DMA_TRANSMITPROCESS_STOPPED ( ( uint32_t ) 0x00000000U ) /*!< Stopped - Reset or Stop Tx Command issued */ + #define ETH_DMA_TRANSMITPROCESS_FETCHING ( ( uint32_t ) 0x00100000U ) /*!< Running - fetching the Tx descriptor */ + #define ETH_DMA_TRANSMITPROCESS_WAITING ( ( uint32_t ) 0x00200000U ) /*!< Running - waiting for status */ + #define ETH_DMA_TRANSMITPROCESS_READING ( ( uint32_t ) 0x00300000U ) /*!< Running - reading the data from host memory */ + #define ETH_DMA_TRANSMITPROCESS_SUSPENDED ( ( uint32_t ) 0x00600000U ) /*!< Suspended - Tx Descriptor unavailable */ + #define ETH_DMA_TRANSMITPROCESS_CLOSING ( ( uint32_t ) 0x00700000U ) /*!< Running - closing Rx descriptor */ + +/** + * @} + */ + + +/** @defgroup ETH_DMA_receive_process_state ETH DMA receive process state + * @{ + */ + #define ETH_DMA_RECEIVEPROCESS_STOPPED ( ( uint32_t ) 0x00000000U ) /*!< Stopped - Reset or Stop Rx Command issued */ + #define ETH_DMA_RECEIVEPROCESS_FETCHING ( ( uint32_t ) 0x00020000U ) /*!< Running - fetching the Rx descriptor */ + #define ETH_DMA_RECEIVEPROCESS_WAITING ( ( uint32_t ) 0x00060000U ) /*!< Running - waiting for packet */ + #define ETH_DMA_RECEIVEPROCESS_SUSPENDED ( ( uint32_t ) 0x00080000U ) /*!< Suspended - Rx Descriptor unavailable */ + #define ETH_DMA_RECEIVEPROCESS_CLOSING ( ( uint32_t ) 0x000A0000U ) /*!< Running - closing descriptor */ + #define ETH_DMA_RECEIVEPROCESS_QUEUING ( ( uint32_t ) 0x000E0000U ) /*!< Running - queuing the receive frame into host memory */ + +/** + * @} + */ + +/** @defgroup ETH_DMA_overflow ETH DMA overflow + * @{ + */ + #define ETH_DMA_OVERFLOW_RXFIFOCOUNTER ( ( uint32_t ) 0x10000000U ) /*!< Overflow bit for FIFO overflow counter */ + #define ETH_DMA_OVERFLOW_MISSEDFRAMECOUNTER ( ( uint32_t ) 0x00010000U ) /*!< Overflow bit for missed frame counter */ + +/** + * @} + */ + +/** @defgroup ETH_EXTI_LINE_WAKEUP ETH EXTI LINE WAKEUP + * @{ + */ + #define ETH_EXTI_LINE_WAKEUP ( ( uint32_t ) 0x00080000U ) /*!< External interrupt line 19 Connected to the ETH EXTI Line */ + +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ + +/** @defgroup ETH_Exported_Macros ETH Exported Macros + * @brief macros to handle interrupts and specific clock configurations + * @{ + */ + +/** @brief Reset ETH handle state + * @param __HANDLE__: specifies the ETH handle. + * @retval None + */ + #define __HAL_ETH_RESET_HANDLE_STATE( __HANDLE__ ) ( ( __HANDLE__ )->State = HAL_ETH_STATE_RESET ) + +/** + * @brief Checks whether the specified Ethernet DMA Tx Desc flag is set or not. + * @param __HANDLE__: ETH Handle + * @param __FLAG__: specifies the flag of TDES0 to check. + * @retval the ETH_DMATxDescFlag (SET or RESET). + */ + #define __HAL_ETH_DMATXDESC_GET_FLAG( __HANDLE__, __FLAG__ ) ( ( __HANDLE__ )->TxDesc->Status & ( __FLAG__ ) == ( __FLAG__ ) ) + +/** + * @brief Checks whether the specified Ethernet DMA Rx Desc flag is set or not. + * @param __HANDLE__: ETH Handle + * @param __FLAG__: specifies the flag of RDES0 to check. + * @retval the ETH_DMATxDescFlag (SET or RESET). + */ + #define __HAL_ETH_DMARXDESC_GET_FLAG( __HANDLE__, __FLAG__ ) ( ( __HANDLE__ )->RxDesc->Status & ( __FLAG__ ) == ( __FLAG__ ) ) + +/** + * @brief Enables the specified DMA Rx Desc receive interrupt. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMARXDESC_ENABLE_IT( __HANDLE__ ) ( ( __HANDLE__ )->RxDesc->ControlBufferSize &= ( ~( uint32_t ) ETH_DMARXDESC_DIC ) ) + +/** + * @brief Disables the specified DMA Rx Desc receive interrupt. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMARXDESC_DISABLE_IT( __HANDLE__ ) ( ( __HANDLE__ )->RxDesc->ControlBufferSize |= ETH_DMARXDESC_DIC ) + +/** + * @brief Set the specified DMA Rx Desc Own bit. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMARXDESC_SET_OWN_BIT( __HANDLE__ ) ( ( __HANDLE__ )->RxDesc->Status |= ETH_DMARXDESC_OWN ) + +/** + * @brief Returns the specified Ethernet DMA Tx Desc collision count. + * @param __HANDLE__: ETH Handle + * @retval The Transmit descriptor collision counter value. + */ + #define __HAL_ETH_DMATXDESC_GET_COLLISION_COUNT( __HANDLE__ ) ( ( ( __HANDLE__ )->TxDesc->Status & ETH_DMATXDESC_CC ) >> ETH_DMATXDESC_COLLISION_COUNTSHIFT ) + +/** + * @brief Set the specified DMA Tx Desc Own bit. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMATXDESC_SET_OWN_BIT( __HANDLE__ ) ( ( __HANDLE__ )->TxDesc->Status |= ETH_DMATXDESC_OWN ) + +/** + * @brief Enables the specified DMA Tx Desc Transmit interrupt. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMATXDESC_ENABLE_IT( __HANDLE__ ) ( ( __HANDLE__ )->TxDesc->Status |= ETH_DMATXDESC_IC ) + +/** + * @brief Disables the specified DMA Tx Desc Transmit interrupt. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMATXDESC_DISABLE_IT( __HANDLE__ ) ( ( __HANDLE__ )->TxDesc->Status &= ~ETH_DMATXDESC_IC ) + +/** + * @brief Selects the specified Ethernet DMA Tx Desc Checksum Insertion. + * @param __HANDLE__: ETH Handle + * @param __CHECKSUM__: specifies is the DMA Tx desc checksum insertion. + * This parameter can be one of the following values: + * @arg ETH_DMATXDESC_CHECKSUMBYPASS : Checksum bypass + * @arg ETH_DMATXDESC_CHECKSUMIPV4HEADER : IPv4 header checksum + * @arg ETH_DMATXDESC_CHECKSUMTCPUDPICMPSEGMENT : TCP/UDP/ICMP checksum. Pseudo header checksum is assumed to be present + * @arg ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL : TCP/UDP/ICMP checksum fully in hardware including pseudo header + * @retval None + */ + #define __HAL_ETH_DMATXDESC_CHECKSUM_INSERTION( __HANDLE__, __CHECKSUM__ ) ( ( __HANDLE__ )->TxDesc->Status |= ( __CHECKSUM__ ) ) + +/** + * @brief Enables the DMA Tx Desc CRC. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMATXDESC_CRC_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->TxDesc->Status &= ~ETH_DMATXDESC_DC ) + +/** + * @brief Disables the DMA Tx Desc CRC. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMATXDESC_CRC_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->TxDesc->Status |= ETH_DMATXDESC_DC ) + +/** + * @brief Enables the DMA Tx Desc padding for frame shorter than 64 bytes. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMATXDESC_SHORT_FRAME_PADDING_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->TxDesc->Status &= ~ETH_DMATXDESC_DP ) + +/** + * @brief Disables the DMA Tx Desc padding for frame shorter than 64 bytes. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_DMATXDESC_SHORT_FRAME_PADDING_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->TxDesc->Status |= ETH_DMATXDESC_DP ) + +/** + * @brief Enables the specified Ethernet MAC interrupts. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the Ethernet MAC interrupt sources to be + * enabled or disabled. + * This parameter can be any combination of the following values: + * @arg ETH_MAC_IT_TST : Time stamp trigger interrupt + * @arg ETH_MAC_IT_PMT : PMT interrupt + * @retval None + */ + #define __HAL_ETH_MAC_ENABLE_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->MACIMR |= ( __INTERRUPT__ ) ) + +/** + * @brief Disables the specified Ethernet MAC interrupts. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the Ethernet MAC interrupt sources to be + * enabled or disabled. + * This parameter can be any combination of the following values: + * @arg ETH_MAC_IT_TST : Time stamp trigger interrupt + * @arg ETH_MAC_IT_PMT : PMT interrupt + * @retval None + */ + #define __HAL_ETH_MAC_DISABLE_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->MACIMR &= ~( __INTERRUPT__ ) ) + +/** + * @brief Initiate a Pause Control Frame (Full-duplex only). + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_INITIATE_PAUSE_CONTROL_FRAME( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACFCR |= ETH_MACFCR_FCBBPA ) + +/** + * @brief Checks whether the Ethernet flow control busy bit is set or not. + * @param __HANDLE__: ETH Handle + * @retval The new state of flow control busy status bit (SET or RESET). + */ + #define __HAL_ETH_GET_FLOW_CONTROL_BUSY_STATUS( __HANDLE__ ) ( ( ( __HANDLE__ )->Instance->MACFCR & ETH_MACFCR_FCBBPA ) == ETH_MACFCR_FCBBPA ) + +/** + * @brief Enables the MAC Back Pressure operation activation (Half-duplex only). + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_BACK_PRESSURE_ACTIVATION_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACFCR |= ETH_MACFCR_FCBBPA ) + +/** + * @brief Disables the MAC BackPressure operation activation (Half-duplex only). + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_BACK_PRESSURE_ACTIVATION_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACFCR &= ~ETH_MACFCR_FCBBPA ) + +/** + * @brief Checks whether the specified Ethernet MAC flag is set or not. + * @param __HANDLE__: ETH Handle + * @param __FLAG__: specifies the flag to check. + * This parameter can be one of the following values: + * @arg ETH_MAC_FLAG_TST : Time stamp trigger flag + * @arg ETH_MAC_FLAG_MMCT : MMC transmit flag + * @arg ETH_MAC_FLAG_MMCR : MMC receive flag + * @arg ETH_MAC_FLAG_MMC : MMC flag + * @arg ETH_MAC_FLAG_PMT : PMT flag + * @retval The state of Ethernet MAC flag. + */ + #define __HAL_ETH_MAC_GET_FLAG( __HANDLE__, __FLAG__ ) ( ( ( __HANDLE__ )->Instance->MACSR & ( __FLAG__ ) ) == ( __FLAG__ ) ) + +/** + * @brief Enables the specified Ethernet DMA interrupts. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the Ethernet DMA interrupt sources to be + * enabled @ref ETH_DMA_Interrupts + * @retval None + */ + #define __HAL_ETH_DMA_ENABLE_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->DMAIER |= ( __INTERRUPT__ ) ) + +/** + * @brief Disables the specified Ethernet DMA interrupts. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the Ethernet DMA interrupt sources to be + * disabled. @ref ETH_DMA_Interrupts + * @retval None + */ + #define __HAL_ETH_DMA_DISABLE_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->DMAIER &= ~( __INTERRUPT__ ) ) + +/** + * @brief Clears the Ethernet DMA IT pending bit. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the interrupt pending bit to clear. @ref ETH_DMA_Interrupts + * @retval None + */ + #define __HAL_ETH_DMA_CLEAR_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->DMASR = ( __INTERRUPT__ ) ) + +/** + * @brief Checks whether the specified Ethernet DMA flag is set or not. + * @param __HANDLE__: ETH Handle + * @param __FLAG__: specifies the flag to check. @ref ETH_DMA_Flags + * @retval The new state of ETH_DMA_FLAG (SET or RESET). + */ + #define __HAL_ETH_DMA_GET_FLAG( __HANDLE__, __FLAG__ ) ( ( ( __HANDLE__ )->Instance->DMASR & ( __FLAG__ ) ) == ( __FLAG__ ) ) + +/** + * @brief Checks whether the specified Ethernet DMA flag is set or not. + * @param __HANDLE__: ETH Handle + * @param __FLAG__: specifies the flag to clear. @ref ETH_DMA_Flags + * @retval The new state of ETH_DMA_FLAG (SET or RESET). + */ + #define __HAL_ETH_DMA_CLEAR_FLAG( __HANDLE__, __FLAG__ ) ( ( __HANDLE__ )->Instance->DMASR = ( __FLAG__ ) ) + +/** + * @brief Checks whether the specified Ethernet DMA overflow flag is set or not. + * @param __HANDLE__: ETH Handle + * @param __OVERFLOW__: specifies the DMA overflow flag to check. + * This parameter can be one of the following values: + * @arg ETH_DMA_OVERFLOW_RXFIFOCOUNTER : Overflow for FIFO Overflows Counter + * @arg ETH_DMA_OVERFLOW_MISSEDFRAMECOUNTER : Overflow for Buffer Unavailable Missed Frame Counter + * @retval The state of Ethernet DMA overflow Flag (SET or RESET). + */ + #define __HAL_ETH_GET_DMA_OVERFLOW_STATUS( __HANDLE__, __OVERFLOW__ ) ( ( ( __HANDLE__ )->Instance->DMAMFBOCR & ( __OVERFLOW__ ) ) == ( __OVERFLOW__ ) ) + +/** + * @brief Set the DMA Receive status watchdog timer register value + * @param __HANDLE__: ETH Handle + * @param __VALUE__: DMA Receive status watchdog timer register value + * @retval None + */ + #define __HAL_ETH_SET_RECEIVE_WATCHDOG_TIMER( __HANDLE__, __VALUE__ ) ( ( __HANDLE__ )->Instance->DMARSWTR = ( __VALUE__ ) ) + +/** + * @brief Enables any unicast packet filtered by the MAC address + * recognition to be a wake-up frame. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_GLOBAL_UNICAST_WAKEUP_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACPMTCSR |= ETH_MACPMTCSR_GU ) + +/** + * @brief Disables any unicast packet filtered by the MAC address + * recognition to be a wake-up frame. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_GLOBAL_UNICAST_WAKEUP_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACPMTCSR &= ~ETH_MACPMTCSR_GU ) + +/** + * @brief Enables the MAC Wake-Up Frame Detection. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_WAKEUP_FRAME_DETECTION_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACPMTCSR |= ETH_MACPMTCSR_WFE ) + +/** + * @brief Disables the MAC Wake-Up Frame Detection. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_WAKEUP_FRAME_DETECTION_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACPMTCSR &= ~ETH_MACPMTCSR_WFE ) + +/** + * @brief Enables the MAC Magic Packet Detection. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_MAGIC_PACKET_DETECTION_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACPMTCSR |= ETH_MACPMTCSR_MPE ) + +/** + * @brief Disables the MAC Magic Packet Detection. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_MAGIC_PACKET_DETECTION_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACPMTCSR &= ~ETH_MACPMTCSR_WFE ) + +/** + * @brief Enables the MAC Power Down. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_POWER_DOWN_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACPMTCSR |= ETH_MACPMTCSR_PD ) + +/** + * @brief Disables the MAC Power Down. + * @param __HANDLE__: ETH Handle + * @retval None + */ + #define __HAL_ETH_POWER_DOWN_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MACPMTCSR &= ~ETH_MACPMTCSR_PD ) + +/** + * @brief Checks whether the specified Ethernet PMT flag is set or not. + * @param __HANDLE__: ETH Handle. + * @param __FLAG__: specifies the flag to check. + * This parameter can be one of the following values: + * @arg ETH_PMT_FLAG_WUFFRPR : Wake-Up Frame Filter Register Pointer Reset + * @arg ETH_PMT_FLAG_WUFR : Wake-Up Frame Received + * @arg ETH_PMT_FLAG_MPR : Magic Packet Received + * @retval The new state of Ethernet PMT Flag (SET or RESET). + */ + #define __HAL_ETH_GET_PMT_FLAG_STATUS( __HANDLE__, __FLAG__ ) ( ( ( __HANDLE__ )->Instance->MACPMTCSR & ( __FLAG__ ) ) == ( __FLAG__ ) ) + +/** + * @brief Preset and Initialize the MMC counters to almost-full value: 0xFFFF_FFF0 (full - 16) + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_MMC_COUNTER_FULL_PRESET( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MMCCR |= ( ETH_MMCCR_MCFHP | ETH_MMCCR_MCP ) ) + +/** + * @brief Preset and Initialize the MMC counters to almost-half value: 0x7FFF_FFF0 (half - 16) + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_MMC_COUNTER_HALF_PRESET( __HANDLE__ ) \ + do { ( __HANDLE__ )->Instance->MMCCR &= ~ETH_MMCCR_MCFHP; \ + ( __HANDLE__ )->Instance->MMCCR |= ETH_MMCCR_MCP; } while( 0 ) + +/** + * @brief Enables the MMC Counter Freeze. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_MMC_COUNTER_FREEZE_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MMCCR |= ETH_MMCCR_MCF ) + +/** + * @brief Disables the MMC Counter Freeze. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_MMC_COUNTER_FREEZE_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MMCCR &= ~ETH_MMCCR_MCF ) + +/** + * @brief Enables the MMC Reset On Read. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_ETH_MMC_RESET_ONREAD_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MMCCR |= ETH_MMCCR_ROR ) + +/** + * @brief Disables the MMC Reset On Read. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_ETH_MMC_RESET_ONREAD_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MMCCR &= ~ETH_MMCCR_ROR ) + +/** + * @brief Enables the MMC Counter Stop Rollover. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_ETH_MMC_COUNTER_ROLLOVER_ENABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MMCCR &= ~ETH_MMCCR_CSR ) + +/** + * @brief Disables the MMC Counter Stop Rollover. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_ETH_MMC_COUNTER_ROLLOVER_DISABLE( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MMCCR |= ETH_MMCCR_CSR ) + +/** + * @brief Resets the MMC Counters. + * @param __HANDLE__: ETH Handle. + * @retval None + */ + #define __HAL_ETH_MMC_COUNTERS_RESET( __HANDLE__ ) ( ( __HANDLE__ )->Instance->MMCCR |= ETH_MMCCR_CR ) + +/** + * @brief Enables the specified Ethernet MMC Rx interrupts. + * @param __HANDLE__: ETH Handle. + * @param __INTERRUPT__: specifies the Ethernet MMC interrupt sources to be enabled or disabled. + * This parameter can be one of the following values: + * @arg ETH_MMC_IT_RGUF : When Rx good unicast frames counter reaches half the maximum value + * @arg ETH_MMC_IT_RFAE : When Rx alignment error counter reaches half the maximum value + * @arg ETH_MMC_IT_RFCE : When Rx crc error counter reaches half the maximum value + * @retval None + */ + #define __HAL_ETH_MMC_RX_IT_ENABLE( __HANDLE__, __INTERRUPT__ ) ( __HANDLE__ )->Instance->MMCRIMR &= ~( ( __INTERRUPT__ ) & 0xEFFFFFFF ) + +/** + * @brief Disables the specified Ethernet MMC Rx interrupts. + * @param __HANDLE__: ETH Handle. + * @param __INTERRUPT__: specifies the Ethernet MMC interrupt sources to be enabled or disabled. + * This parameter can be one of the following values: + * @arg ETH_MMC_IT_RGUF : When Rx good unicast frames counter reaches half the maximum value + * @arg ETH_MMC_IT_RFAE : When Rx alignment error counter reaches half the maximum value + * @arg ETH_MMC_IT_RFCE : When Rx crc error counter reaches half the maximum value + * @retval None + */ + #define __HAL_ETH_MMC_RX_IT_DISABLE( __HANDLE__, __INTERRUPT__ ) ( __HANDLE__ )->Instance->MMCRIMR |= ( ( __INTERRUPT__ ) & 0xEFFFFFFF ) + +/** + * @brief Enables the specified Ethernet MMC Tx interrupts. + * @param __HANDLE__: ETH Handle. + * @param __INTERRUPT__: specifies the Ethernet MMC interrupt sources to be enabled or disabled. + * This parameter can be one of the following values: + * @arg ETH_MMC_IT_TGF : When Tx good frame counter reaches half the maximum value + * @arg ETH_MMC_IT_TGFMSC: When Tx good multi col counter reaches half the maximum value + * @arg ETH_MMC_IT_TGFSC : When Tx good single col counter reaches half the maximum value + * @retval None + */ + #define __HAL_ETH_MMC_TX_IT_ENABLE( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->MMCRIMR &= ~( __INTERRUPT__ ) ) + +/** + * @brief Disables the specified Ethernet MMC Tx interrupts. + * @param __HANDLE__: ETH Handle. + * @param __INTERRUPT__: specifies the Ethernet MMC interrupt sources to be enabled or disabled. + * This parameter can be one of the following values: + * @arg ETH_MMC_IT_TGF : When Tx good frame counter reaches half the maximum value + * @arg ETH_MMC_IT_TGFMSC: When Tx good multi col counter reaches half the maximum value + * @arg ETH_MMC_IT_TGFSC : When Tx good single col counter reaches half the maximum value + * @retval None + */ + #define __HAL_ETH_MMC_TX_IT_DISABLE( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->MMCRIMR |= ( __INTERRUPT__ ) ) + +/** + * @brief Enables the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_ENABLE_IT() EXTI->IMR |= ( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Disables the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_DISABLE_IT() EXTI->IMR &= ~( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Enable event on ETH External event line. + * @retval None. + */ + #define __HAL_ETH_WAKEUP_EXTI_ENABLE_EVENT() EXTI->EMR |= ( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Disable event on ETH External event line + * @retval None. + */ + #define __HAL_ETH_WAKEUP_EXTI_DISABLE_EVENT() EXTI->EMR &= ~( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Get flag of the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_GET_FLAG() EXTI->PR & ( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Clear flag of the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_CLEAR_FLAG() EXTI->PR = ( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Enables rising edge trigger to the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_ENABLE_RISING_EDGE_TRIGGER() EXTI->RTSR |= ETH_EXTI_LINE_WAKEUP + +/** + * @brief Disables the rising edge trigger to the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_DISABLE_RISING_EDGE_TRIGGER() EXTI->RTSR &= ~( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Enables falling edge trigger to the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_ENABLE_FALLING_EDGE_TRIGGER() EXTI->FTSR |= ( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Disables falling edge trigger to the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_DISABLE_FALLING_EDGE_TRIGGER() EXTI->FTSR &= ~( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Enables rising/falling edge trigger to the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_ENABLE_FALLINGRISING_TRIGGER() \ + EXTI->RTSR |= ETH_EXTI_LINE_WAKEUP; \ + EXTI->FTSR |= ETH_EXTI_LINE_WAKEUP + +/** + * @brief Disables rising/falling edge trigger to the ETH External interrupt line. + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_DISABLE_FALLINGRISING_TRIGGER() \ + EXTI->RTSR &= ~( ETH_EXTI_LINE_WAKEUP ); \ + EXTI->FTSR &= ~( ETH_EXTI_LINE_WAKEUP ) + +/** + * @brief Generate a Software interrupt on selected EXTI line. + * @retval None. + */ + #define __HAL_ETH_WAKEUP_EXTI_GENERATE_SWIT() EXTI->SWIER |= ETH_EXTI_LINE_WAKEUP + +/** + * @} + */ +/* Exported functions --------------------------------------------------------*/ + +/** @addtogroup ETH_Exported_Functions + * @{ + */ + +/* Initialization and de-initialization functions ****************************/ + +/** @addtogroup ETH_Exported_Functions_Group1 + * @{ + */ + HAL_StatusTypeDef HAL_ETH_Init( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_DeInit( ETH_HandleTypeDef * heth ); + void HAL_ETH_MspInit( ETH_HandleTypeDef * heth ); + void HAL_ETH_MspDeInit( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_DMATxDescListInit( ETH_HandleTypeDef * heth, + ETH_DMADescTypeDef * DMATxDescTab, + uint8_t * TxBuff, + uint32_t TxBuffCount ); + HAL_StatusTypeDef HAL_ETH_DMARxDescListInit( ETH_HandleTypeDef * heth, + ETH_DMADescTypeDef * DMARxDescTab, + uint8_t * RxBuff, + uint32_t RxBuffCount ); + +/** + * @} + */ +/* IO operation functions ****************************************************/ + +/** @addtogroup ETH_Exported_Functions_Group2 + * @{ + */ + HAL_StatusTypeDef HAL_ETH_TransmitFrame( ETH_HandleTypeDef * heth, + uint32_t FrameLength ); + HAL_StatusTypeDef HAL_ETH_GetReceivedFrame( ETH_HandleTypeDef * heth ); +/* Communication with PHY functions*/ + HAL_StatusTypeDef HAL_ETH_ReadPHYRegister( ETH_HandleTypeDef * heth, + uint16_t PHYReg, + uint32_t * RegValue ); + HAL_StatusTypeDef HAL_ETH_WritePHYRegister( ETH_HandleTypeDef * heth, + uint16_t PHYReg, + uint32_t RegValue ); +/* Non-Blocking mode: Interrupt */ + HAL_StatusTypeDef HAL_ETH_GetReceivedFrame_IT( ETH_HandleTypeDef * heth ); + void HAL_ETH_IRQHandler( ETH_HandleTypeDef * heth ); +/* Callback in non blocking modes (Interrupt) */ + void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef * heth ); + void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef * heth ); + void HAL_ETH_ErrorCallback( ETH_HandleTypeDef * heth ); + +/** + * @} + */ + +/* Peripheral Control functions **********************************************/ + +/** @addtogroup ETH_Exported_Functions_Group3 + * @{ + */ + + HAL_StatusTypeDef HAL_ETH_Start( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_Stop( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_ConfigMAC( ETH_HandleTypeDef * heth, + ETH_MACInitTypeDef * macconf ); + HAL_StatusTypeDef HAL_ETH_ConfigDMA( ETH_HandleTypeDef * heth, + ETH_DMAInitTypeDef * dmaconf ); + +/** + * @} + */ + +/* Peripheral State functions ************************************************/ + +/** @addtogroup ETH_Exported_Functions_Group4 + * @{ + */ + HAL_ETH_StateTypeDef HAL_ETH_GetState( ETH_HandleTypeDef * heth ); + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + #ifdef __cplusplus + } + #endif + +#endif /* __STM32Fxx_HAL_ETH_H */ + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/NetworkInterface.c new file mode 100644 index 0000000..48a161d --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/NetworkInterface.c @@ -0,0 +1,946 @@ +/* + * Some constants, hardware definitions and comments taken from ST's HAL driver + * library, COPYRIGHT(c) 2015 STMicroelectronics. + */ + +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_DNS.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "phyHandling.h" + +/* ST includes. */ +#include "stm32h7xx_hal.h" + +#ifndef STM32Hxx_HAL_ETH_H + +/* + * The ST HAL library provides stm32h7xx_hal_eth.{c,h}. + * This FreeRTOS+TCP driver renamed these files to stm32hxx_hal_eth.{c,h} + * by removing the '7'. + * Please make sure that "portable/NetworkInterface/STM32Hxx" is included + * in the include paths earlier than "STM32H7xx_HAL_Driver/Inc". + * and also make sure that you have defined 'HAL_ETH_MODULE_ENABLED' + * in your copy of "stm32h7xx_hal_conf". + */ + #error stm32hxx_hal_eth.h is possibly not included +#endif + +/* Interrupt events to process: reception, transmission and error handling. */ +#define EMAC_IF_RX_EVENT 1UL +#define EMAC_IF_TX_EVENT 2UL +#define EMAC_IF_ERR_EVENT 4UL + + +#ifndef niEMAC_HANDLER_TASK_NAME + #define niEMAC_HANDLER_TASK_NAME "EMAC-task" +#endif + +#ifndef niEMAC_HANDLER_TASK_STACK_SIZE + #define niEMAC_HANDLER_TASK_STACK_SIZE ( 4 * configMINIMAL_STACK_SIZE ) +#endif + +#ifndef niEMAC_HANDLER_TASK_PRIORITY + #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1 +#endif + + +/* Bit map of outstanding ETH interrupt events for processing. */ +static volatile uint32_t ulISREvents; + +typedef enum +{ + eMACInit, /* Must initialise MAC. */ + eMACPass, /* Initialisation was successful. */ + eMACFailed, /* Initialisation failed. */ +} eMAC_INIT_STATUS_TYPE; + +static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit; + +/* xTXDescriptorSemaphore is shared with stm32h7xx_hal_eth.c. */ +SemaphoreHandle_t xTXDescriptorSemaphore = NULL; + +/* Both the IP-task and the EMAC task use the TX channel. Use + * a mutex to protect it against synchronous access by both tasks. */ +static SemaphoreHandle_t xTransmissionMutex; + +/* Global Ethernet handle */ +static ETH_HandleTypeDef xEthHandle; +static ETH_TxPacketConfig xTxConfig; + +/* + * About the section ".ethernet_data" : the DMA wants the descriptors and buffers allocated in the + * RAM3 memory, which can be added to the .LD file as follows:: + * + * RAM3 (xrw) : ORIGIN = 0x24040000, LENGTH = 0x8000 + * + * .ethernet_data : + * { + * PROVIDE_HIDDEN (__ethernet_data_start = .); + * KEEP (*(SORT(.ethernet_data.*))) + * KEEP (*(.ethernet_data*)) + * PROVIDE_HIDDEN (__ethernet_data_end = .); + * } >RAM3 + * + */ +/* Ethernet Rx DMA Descriptors */ +ETH_DMADescTypeDef DMARxDscrTab[ ETH_RX_DESC_CNT ] __attribute__( ( section( ".ethernet_data" ), aligned( 32 ) ) ); + +/* Ethernet Receive Buffer */ +#if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + uint8_t Rx_Buff[ ETH_RX_DESC_CNT ][ ETH_RX_BUF_SIZE ] __attribute__( ( section( ".ethernet_data" ), aligned( 32 ) ) ); +#endif + +/* Ethernet Tx DMA Descriptors */ +ETH_DMADescTypeDef DMATxDscrTab[ ETH_TX_DESC_CNT ] __attribute__( ( section( ".ethernet_data" ), aligned( 32 ) ) ); + +/* Ethernet Transmit Buffer */ +#if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + uint8_t Tx_Buff[ ETH_TX_DESC_CNT ][ ETH_TX_BUF_SIZE ] __attribute__( ( section( ".ethernet_data" ), aligned( 32 ) ) ); +#endif + +/* This function binds PHY IO functions, then inits and configures */ +static void prvMACBProbePhy( void ); + +/* Force a negotiation with the Switch or Router and wait for LS. */ +static void prvEthernetUpdateConfig( BaseType_t xForce ); + +/* Holds the handle of the task used as a deferred interrupt processor. The + * handle is used so direct notifications can be sent to the task for all EMAC/DMA + * related interrupts. */ +static TaskHandle_t xEMACTaskHandle = NULL; + +/* + * A deferred interrupt handler task that processes + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/* + * See if there is a new packet and forward it to the IP-task. + */ +static BaseType_t prvNetworkInterfaceInput( void ); + +/* Private PHY IO functions and properties */ +static int32_t ETH_PHY_IO_ReadReg( uint32_t DevAddr, + uint32_t RegAddr, + uint32_t * pRegVal ); +static int32_t ETH_PHY_IO_WriteReg( uint32_t DevAddr, + uint32_t RegAddr, + uint32_t RegVal ); + +static void vClearOptionBit( volatile uint32_t * pulValue, + uint32_t ulValue ); + +static size_t uxGetOwnCount( ETH_HandleTypeDef * heth ); + +/*-----------------------------------------------------------*/ + +static EthernetPhy_t xPhyObject; +/* For local use only: describe the PHY's properties: */ +const PhyProperties_t xPHYProperties = +{ + .ucSpeed = PHY_SPEED_AUTO, + .ucDuplex = PHY_DUPLEX_AUTO, + .ucMDI_X = PHY_MDIX_DIRECT +}; +/*-----------------------------------------------------------*/ + + + +/******************************************************************************* +* Network Interface API Functions +*******************************************************************************/ + +static uint8_t * pucGetRXBuffer( size_t uxSize ) +{ + TickType_t uxBlockTimeTicks = ipMS_TO_MIN_TICKS( 10U ); + NetworkBufferDescriptor_t * pxBufferDescriptor; + uint8_t * pucReturn = NULL; + + pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( uxSize, uxBlockTimeTicks ); + + if( pxBufferDescriptor != NULL ) + { + pucReturn = pxBufferDescriptor->pucEthernetBuffer; + } + + return pucReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + BaseType_t xResult = pdFAIL; + HAL_StatusTypeDef xHalEthInitStatus; + size_t uxIndex = 0; + + if( xMacInitStatus == eMACInit ) + { + /* + * Initialize ETH Handler + * It assumes that Ethernet GPIO and clock configuration + * are already done in the ETH_MspInit() + */ + xEthHandle.Instance = ETH; + xEthHandle.Init.MACAddr = ( uint8_t * ) FreeRTOS_GetMACAddress(); + xEthHandle.Init.MediaInterface = HAL_ETH_RMII_MODE; + xEthHandle.Init.TxDesc = DMATxDscrTab; + xEthHandle.Init.RxDesc = DMARxDscrTab; + xEthHandle.Init.RxBuffLen = ( ETH_RX_BUF_SIZE - ipBUFFER_PADDING ) & ~( ( uint32_t ) 3U ); + + /* Make sure that all unused fields are cleared. */ + memset( &( DMATxDscrTab ), '\0', sizeof( DMATxDscrTab ) ); + memset( &( DMARxDscrTab ), '\0', sizeof( DMARxDscrTab ) ); + + xHalEthInitStatus = HAL_ETH_Init( &( xEthHandle ) ); + + if( xHalEthInitStatus == HAL_OK ) + { + /* Configuration for HAL_ETH_Transmit(_IT). */ + memset( &( xTxConfig ), 0, sizeof( ETH_TxPacketConfig ) ); + xTxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CRCPAD; + + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + { + /*xTxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC; */ + xTxConfig.Attributes |= ETH_TX_PACKETS_FEATURES_CSUM; + xTxConfig.ChecksumCtrl = ETH_DMATXNDESCRF_CIC_IPHDR_PAYLOAD_INSERT_PHDR_CALC; + } + #else + { + xTxConfig.ChecksumCtrl = ETH_CHECKSUM_DISABLE; + } + #endif + xTxConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT; + + /* This counting semaphore will count the number of free TX DMA descriptors. */ + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ETH_TX_DESC_CNT, ( UBaseType_t ) ETH_TX_DESC_CNT ); + configASSERT( xTXDescriptorSemaphore ); + + xTransmissionMutex = xSemaphoreCreateMutex(); + configASSERT( xTransmissionMutex ); + + /* Assign Rx memory buffers to a DMA Rx descriptor */ + for( uxIndex = 0; uxIndex < ETH_RX_DESC_CNT; uxIndex++ ) + { + uint8_t * pucBuffer; + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + pucBuffer = pucGetRXBuffer( ETH_RX_BUF_SIZE ); + configASSERT( pucBuffer != NULL ); + } + #else + { + pucBuffer = Rx_Buff[ uxIndex ]; + } + #endif + + HAL_ETH_DescAssignMemory( &( xEthHandle ), uxIndex, pucBuffer, NULL ); + } + + /* Configure the MDIO Clock */ + HAL_ETH_SetMDIOClockRange( &( xEthHandle ) ); + + /* Initialize the MACB and set all PHY properties */ + prvMACBProbePhy(); + + /* Force a negotiation with the Switch or Router and wait for LS. */ + prvEthernetUpdateConfig( pdTRUE ); + + /* The deferred interrupt handler task is created at the highest + * possible priority to ensure the interrupt handler can return directly + * to it. The task's handle is stored in xEMACTaskHandle so interrupts can + * notify the task when there is something to process. */ + if( xTaskCreate( prvEMACHandlerTask, niEMAC_HANDLER_TASK_NAME, niEMAC_HANDLER_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &( xEMACTaskHandle ) ) == pdPASS ) + { + /* The task was created successfully. */ + xMacInitStatus = eMACPass; + } + else + { + xMacInitStatus = eMACFailed; + } + } + else + { + xMacInitStatus = eMACFailed; + } + } /* ( xMacInitStatus == eMACInit ) */ + + if( xMacInitStatus == eMACPass ) + { + if( xPhyObject.ulLinkStatusMask != 0uL ) + { + xResult = pdPASS; + FreeRTOS_printf( ( "Link Status is high\n" ) ); + } + else + { + /* For now pdFAIL will be returned. But prvEMACHandlerTask() is running + * and it will keep on checking the PHY and set 'ulLinkStatusMask' when necessary. */ + } + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ + BaseType_t xReturn; + + if( xPhyObject.ulLinkStatusMask != 0U ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t xReleaseAfterSend ) +{ + BaseType_t xResult = pdFAIL; + TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 100U ); + uint8_t * pucTXBuffer; + + if( xGetPhyLinkStatus() == pdPASS ) + { + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + /* Zero-copy method, pass the buffer. */ + pucTXBuffer = pxDescriptor->pucEthernetBuffer; + + /* As the buffer is passed to the driver, it must exist. + * The library takes care of this. */ + configASSERT( xReleaseAfterSend != pdFALSE ); + #else + pucTXBuffer = Tx_Buff[ xEthHandle.TxDescList.CurTxDesc ]; + /* The copy method, left here for educational purposes. */ + configASSERT( pxDescriptor->xDataLength <= sizeof( Tx_Buff[ 0 ] ) ); + #endif + + ETH_BufferTypeDef xTransmitBuffer = + { + .buffer = pucTXBuffer, + .len = pxDescriptor->xDataLength, + .next = NULL /* FreeRTOS+TCP does not use linked buffers. */ + }; + /* This is the total length, which is equal to the buffer. */ + xTxConfig.Length = pxDescriptor->xDataLength; + xTxConfig.TxBuffer = &( xTransmitBuffer ); + + /* This counting semaphore counts the number of free TX DMA descriptors. */ + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + /* If the logging routine is using the network, the following message + * may cause a new error message. */ + FreeRTOS_printf( ( "emacps_send_message: Time-out waiting for TX buffer\n" ) ); + } + else + { + /* Memory barrier: Make sure that the data written to the packet buffer got written. */ + __DSB(); + + /* Get exclusive accces to the TX process. + * Both the IP-task and the EMAC task will work on the TX process. */ + if( xSemaphoreTake( xTransmissionMutex, xBlockTimeTicks ) != pdFAIL ) + { + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Do not release the buffer. */ + xReleaseAfterSend = pdFALSE; + } + #else + { + memcpy( pucTXBuffer, pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ); + + /* A memory barrier to make sure that the outgoing packets has been written + * to the physical memory. */ + __DSB(); + } + #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + + if( HAL_ETH_Transmit_IT( &( xEthHandle ), &( xTxConfig ) ) == HAL_OK ) + { + xResult = pdPASS; + } + + /* And release the mutex. */ + xSemaphoreGive( xTransmissionMutex ); + } + + /* Call the standard trace macro to log the send event. */ + iptraceNETWORK_INTERFACE_TRANSMIT(); + } + } + + if( xReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +/******************************************************************************* +* END Network Interface API Functions +*******************************************************************************/ + + + +/******************************************************************************* +* Network Interface Static Functions +*******************************************************************************/ + +static void prvMACBProbePhy( void ) +{ + /* Bind the write and read access functions. */ + vPhyInitialise( &( xPhyObject ), + ( xApplicationPhyReadHook_t ) ETH_PHY_IO_ReadReg, + ( xApplicationPhyWriteHook_t ) ETH_PHY_IO_WriteReg ); + /* Poll the bus for all connected PHY's. */ + xPhyDiscover( &( xPhyObject ) ); + /* Configure them using the properties provided. */ + xPhyConfigure( &( xPhyObject ), &( xPHYProperties ) ); +} +/*-----------------------------------------------------------*/ + +static void prvEthernetUpdateConfig( BaseType_t xForce ) +{ + ETH_MACConfigTypeDef MACConf; + uint32_t speed = 0, duplex = 0; + + FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS mask %02lX Force %d\n", + xPhyObject.ulLinkStatusMask, + ( int ) xForce ) ); + + if( ( xForce != pdFALSE ) || ( xPhyObject.ulLinkStatusMask != 0 ) ) + { + /* Restart the auto-negotiation. */ + xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &( xPhyObject ) ) ); + + /* Configure the MAC with the Duplex Mode fixed by the + * auto-negotiation process. */ + if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL ) + { + duplex = ETH_FULLDUPLEX_MODE; + } + else + { + duplex = ETH_HALFDUPLEX_MODE; + } + + /* Configure the MAC with the speed fixed by the + * auto-negotiation process. */ + if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 ) + { + speed = ETH_SPEED_10M; + } + else + { + speed = ETH_SPEED_100M; + } + + /* Get MAC and configure it */ + HAL_ETH_GetMACConfig( &( xEthHandle ), &( MACConf ) ); + MACConf.DuplexMode = duplex; + MACConf.Speed = speed; + HAL_ETH_SetMACConfig( &( xEthHandle ), &( MACConf ) ); + #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM != 0 ) + { + MACConf.ChecksumOffload = ENABLE; + } + #else + { + MACConf.ChecksumOffload = DISABLE; + } + #endif /* ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM != 0 ) */ + + /* Restart MAC interface */ + HAL_ETH_Start_IT( &( xEthHandle ) ); + } + else + { + /* Stop MAC interface */ + HAL_ETH_Stop_IT( &( xEthHandle ) ); + } +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvNetworkInterfaceInput( void ) +{ + BaseType_t xReturn = 0; + + /* For as long as a packet is immediately available. */ + for( ; ; ) + { + NetworkBufferDescriptor_t * pxBufferDescriptor; + NetworkBufferDescriptor_t * pxReceivedBuffer = NULL; + ETH_BufferTypeDef data_buffer; + size_t uxDataLength; + size_t uxLength; + + uxDataLength = HAL_ETH_GetRxData( &( xEthHandle ), &( data_buffer ) ); + + if( uxDataLength == 0U ) + { + /* No more packets received. */ + break; + } + + xReturn++; + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + /* Reserve the maximum length for the next reception. */ + uxLength = ETH_RX_BUF_SIZE; + + if( data_buffer.buffer != NULL ) + { + pxReceivedBuffer = pxPacketBuffer_to_NetworkBuffer( data_buffer.buffer ); + #if ( ipconfigTCP_IP_SANITY != 0 ) + { + configASSERT( bIsValidNetworkDescriptor( pxReceivedBuffer ) != 0 ); + } + #endif + } + + if( pxReceivedBuffer == NULL ) + { + FreeRTOS_printf( ( "Strange: no descriptor received\n" ) ); + } + } + #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + { + /* Reserve the length of the packet that was just received. */ + uxLength = uxDataLength; + } + #endif /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + + pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( uxLength, 0u ); + + if( pxBufferDescriptor == NULL ) + { + /* The event was lost because a network buffer was not available. + * Call the standard trace macro to log the occurrence. */ + iptraceETHERNET_RX_EVENT_LOST(); + } + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + if( pxBufferDescriptor == NULL ) + { + /* Can not receive this packet. Buffer will be re-used. */ + pxReceivedBuffer = NULL; + } + else if( pxReceivedBuffer != NULL ) + { + pxReceivedBuffer->xDataLength = uxDataLength; + } + else + { + /* Allocating a new buffer failed. */ + } + } + #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + { + if( pxBufferDescriptor != NULL ) + { + pxReceivedBuffer = pxBufferDescriptor; + /* The copy method. */ + memcpy( pxReceivedBuffer->pucEthernetBuffer, data_buffer.buffer, uxDataLength ); + pxReceivedBuffer->xDataLength = uxDataLength; + /* Make sure that the descriptor isn't used any more. */ + pxBufferDescriptor = NULL; + } + } + #endif /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + + { + uint8_t * pucBuffer = NULL; + + if( pxBufferDescriptor != NULL ) + { + pucBuffer = pxBufferDescriptor->pucEthernetBuffer; + } + + /* Assign an RX buffer to the descriptor, so that + * a next packet can be received. */ + HAL_ETH_BuildRxDescriptors( &( xEthHandle ), pucBuffer ); + } + + /* See if the data contained in the received Ethernet frame needs + * to be processed. NOTE! It is preferable to do this in + * the interrupt service routine itself, which would remove the need + * to unblock this task for packets that don't need processing. */ + + if( pxReceivedBuffer != NULL ) + { + BaseType_t xDoRelease = pdFALSE; + + if( eConsiderFrameForProcessing( pxReceivedBuffer->pucEthernetBuffer ) != eProcessBuffer ) + { + /* The Ethernet frame can be dropped, but the Ethernet buffer must be released. */ + xDoRelease = pdTRUE; + } + else + { + /* The event about to be sent to the TCP/IP is an Rx event. + * pvData is used to point to the network buffer descriptor that + * now references the received data. */ + + IPStackEvent_t xRxEvent = + { + .eEventType = eNetworkRxEvent, + .pvData = ( void * ) pxReceivedBuffer + }; + + /* Send the data to the TCP/IP stack. */ + if( xSendEventStructToIPTask( &( xRxEvent ), 0 ) != pdFALSE ) + { + /* The message was successfully sent to the TCP/IP stack. + * Call the standard trace macro to log the occurrence. */ + iptraceNETWORK_INTERFACE_RECEIVE(); + } + else + { + xDoRelease = pdTRUE; + + /* The buffer could not be sent to the IP task so the buffer + * must be released. */ + + /* Make a call to the standard trace macro to log the + * occurrence. */ + iptraceETHERNET_RX_EVENT_LOST(); + } + } + + if( xDoRelease != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxReceivedBuffer ); + } + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/******************************************************************************* +* END Network Interface Static Functions +*******************************************************************************/ + + + +/******************************************************************************* +* PHY IO Functions +*******************************************************************************/ + +/** + * @brief Read a PHY register through the MDIO interface. + * @param DevAddr: PHY port address + * @param RegAddr: PHY register address + * @param pRegVal: pointer to hold the register value + * @retval 0 if OK -1 if Error + */ +static int32_t ETH_PHY_IO_ReadReg( uint32_t ulDevAddr, + uint32_t ulRegAddr, + uint32_t * pulRegVal ) +{ + int32_t iResult = -1; + + if( HAL_ETH_ReadPHYRegister( &( xEthHandle ), ulDevAddr, ulRegAddr, pulRegVal ) == HAL_OK ) + { + iResult = 0; + } + + return iResult; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Write a value to a PHY register through the MDIO interface. + * @param DevAddr: PHY port address + * @param RegAddr: PHY register address + * @param RegVal: Value to be written + * @retval 0 if OK -1 if Error + */ +static int32_t ETH_PHY_IO_WriteReg( uint32_t ulDevAddr, + uint32_t ulRegAddr, + uint32_t pulRegVal ) +{ + int32_t iResult = -1; + + if( HAL_ETH_WritePHYRegister( &( xEthHandle ), ulDevAddr, ulRegAddr, pulRegVal ) == HAL_OK ) + { + iResult = 0; + } + + return iResult; +} +/*-----------------------------------------------------------*/ + +/******************************************************************************* +* END PHY IO Functions +*******************************************************************************/ + + + +/******************************************************************************* +* Ethernet Handling Functions +*******************************************************************************/ + +void ETH_IRQHandler( void ) +{ + HAL_ETH_IRQHandler( &( xEthHandle ) ); +} +/*-----------------------------------------------------------*/ + +static void prvSetFlagsAndNotify( uint32_t ulFlags ) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + /* Ethernet RX-Complete callback function, elsewhere declared as weak. + * No critical section needed, this function is called from an ISR. */ + ulISREvents |= ulFlags; + + /* Wakeup the prvEMACHandlerTask. */ + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &( xHigherPriorityTaskWoken ) ); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + } +} +/*-----------------------------------------------------------*/ + +void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef * heth ) +{ + ( void ) heth; + prvSetFlagsAndNotify( EMAC_IF_TX_EVENT ); +} +/*-----------------------------------------------------------*/ + +void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef * heth ) +{ + ( void ) heth; + prvSetFlagsAndNotify( EMAC_IF_RX_EVENT ); +} +/*-----------------------------------------------------------*/ + +void HAL_ETH_DMAErrorCallback( ETH_HandleTypeDef * heth ) +{ + ( void ) heth; + prvSetFlagsAndNotify( EMAC_IF_ERR_EVENT ); +} +/*-----------------------------------------------------------*/ + +/******************************************************************************* +* END Ethernet Handling Functions +*******************************************************************************/ + +uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * ETH_RX_BUF_SIZE ] +#if ( ipconfigZERO_COPY_RX_DRIVER != 0 || ipconfigZERO_COPY_TX_DRIVER != 0 ) + __attribute__( ( section( ".ethernet_data" ) ) ) +#endif /* ( ipconfigZERO_COPY_RX_DRIVER != 0 || ipconfigZERO_COPY_TX_DRIVER != 0 ) */ +__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 += ETH_RX_BUF_SIZE; + } +} +/*-----------------------------------------------------------*/ + +static void vClearOptionBit( volatile uint32_t * pulValue, + uint32_t ulValue ) +{ + portENTER_CRITICAL(); + *( pulValue ) &= ~( ulValue ); + portEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +static size_t uxGetOwnCount( ETH_HandleTypeDef * heth ) +{ + BaseType_t xIndex; + BaseType_t xCount = 0; + ETH_RxDescListTypeDef * dmarxdesclist = &heth->RxDescList; + + /* Count the number of RX descriptors that are owned by DMA. */ + for( xIndex = 0; xIndex < ETH_RX_DESC_CNT; xIndex++ ) + { + __IO const ETH_DMADescTypeDef * dmarxdesc = + ( __IO const ETH_DMADescTypeDef * )dmarxdesclist->RxDesc[ xIndex ]; + + if( ( dmarxdesc->DESC3 & ETH_DMARXNDESCWBF_OWN ) != 0U ) + { + xCount++; + } + } + + return xCount; +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void * pvParameters ) +{ +/* When sending a packet, all descriptors in the transmission channel may + * be occupied. In stat case, the program will wait (block) for the counting + * semaphore. */ + const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); + size_t uxTXDescriptorsUsed = 0U; + size_t uxRXDescriptorsUsed = ETH_RX_DESC_CNT; + + ( void ) pvParameters; + + for( ; ; ) + { + BaseType_t xResult = 0; + + #if ( ipconfigHAS_PRINTF != 0 ) + { + size_t uxUsed; + size_t uxOwnCount; + + /* Call a function that monitors resources: the amount of free network + * buffers and the amount of free space on the heap. See FreeRTOS_IP.c + * for more detailed comments. */ + vPrintResourceStats(); + + /* Some more statistics: number of free descriptors. */ + uxUsed = ETH_TX_DESC_CNT - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + if( uxTXDescriptorsUsed < uxUsed ) + { + uxTXDescriptorsUsed = uxUsed; + FreeRTOS_printf( ( "TX descriptors %u/%u\n", + uxTXDescriptorsUsed, + ETH_TX_DESC_CNT ) ); + } + + uxOwnCount = uxGetOwnCount( &( xEthHandle ) ); + + if( uxRXDescriptorsUsed > uxOwnCount ) + { + uxRXDescriptorsUsed = uxOwnCount; + FreeRTOS_printf( ( "RX descriptors %u/%u\n", + uxRXDescriptorsUsed, + ETH_RX_DESC_CNT ) ); + } + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + + ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); + + /* Wait for the Ethernet MAC interrupt to indicate that another packet + * has been received. */ + if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0U ) + { + vClearOptionBit( &( ulISREvents ), EMAC_IF_RX_EVENT ); + xResult = prvNetworkInterfaceInput(); + } + + /* When a packet has been transmitted, the descriptor must be + * prepared for a next transmission. + * When using zero-copy, the network buffer must be released + * ( i.e. returned to the pool of network buffers ). */ + + if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0U ) + { + vClearOptionBit( &( ulISREvents ), EMAC_IF_TX_EVENT ); + + if( xSemaphoreTake( xTransmissionMutex, 10000U ) != pdFAIL ) + { + ETH_Clear_Tx_Descriptors( &( xEthHandle ) ); + xSemaphoreGive( xTransmissionMutex ); + } + } + + /* Some error has occurred, possibly an overflow or an underflow. */ + if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0U ) + { + vClearOptionBit( &( ulISREvents ), EMAC_IF_ERR_EVENT ); + + xEthHandle.gState = HAL_ETH_STATE_READY; + /* Enable all interrupts */ + HAL_ETH_Start_IT( &( xEthHandle ) ); + xResult += prvNetworkInterfaceInput(); + } + + if( xPhyCheckLinkStatus( &xPhyObject, xResult ) != pdFALSE ) + { + /* + * The function xPhyCheckLinkStatus() returns pdTRUE if the + * Link Status has changes since it was called the last time. + */ + if( xGetPhyLinkStatus() == pdFALSE ) + { + /* Stop the DMA transfer. */ + HAL_ETH_Stop_IT( &( xEthHandle ) ); + /* Clear the Transmit buffers. */ + memset( &( DMATxDscrTab ), '\0', sizeof( DMATxDscrTab ) ); + /* Since the link is down, clear the descriptors. */ + ETH_Clear_Tx_Descriptors( &( xEthHandle ) ); + } + else + { + /* Something has changed to a Link Status, need re-check. */ + prvEthernetUpdateConfig( pdFALSE ); + } + } + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/readme.md b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/readme.md new file mode 100644 index 0000000..6ea5356 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/readme.md @@ -0,0 +1,122 @@ + +FreeRTOS+TCP driver for STM32H7xx. + +Some STM32 network settings are stored in 'stm32h7xx_hal_conf.h'. + +Number of DMA descriptors, for transmission and for reception. + +The descriptors for transmission are protected with a counting semaphore. +By the time that a packet has been sent, the other TX descriptor becomes +available already. +The number of descriptors has an incluence on the performance. But that also depends on the size +of the TCP buffers and TCP window sizes. + +When ETH_RX_DESC_CNT is too low, the adapter may miss incoming packets, they will be dropped. +When ETH_RX_DESC_CNT is low, sending packets becomes slower. + +Here are settings give a high performance for iperf3: + +~~~ +/* ########################### Ethernet Configuration ######################### */ +#define ETH_TX_DESC_CNT 14U /* number of Ethernet Tx DMA descriptors */ +#define ETH_RX_DESC_CNT 8U /* number of Ethernet Rx DMA descriptors */ +~~~ + +Two more defines that are needed: + +~~~ +#define HAL_ETH_MODULE_ENABLED +#define USE_HAL_ETH_REGISTER_CALLBACKS 0U /* ETH register callback disabled */ +~~~ + +The following macro's are **not** used by the FreeRTOS driver: + + #define ETH_MAC_ADDR0 ((uint8_t)0x02) + #define ETH_MAC_ADDR1 ((uint8_t)0x00) + ... + +All memory that is shared between the CPU and the DMA ETH peripheral, should be +located in special RAM area called ".ethernet_data". This shall be declared in +the linker file. + +It is possible to use the AXI SRAM for this, but RAM{1,2,3} are also connected +to the Ethernet MAC. + +Here is an example of the changes to the linker file: + + AXI_RAM (xrw) : ORIGIN = 0x24000000, LENGTH = 512K /* .ethernet_data declared here. */ + .ethernet_data : + { + PROVIDE_HIDDEN (__ethernet_data_start = .); + KEEP (*(SORT(.ethernet_data.*))) + KEEP (*(.ethernet_data*)) + PROVIDE_HIDDEN (__ethernet_data_end = .); + } >AXI_RAM + +Here is a table of 3 types of STH32H7 : + +|RAM area |H747|H743|H742|Location | +|-----------|----|----|----|----------| +|DTCM |128k|128k|128k|0x20000000| +|AXI-SRAM |511k|511k|384k|0x24000000| +|SRAM1 |128k|128k|32k |0x30000000| +|SRAM2 |128k|128k|16k |0x30020000| +|SRAM3 |32k | 32k| - |0x30040000| +|SRAM4 |64k |64k |64k |0x38000000| +|Backup SRAM|4k |4k |4k |0x38800000| + + +Please make sure that the addresses and lengths are correct for your model of STM32H7xx. +If you use a memory that is not supported, it will result in a DMA errors. + +In FreeRTOSIPConfig.h : + +Define the total number of network buffer descriptors, e.g. 64: + +~~~ + #define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ( 64 ) +~~~ + +It is recommended to use the zero-copy method for both reception and transmission: + +~~~ + #define ipconfigZERO_COPY_RX_DRIVER ( 1 ) + #define ipconfigZERO_COPY_TX_DRIVER ( 1 ) +~~~ + +The copy-method also works well, may just a little slower. + +Checksum cal be calculated in the Ethernet MAC, which is faster than doing manual calculations: + +~~~ + /* The checksums will be checked and calculated by the STM32F4x ETH peripheral. */ + #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM ( 1 ) + #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM ( 1 ) +~~~ + +The most important DMAC registers, along with their names which are used in the reference manual: + + __IO uint32_t DMAMR; // ETH_DMAMR DMA mode register + __IO uint32_t DMAISR; // ETH_DMAISR DMA Interrupt status register + __IO uint32_t DMADSR; // ETH_DMADSR DMA Debug status register + __IO uint32_t DMACCR; // ETH_DMACCR DMA Channel control register + __IO uint32_t DMACTCR; // ETH_DMACTXCR Channel Tx transmit control register + __IO uint32_t DMACRCR; // ETH_DMACRXCR Channel Rx receive control register + __IO uint32_t DMACTDLAR; // ETH_DMACTXDLAR Channel Tx descriptor list address register + __IO uint32_t DMACRDLAR; // ETH_DMACRXDLAR Channel Rx descriptor list address register + __IO uint32_t DMACTDTPR; // ETH_DMACTXDTPR Channel TX tail pointer + __IO uint32_t DMACRDTPR; // ETH_DMACRXDTPR Channel RX tail pointer + __IO uint32_t DMACTDRLR; // ETH_DMACTXRLR Channel Tx descriptor ring length register + __IO uint32_t DMACRDRLR; // ETH_DMACRXRLR Channel Rx descriptor ring length register + __IO uint32_t DMACIER; // ETH_DMACIER Channel interrupt enable register + __IO uint32_t DMACRIWTR; // ETH_DMACRXIWTR Channel Rx interrupt watchdog timer register + __IO uint32_t DMACCATDR; // ETH_DMACCATXDR Channel Tx current application transmit descriptor register + __IO uint32_t DMACCARDR; // ETH_DMACCARXDR Channel Rx current application receive descriptor register + __IO uint32_t DMACCATBR; // ETH_DMACCATXBR Channel Tx current application transmit buffer register + __IO uint32_t DMACCARBR; // ETH_DMACCARXBR Channel Rx current application receive buffer register + __IO uint32_t DMACSR; // ETH_DMACSR Channel status register + + +As most EMAC's, the STM32H7 EMAC is able to put packets in multiple linked DMA segments. +FreeRTOS+TCP never uses this feature. Each packet is stored in a single buffer called +`NetworkBufferDescriptor_t`. diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/stm32h7xx_hal_eth.h b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/stm32h7xx_hal_eth.h new file mode 100644 index 0000000..0a0002c --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/stm32h7xx_hal_eth.h @@ -0,0 +1,6 @@ +/* + * The Ethernet header files for STM32F2, STM32F4 and STM32F7 have been merged to + * a single module that works for both parts: "stm32hxx_hal_eth" + */ + +#include "stm32hxx_hal_eth.h" diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/stm32hxx_hal_eth.c b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/stm32hxx_hal_eth.c new file mode 100644 index 0000000..4601686 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/stm32hxx_hal_eth.c @@ -0,0 +1,3033 @@ +/** + ****************************************************************************** + * @file stm32hxx_hal_eth.c + * @author MCD Application Team + * @brief ETH HAL module driver. + * This file provides firmware functions to manage the following + * functionalities of the Ethernet (ETH) peripheral: + * + Initialization and deinitialization functions + * + IO operation functions + * + Peripheral Control functions + * + Peripheral State and Errors functions + * + * @verbatim + * ============================================================================== + ##### How to use this driver ##### + #####============================================================================== + #####[..] + #####The ETH HAL driver can be used as follows: + ##### + #####(#)Declare a ETH_HandleTypeDef handle structure, for example: + ##### ETH_HandleTypeDef heth; + ##### + #####(#)Fill parameters of Init structure in heth handle + ##### + #####(#)Call HAL_ETH_Init() API to initialize the Ethernet peripheral (MAC, DMA, ...) + ##### + #####(#)Initialize the ETH low level resources through the HAL_ETH_MspInit() API: + ##### (##) Enable the Ethernet interface clock using + ##### (+++) __HAL_RCC_ETH1MAC_CLK_ENABLE() + ##### (+++) __HAL_RCC_ETH1TX_CLK_ENABLE() + ##### (+++) __HAL_RCC_ETH1RX_CLK_ENABLE() + ##### + ##### (##) Initialize the related GPIO clocks + ##### (##) Configure Ethernet pinout + ##### (##) Configure Ethernet NVIC interrupt (in Interrupt mode) + ##### + #####(#) Ethernet data reception is asynchronous, so call the following API + ##### to start the listening mode: + ##### (##) HAL_ETH_Start(): + ##### This API starts the MAC and DMA transmission and reception process, + ##### without enabling end of transfer interrupts, in this mode user + ##### has to poll for data availability by calling HAL_ETH_IsRxDataAvailable() + ##### (##) HAL_ETH_Start_IT(): + ##### This API starts the MAC and DMA transmission and reception process, + ##### end of transfer interrupts are enabled in this mode, + ##### HAL_ETH_RxCpltCallback() will be executed when an Ethernet packet is received + ##### + #####(#) When data is received (HAL_ETH_IsRxDataAvailable() returns 1 or Rx interrupt + ##### occurred), user can call the following APIs to get received data: + ##### (##) HAL_ETH_GetRxDataBuffer(): Get buffer address of received frame + ##### (##) HAL_ETH_GetRxDataLength(): Get received frame length + ##### (##) HAL_ETH_GetRxDataInfo(): Get received frame additional info, + ##### please refer to ETH_RxPacketInfo typedef structure + ##### + #####(#) For transmission path, two APIs are available: + ##### (##) HAL_ETH_Transmit(): Transmit an ETH frame in blocking mode + ##### (##) HAL_ETH_Transmit_IT(): Transmit an ETH frame in interrupt mode, + ##### HAL_ETH_TxCpltCallback() will be executed when end of transfer occur + ##### + #####(#) Communication with an external PHY device: + ##### (##) HAL_ETH_ReadPHYRegister(): Read a register from an external PHY + ##### (##) HAL_ETH_WritePHYRegister(): Write data to an external RHY register + ##### + #####(#) Configure the Ethernet MAC after ETH peripheral initialization + ##### (##) HAL_ETH_GetMACConfig(): Get MAC actual configuration into ETH_MACConfigTypeDef + ##### (##) HAL_ETH_SetMACConfig(): Set MAC configuration based on ETH_MACConfigTypeDef + ##### + #####(#) Configure the Ethernet DMA after ETH peripheral initialization + ##### (##) HAL_ETH_GetDMAConfig(): Get DMA actual configuration into ETH_DMAConfigTypeDef + ##### (##) HAL_ETH_SetDMAConfig(): Set DMA configuration based on ETH_DMAConfigTypeDef + ##### + #####-@- The PTP protocol offload APIs are not supported in this driver. + ##### + *** Callback registration *** + ***============================================= + *** + ***The compilation define USE_HAL_ETH_REGISTER_CALLBACKS when set to 1 + ***allows the user to configure dynamically the driver callbacks. + ***Use Function @ref HAL_ETH_RegisterCallback() to register an interrupt callback. + *** + ***Function @ref HAL_ETH_RegisterCallback() allows to register following callbacks: + ***(+) TxCpltCallback : Tx Complete Callback. + ***(+) RxCpltCallback : Rx Complete Callback. + ***(+) DMAErrorCallback : DMA Error Callback. + ***(+) MACErrorCallback : MAC Error Callback. + ***(+) PMTCallback : Power Management Callback + ***(+) EEECallback : EEE Callback. + ***(+) WakeUpCallback : Wake UP Callback + ***(+) MspInitCallback : MspInit Callback. + ***(+) MspDeInitCallback: MspDeInit Callback. + *** + ***This function takes as parameters the HAL peripheral handle, the Callback ID + ***and a pointer to the user callback function. + *** + ***Use function @ref HAL_ETH_UnRegisterCallback() to reset a callback to the default + ***weak function. + ***@ref HAL_ETH_UnRegisterCallback takes as parameters the HAL peripheral handle, + ***and the Callback ID. + ***This function allows to reset following callbacks: + ***(+) TxCpltCallback : Tx Complete Callback. + ***(+) RxCpltCallback : Rx Complete Callback. + ***(+) DMAErrorCallback : DMA Error Callback. + ***(+) MACErrorCallback : MAC Error Callback. + ***(+) PMTCallback : Power Management Callback + ***(+) EEECallback : EEE Callback. + ***(+) WakeUpCallback : Wake UP Callback + ***(+) MspInitCallback : MspInit Callback. + ***(+) MspDeInitCallback: MspDeInit Callback. + *** + ***By default, after the HAL_ETH_Init and when the state is HAL_ETH_STATE_RESET + ***all callbacks are set to the corresponding weak functions: + ***examples @ref HAL_ETH_TxCpltCallback(), @ref HAL_ETH_RxCpltCallback(). + ***Exception done for MspInit and MspDeInit functions that are + ***reset to the legacy weak function in the HAL_ETH_Init/ @ref HAL_ETH_DeInit only when + ***these callbacks are null (not registered beforehand). + ***if not, MspInit or MspDeInit are not null, the HAL_ETH_Init/ @ref HAL_ETH_DeInit + ***keep and use the user MspInit/MspDeInit callbacks (registered beforehand) + *** + ***Callbacks can be registered/unregistered in HAL_ETH_STATE_READY state only. + ***Exception done MspInit/MspDeInit that can be registered/unregistered + ***in HAL_ETH_STATE_READY or HAL_ETH_STATE_RESET state, + ***thus registered (user) MspInit/DeInit callbacks can be used during the Init/DeInit. + ***In that case first register the MspInit/MspDeInit user callbacks + ***using @ref HAL_ETH_RegisterCallback() before calling @ref HAL_ETH_DeInit + ***or HAL_ETH_Init function. + *** + ***When The compilation define USE_HAL_ETH_REGISTER_CALLBACKS is set to 0 or + ***not defined, the callback registration feature is not available and all callbacks + ***are set to the corresponding weak functions. + *** + ***@endverbatim + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2017 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* + * The contents of the files stm32hxx_hal_eth.{c,h} is based on the ETH HAL driver + * stm32h7xx_hal_eth.{c,h} as provided by ST Electronics. + * It is slightly adapted for the needs of FreeRTOS+TCP. + */ + +/* Includes ------------------------------------------------------------------*/ +#include "FreeRTOS.h" +#include "task.h" + +#include "stm32h7xx_hal.h" + +#include "FreeRTOS_IP.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_IP_Private.h" + +#ifndef ARRAY_SIZE + #define ARRAY_SIZE( x ) ( ( BaseType_t ) ( sizeof( x ) / sizeof( ( x )[ 0 ] ) ) ) +#endif + +/* A semaphore that indicates the number of freeTX DMA descriptors. */ +extern SemaphoreHandle_t xTXDescriptorSemaphore; + +/** @addtogroup STM32H7xx_HAL_Driver + * @{ + */ +#ifdef HAL_ETH_MODULE_ENABLED + + static void set_error_state( ETH_HandleTypeDef * heth, + uint32_t ulState ) + { + heth->gState = ulState; + } + + #if defined( ETH ) + +/** @defgroup ETH ETH + * @brief ETH HAL module driver + * @{ + */ + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ + +/** @addtogroup ETH_Private_Constants ETH Private Constants + * @{ + */ + #define ETH_MACCR_MASK ( ( uint32_t ) 0xFFFB7F7CU ) + #define ETH_MACECR_MASK ( ( uint32_t ) 0x3F077FFFU ) + #define ETH_MACPFR_MASK ( ( uint32_t ) 0x800007FFU ) + #define ETH_MACWTR_MASK ( ( uint32_t ) 0x0000010FU ) + #define ETH_MACTFCR_MASK ( ( uint32_t ) 0xFFFF00F2U ) + #define ETH_MACRFCR_MASK ( ( uint32_t ) 0x00000003U ) + #define ETH_MTLTQOMR_MASK ( ( uint32_t ) 0x00000072U ) + #define ETH_MTLRQOMR_MASK ( ( uint32_t ) 0x0000007BU ) + + #define ETH_DMAMR_MASK ( ( uint32_t ) 0x00007802U ) + #define ETH_DMASBMR_MASK ( ( uint32_t ) 0x0000D001U ) + #define ETH_DMACCR_MASK ( ( uint32_t ) 0x00013FFFU ) + #define ETH_DMACTCR_MASK ( ( uint32_t ) 0x003F1010U ) + #define ETH_DMACRCR_MASK ( ( uint32_t ) 0x803F0000U ) + #define ETH_MACPCSR_MASK \ + ( ETH_MACPCSR_PWRDWN | ETH_MACPCSR_RWKPKTEN | \ + ETH_MACPCSR_MGKPKTEN | ETH_MACPCSR_GLBLUCAST | \ + ETH_MACPCSR_RWKPFE ) + +/* Timeout values */ + #define ETH_SWRESET_TIMEOUT ( ( uint32_t ) 500U ) + #define ETH_MDIO_BUS_TIMEOUT ( ( uint32_t ) 1000U ) + + #define ETH_DMARXNDESCWBF_ERRORS_MASK \ + ( ( uint32_t ) ( ETH_DMARXNDESCWBF_DE | ETH_DMARXNDESCWBF_RE | \ + ETH_DMARXNDESCWBF_OE | ETH_DMARXNDESCWBF_RWT | \ + ETH_DMARXNDESCWBF_GP | ETH_DMARXNDESCWBF_CE ) ) + + #define ETH_MAC_US_TICK ( ( uint32_t ) 1000000U ) + +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ + +/** @defgroup ETH_Private_Macros ETH Private Macros + * @{ + */ +/* Helper macros for TX descriptor handling */ + #define INCR_TX_DESC_INDEX( inx, offset ) \ + do { \ + ( inx ) += ( offset ); \ + if( ( inx ) >= ( uint32_t ) ETH_TX_DESC_CNT ) { \ + ( inx ) = ( ( inx ) - ( uint32_t ) ETH_TX_DESC_CNT ); } \ + } while( 0 ) + +/* Helper macros for RX descriptor handling */ + #define INCR_RX_DESC_INDEX( inx, offset ) \ + do { \ + ( inx ) += ( offset ); \ + if( ( inx ) >= ( uint32_t ) ETH_RX_DESC_CNT ) { \ + ( inx ) = ( ( inx ) - ( uint32_t ) ETH_RX_DESC_CNT ); } \ + } while( 0 ) + +/** + * @} + */ +/* Private function prototypes -----------------------------------------------*/ + +/** @defgroup ETH_Private_Functions ETH Private Functions + * @{ + */ + static void ETH_MAC_MDIO_ClkConfig( ETH_HandleTypeDef * heth ); + static void ETH_SetMACConfig( ETH_HandleTypeDef * heth, + ETH_MACConfigTypeDef * macconf ); + static void ETH_SetDMAConfig( ETH_HandleTypeDef * heth, + ETH_DMAConfigTypeDef * dmaconf ); + static void ETH_MACDMAConfig( ETH_HandleTypeDef * heth ); + static void ETH_DMATxDescListInit( ETH_HandleTypeDef * heth ); + static void ETH_DMARxDescListInit( ETH_HandleTypeDef * heth ); + static uint32_t ETH_Prepare_Tx_Descriptors( ETH_HandleTypeDef * heth, + ETH_TxPacketConfig * pTxConfig, + uint32_t ItMode ); + + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + static void ETH_InitCallbacksToDefault( ETH_HandleTypeDef * heth ); + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + +/** + * @} + */ + +/* Exported functions ---------------------------------------------------------*/ + +/** @defgroup ETH_Exported_Functions ETH Exported Functions + * @{ + */ + +/** @defgroup ETH_Exported_Functions_Group1 Initialization and deinitialization functions + * @brief Initialization and Configuration functions + * + * @verbatim + * =============================================================================== + ##### Initialization and Configuration functions ##### + #####=============================================================================== + #####[..] This subsection provides a set of functions allowing to initialize and + ##### deinitialize the ETH peripheral: + ##### + #####(+) User must Implement HAL_ETH_MspInit() function in which he configures + ##### all related peripherals resources (CLOCK, GPIO and NVIC ). + ##### + #####(+) Call the function HAL_ETH_Init() to configure the selected device with + ##### the selected configuration: + ##### (++) MAC address + ##### (++) Media interface (MII or RMII) + ##### (++) Rx DMA Descriptors Tab + ##### (++) Tx DMA Descriptors Tab + ##### (++) Length of Rx Buffers + ##### + #####(+) Call the function HAL_ETH_DescAssignMemory() to assign data buffers + ##### for each Rx DMA Descriptor + ##### + #####(+) Call the function HAL_ETH_DeInit() to restore the default configuration + ##### of the selected ETH peripheral. + ##### + #####@endverbatim + * @{ + */ + +/** + * @brief Initialize the Ethernet peripheral registers. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Init( ETH_HandleTypeDef * heth ) + { + uint32_t tickstart; + + if( heth == NULL ) + { + return HAL_ERROR; + } + + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + if( heth->gState == HAL_ETH_STATE_RESET ) + { + /* Allocate lock resource and initialize it */ + heth->Lock = HAL_UNLOCKED; + + ETH_InitCallbacksToDefault( heth ); + + if( heth->MspInitCallback == NULL ) + { + heth->MspInitCallback = HAL_ETH_MspInit; + } + + /* Init the low level hardware */ + heth->MspInitCallback( heth ); + } + #else /* if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) */ + /* Check the ETH peripheral state */ + if( heth->gState == HAL_ETH_STATE_RESET ) + { + /* Init the low level hardware : GPIO, CLOCK, NVIC. */ + HAL_ETH_MspInit( heth ); + } + #endif /* (USE_HAL_ETH_REGISTER_CALLBACKS) */ + + heth->gState = HAL_ETH_STATE_BUSY; + + __HAL_RCC_SYSCFG_CLK_ENABLE(); + + if( heth->Init.MediaInterface == HAL_ETH_MII_MODE ) + { + HAL_SYSCFG_ETHInterfaceSelect( SYSCFG_ETH_MII ); + } + else + { + HAL_SYSCFG_ETHInterfaceSelect( SYSCFG_ETH_RMII ); + } + + /* Ethernet Software reset */ + /* Set the SWR bit: resets all MAC subsystem internal registers and logic */ + /* After reset all the registers holds their respective reset values */ + SET_BIT( heth->Instance->DMAMR, ETH_DMAMR_SWR ); + + /* Get tick */ + tickstart = HAL_GetTick(); + + /* Wait for software reset */ + while( READ_BIT( heth->Instance->DMAMR, ETH_DMAMR_SWR ) > 0U ) + { + if( ( ( HAL_GetTick() - tickstart ) > ETH_SWRESET_TIMEOUT ) ) + { + /* Set Error Code */ + heth->ErrorCode = HAL_ETH_ERROR_TIMEOUT; + /* Set State as Error */ + set_error_state( heth, HAL_ETH_STATE_ERROR ); + /* Return Error */ + return HAL_ERROR; + } + } + + /*------------------ MDIO CSR Clock Range Configuration --------------------*/ + ETH_MAC_MDIO_ClkConfig( heth ); + + /*------------------ MAC LPI 1US Tic Counter Configuration --------------------*/ + WRITE_REG( heth->Instance->MAC1USTCR, ( ( ( uint32_t ) HAL_RCC_GetHCLKFreq() / ETH_MAC_US_TICK ) - 1U ) ); + + /*------------------ MAC, MTL and DMA default Configuration ----------------*/ + ETH_MACDMAConfig( heth ); + + /* SET DSL to 64 bit */ + MODIFY_REG( heth->Instance->DMACCR, ETH_DMACCR_DSL, ETH_DMACCR_DSL_64BIT ); + + /* Set Receive Buffers Length (must be a multiple of 4) */ + if( ( heth->Init.RxBuffLen % 0x4U ) != 0x0U ) + { + /* Set Error Code */ + heth->ErrorCode = HAL_ETH_ERROR_PARAM; + /* Set State as Error */ + set_error_state( heth, HAL_ETH_STATE_ERROR ); + /* Return Error */ + return HAL_ERROR; + } + else + { + MODIFY_REG( heth->Instance->DMACRCR, ETH_DMACRCR_RBSZ, ( ( heth->Init.RxBuffLen ) << 1 ) ); + } + + /*------------------ DMA Tx Descriptors Configuration ----------------------*/ + ETH_DMATxDescListInit( heth ); + + /*------------------ DMA Rx Descriptors Configuration ----------------------*/ + ETH_DMARxDescListInit( heth ); + + /*--------------------- ETHERNET MAC Address Configuration ------------------*/ + /* Set MAC addr bits 32 to 47 */ + heth->Instance->MACA0HR = ( ( ( uint32_t ) ( heth->Init.MACAddr[ 5 ] ) << 8 ) | ( uint32_t ) heth->Init.MACAddr[ 4 ] ); + /* Set MAC addr bits 0 to 31 */ + heth->Instance->MACA0LR = ( ( ( uint32_t ) ( heth->Init.MACAddr[ 3 ] ) << 24 ) | ( ( uint32_t ) ( heth->Init.MACAddr[ 2 ] ) << 16 ) | + ( ( uint32_t ) ( heth->Init.MACAddr[ 1 ] ) << 8 ) | ( uint32_t ) heth->Init.MACAddr[ 0 ] ); + + heth->ErrorCode = HAL_ETH_ERROR_NONE; + heth->gState = HAL_ETH_STATE_READY; + heth->RxState = HAL_ETH_STATE_READY; + + /* + * Disable the interrupts that are related to the MMC counters. + * These interrupts are enabled by default. The interrupt can + * only be acknowledged by reading the corresponding counter. + */ + + heth->Instance->MMCRIMR = + ETH_MMCRIMR_RXLPITRCIM | /* RXLPITRC */ + ETH_MMCRIMR_RXLPIUSCIM | /* RXLPIUSC */ + ETH_MMCRIMR_RXUCGPIM | /* RXUCASTG */ + ETH_MMCRIMR_RXALGNERPIM | /* RXALGNERR */ + ETH_MMCRIMR_RXCRCERPIM; /* RXCRCERR */ + + heth->Instance->MMCTIMR = + ETH_MMCTIMR_TXLPITRCIM | /* TXLPITRC */ + ETH_MMCTIMR_TXLPIUSCIM | /* TXLPIUSC */ + ETH_MMCTIMR_TXGPKTIM | /* TXPKTG */ + ETH_MMCTIMR_TXMCOLGPIM | /* TXMULTCOLG */ + ETH_MMCTIMR_TXSCOLGPIM; /* TXSNGLCOLG */ + + return HAL_OK; + } + +/*/ ** */ +/** @brief DeInitializes the ETH peripheral. */ +/** @param heth: pointer to a ETH_HandleTypeDef structure that contains */ +/** the configuration information for ETHERNET module */ +/** @retval HAL status */ +/** / */ +/*HAL_StatusTypeDef HAL_ETH_DeInit(ETH_HandleTypeDef *heth) */ +/*{ */ +/*/ * Set the ETH peripheral state to BUSY * / */ +/*heth->gState = HAL_ETH_STATE_BUSY; */ +/* */ +/*#if (USE_HAL_ETH_REGISTER_CALLBACKS == 1) */ +/* */ +/*if(heth->MspDeInitCallback == NULL) */ +/*{ */ +/*heth->MspDeInitCallback = HAL_ETH_MspDeInit; */ +/*} */ +/*/ * DeInit the low level hardware * / */ +/*heth->MspDeInitCallback(heth); */ +/*#else */ +/* */ +/*/ * De-Init the low level hardware : GPIO, CLOCK, NVIC. * / */ +/*HAL_ETH_MspDeInit(heth); */ +/* */ +/*#endif / * (USE_HAL_ETH_REGISTER_CALLBACKS) * / */ +/* */ +/*/ * Set ETH HAL state to Disabled * / */ +/*heth->gState= HAL_ETH_STATE_RESET; */ +/* */ +/*/ * Return function status * / */ +/*return HAL_OK; */ +/*} */ + +/** + * @brief Initializes the ETH MSP. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_MspInit( ETH_HandleTypeDef * heth ) + { + /* Prevent unused argument(s) compilation warning */ + UNUSED( heth ); + + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_MspInit could be implemented in the user file + */ + } + +/** + * @brief DeInitializes ETH MSP. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_MspDeInit( ETH_HandleTypeDef * heth ) + { + /* Prevent unused argument(s) compilation warning */ + UNUSED( heth ); + + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_MspDeInit could be implemented in the user file + */ + } + + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + +/** + * @brief Register a User ETH Callback + * To be used instead of the weak predefined callback + * @param heth eth handle + * @param CallbackID ID of the callback to be registered + * This parameter can be one of the following values: + * @arg @ref HAL_ETH_TX_COMPLETE_CB_ID Tx Complete Callback ID + * @arg @ref HAL_ETH_RX_COMPLETE_CB_ID Rx Complete Callback ID + * @arg @ref HAL_ETH_DMA_ERROR_CB_ID DMA Error Callback ID + * @arg @ref HAL_ETH_MAC_ERROR_CB_ID MAC Error Callback ID + * @arg @ref HAL_ETH_PMT_CB_ID Power Management Callback ID + * @arg @ref HAL_ETH_EEE_CB_ID EEE Callback ID + * @arg @ref HAL_ETH_WAKEUP_CB_ID Wake UP Callback ID + * @arg @ref HAL_ETH_MSPINIT_CB_ID MspInit callback ID + * @arg @ref HAL_ETH_MSPDEINIT_CB_ID MspDeInit callback ID + * @param pCallback pointer to the Callback function + * @retval status + */ + HAL_StatusTypeDef HAL_ETH_RegisterCallback( ETH_HandleTypeDef * heth, + HAL_ETH_CallbackIDTypeDef CallbackID, + pETH_CallbackTypeDef pCallback ) + { + HAL_StatusTypeDef status = HAL_OK; + + if( pCallback == NULL ) + { + /* Update the error code */ + heth->ErrorCode |= HAL_ETH_ERROR_INVALID_CALLBACK; + + return HAL_ERROR; + } + + /* Process locked */ + __HAL_LOCK( heth ); + + if( heth->gState == HAL_ETH_STATE_READY ) + { + switch( CallbackID ) + { + case HAL_ETH_TX_COMPLETE_CB_ID: + heth->TxCpltCallback = pCallback; + break; + + case HAL_ETH_RX_COMPLETE_CB_ID: + heth->RxCpltCallback = pCallback; + break; + + case HAL_ETH_DMA_ERROR_CB_ID: + heth->DMAErrorCallback = pCallback; + break; + + case HAL_ETH_MAC_ERROR_CB_ID: + heth->MACErrorCallback = pCallback; + break; + + case HAL_ETH_PMT_CB_ID: + heth->PMTCallback = pCallback; + break; + + case HAL_ETH_EEE_CB_ID: + heth->EEECallback = pCallback; + break; + + case HAL_ETH_WAKEUP_CB_ID: + heth->WakeUpCallback = pCallback; + break; + + case HAL_ETH_MSPINIT_CB_ID: + heth->MspInitCallback = pCallback; + break; + + case HAL_ETH_MSPDEINIT_CB_ID: + heth->MspDeInitCallback = pCallback; + break; + + default: + /* Update the error code */ + heth->ErrorCode |= HAL_ETH_ERROR_INVALID_CALLBACK; + /* Return error status */ + status = HAL_ERROR; + break; + } + } + else if( heth->gState == HAL_ETH_STATE_RESET ) + { + switch( CallbackID ) + { + case HAL_ETH_MSPINIT_CB_ID: + heth->MspInitCallback = pCallback; + break; + + case HAL_ETH_MSPDEINIT_CB_ID: + heth->MspDeInitCallback = pCallback; + break; + + default: + /* Update the error code */ + heth->ErrorCode |= HAL_ETH_ERROR_INVALID_CALLBACK; + /* Return error status */ + status = HAL_ERROR; + break; + } + } + else + { + /* Update the error code */ + heth->ErrorCode |= HAL_ETH_ERROR_INVALID_CALLBACK; + /* Return error status */ + status = HAL_ERROR; + } + + /* Release Lock */ + __HAL_UNLOCK( heth ); + + return status; + } + +/** + * @brief Unregister an ETH Callback + * ETH callabck is redirected to the weak predefined callback + * @param heth eth handle + * @param CallbackID ID of the callback to be unregistered + * This parameter can be one of the following values: + * @arg @ref HAL_ETH_TX_COMPLETE_CB_ID Tx Complete Callback ID + * @arg @ref HAL_ETH_RX_COMPLETE_CB_ID Rx Complete Callback ID + * @arg @ref HAL_ETH_DMA_ERROR_CB_ID DMA Error Callback ID + * @arg @ref HAL_ETH_MAC_ERROR_CB_ID MAC Error Callback ID + * @arg @ref HAL_ETH_PMT_CB_ID Power Management Callback ID + * @arg @ref HAL_ETH_EEE_CB_ID EEE Callback ID + * @arg @ref HAL_ETH_WAKEUP_CB_ID Wake UP Callback ID + * @arg @ref HAL_ETH_MSPINIT_CB_ID MspInit callback ID + * @arg @ref HAL_ETH_MSPDEINIT_CB_ID MspDeInit callback ID + * @retval status + */ + HAL_StatusTypeDef HAL_ETH_UnRegisterCallback( ETH_HandleTypeDef * heth, + HAL_ETH_CallbackIDTypeDef CallbackID ) + { + HAL_StatusTypeDef status = HAL_OK; + + /* Process locked */ + __HAL_LOCK( heth ); + + if( heth->gState == HAL_ETH_STATE_READY ) + { + switch( CallbackID ) + { + case HAL_ETH_TX_COMPLETE_CB_ID: + heth->TxCpltCallback = HAL_ETH_TxCpltCallback; + break; + + case HAL_ETH_RX_COMPLETE_CB_ID: + heth->RxCpltCallback = HAL_ETH_RxCpltCallback; + break; + + case HAL_ETH_DMA_ERROR_CB_ID: + heth->DMAErrorCallback = HAL_ETH_DMAErrorCallback; + break; + + case HAL_ETH_MAC_ERROR_CB_ID: + heth->MACErrorCallback = HAL_ETH_MACErrorCallback; + break; + + case HAL_ETH_PMT_CB_ID: + heth->PMTCallback = HAL_ETH_PMTCallback; + break; + + case HAL_ETH_EEE_CB_ID: + heth->EEECallback = HAL_ETH_EEECallback; + break; + + case HAL_ETH_WAKEUP_CB_ID: + heth->WakeUpCallback = HAL_ETH_WakeUpCallback; + break; + + case HAL_ETH_MSPINIT_CB_ID: + heth->MspInitCallback = HAL_ETH_MspInit; + break; + + case HAL_ETH_MSPDEINIT_CB_ID: + heth->MspDeInitCallback = HAL_ETH_MspDeInit; + break; + + default: + /* Update the error code */ + heth->ErrorCode |= HAL_ETH_ERROR_INVALID_CALLBACK; + /* Return error status */ + status = HAL_ERROR; + break; + } + } + else if( heth->gState == HAL_ETH_STATE_RESET ) + { + switch( CallbackID ) + { + case HAL_ETH_MSPINIT_CB_ID: + heth->MspInitCallback = HAL_ETH_MspInit; + break; + + case HAL_ETH_MSPDEINIT_CB_ID: + heth->MspDeInitCallback = HAL_ETH_MspDeInit; + break; + + default: + /* Update the error code */ + heth->ErrorCode |= HAL_ETH_ERROR_INVALID_CALLBACK; + /* Return error status */ + status = HAL_ERROR; + break; + } + } + else + { + /* Update the error code */ + heth->ErrorCode |= HAL_ETH_ERROR_INVALID_CALLBACK; + /* Return error status */ + status = HAL_ERROR; + } + + /* Release Lock */ + __HAL_UNLOCK( heth ); + + return status; + } + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + +/** + * @brief Assign memory buffers to a DMA Rx descriptor + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param Index : index of the DMA Rx descriptor + * this parameter can be a value from 0x0 to (ETH_RX_DESC_CNT -1) + * @param pBuffer1: address of buffer 1 + * @param pBuffer2: address of buffer 2 if available + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_DescAssignMemory( ETH_HandleTypeDef * heth, + uint32_t Index, + uint8_t * pBuffer1, + uint8_t * pBuffer2 ) + { + ETH_DMADescTypeDef * dmarxdesc = ( ETH_DMADescTypeDef * ) heth->RxDescList.RxDesc[ Index ]; + + if( ( pBuffer1 == NULL ) || ( Index >= ( uint32_t ) ETH_RX_DESC_CNT ) ) + { + /* Set Error Code */ + heth->ErrorCode = HAL_ETH_ERROR_PARAM; + /* Return Error */ + return HAL_ERROR; + } + + /* write buffer address to RDES0 */ + WRITE_REG( dmarxdesc->DESC0, ( uint32_t ) pBuffer1 ); + /* store buffer address */ + WRITE_REG( dmarxdesc->BackupAddr0, ( uint32_t ) pBuffer1 ); + /* set buffer address valid bit to RDES3 */ + SET_BIT( dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF1V ); + +/* Not used for FreeRTOS+TCP */ +/* if(pBuffer2 != NULL) */ +/* { */ +/* / * write buffer 2 address to RDES1 * / */ +/* WRITE_REG(dmarxdesc->DESC2, (uint32_t)pBuffer2); */ +/* / * store buffer 2 address * / */ +/* WRITE_REG(dmarxdesc->BackupAddr1, (uint32_t)pBuffer2); */ +/* / * set buffer 2 address valid bit to RDES3 * / */ +/* SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF2V); */ +/* } */ + + /* set OWN bit to RDES3 */ + SET_BIT( dmarxdesc->DESC3, ETH_DMARXNDESCRF_OWN ); + ( void ) dmarxdesc->DESC3; + + return HAL_OK; + } + +/** + * @} + */ + +/** @defgroup ETH_Exported_Functions_Group2 IO operation functions + * @brief ETH Transmit and Receive functions + * + * @verbatim + * ============================================================================== + ##### IO operation functions ##### + #####============================================================================== + #####[..] + #####This subsection provides a set of functions allowing to manage the ETH + #####data transfer. + ##### + #####@endverbatim + * @{ + */ + +/** + * @brief Enables Ethernet MAC and DMA reception and transmission + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Start( ETH_HandleTypeDef * heth ) + { + if( heth->gState == HAL_ETH_STATE_READY ) + { + heth->gState = HAL_ETH_STATE_BUSY; + + /* Enable the MAC transmission */ + SET_BIT( heth->Instance->MACCR, ETH_MACCR_TE ); + + /* Enable the MAC reception */ + SET_BIT( heth->Instance->MACCR, ETH_MACCR_RE ); + + /* Set the Flush Transmit FIFO bit */ + SET_BIT( heth->Instance->MTLTQOMR, ETH_MTLTQOMR_FTQ ); + + /* Enable the DMA transmission */ + SET_BIT( heth->Instance->DMACTCR, ETH_DMACTCR_ST ); + + /* Enable the DMA reception */ + SET_BIT( heth->Instance->DMACRCR, ETH_DMACRCR_SR ); + + /* Clear Tx and Rx process stopped flags */ + heth->Instance->DMACSR |= ( ETH_DMACSR_TPS | ETH_DMACSR_RPS ); + + heth->gState = HAL_ETH_STATE_READY; + heth->RxState = HAL_ETH_STATE_BUSY_RX; + + return HAL_OK; + } + else + { + return HAL_ERROR; + } + } + +/** + * @brief Enables Ethernet MAC and DMA reception/transmission in Interrupt mode + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Start_IT( ETH_HandleTypeDef * heth ) + { + uint32_t desc_index; + + if( heth->gState == HAL_ETH_STATE_READY ) + { + heth->gState = HAL_ETH_STATE_BUSY; + + /* Set IOC bit (Interrupt Enabled on Completion) to all Rx descriptors */ + for( desc_index = 0; desc_index < ( uint32_t ) ETH_RX_DESC_CNT; desc_index++ ) + { + ETH_DMADescTypeDef * dma_rx_desc; + + dma_rx_desc = ( ETH_DMADescTypeDef * ) heth->RxDescList.RxDesc[ desc_index ]; + SET_BIT( dma_rx_desc->DESC3, ETH_DMARXNDESCRF_IOC ); + } + + /* save IT mode to ETH Handle */ + heth->RxDescList.ItMode = 1U; + + /* Enable the MAC transmission */ + SET_BIT( heth->Instance->MACCR, ETH_MACCR_TE ); + + /* Enable the MAC reception */ + SET_BIT( heth->Instance->MACCR, ETH_MACCR_RE ); + + /* Set the Flush Transmit FIFO bit */ + SET_BIT( heth->Instance->MTLTQOMR, ETH_MTLTQOMR_FTQ ); + + /* Enable the DMA transmission */ + SET_BIT( heth->Instance->DMACTCR, ETH_DMACTCR_ST ); + + /* Enable the DMA reception */ + SET_BIT( heth->Instance->DMACRCR, ETH_DMACRCR_SR ); + + /* Clear Tx and Rx process stopped flags */ + heth->Instance->DMACSR |= ( ETH_DMACSR_TPS | ETH_DMACSR_RPS ); + + /* Enable ETH DMA interrupts: + * - Tx complete interrupt + * - Rx complete interrupt + * - Fatal bus interrupt + */ + __HAL_ETH_DMA_ENABLE_IT( heth, + ETH_DMACIER_NIE | /* Normal Interrupt Summary Enable */ + ETH_DMACIER_RIE | /* Receive Interrupt Enable */ + ETH_DMACIER_TIE | /* Transmit Interrupt Enable */ + ETH_DMACIER_FBEE | /* Fatal Bus Error Enable */ + ETH_DMACIER_AIE ); /* Abnormal Interrupt Summary Enable */ + + heth->gState = HAL_ETH_STATE_READY; + heth->RxState = HAL_ETH_STATE_BUSY_RX; + + return HAL_OK; + } + else + { + return HAL_ERROR; + } + } + +/** + * @brief Stop Ethernet MAC and DMA reception/transmission + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Stop( ETH_HandleTypeDef * heth ) + { + if( heth->gState != HAL_ETH_STATE_RESET ) + { + /* Set the ETH peripheral state to BUSY */ + heth->gState = HAL_ETH_STATE_BUSY; + + /* Disable the DMA transmission */ + CLEAR_BIT( heth->Instance->DMACTCR, ETH_DMACTCR_ST ); + + /* Disable the DMA reception */ + CLEAR_BIT( heth->Instance->DMACRCR, ETH_DMACRCR_SR ); + + /* Disable the MAC reception */ + CLEAR_BIT( heth->Instance->MACCR, ETH_MACCR_RE ); + + /* Set the Flush Transmit FIFO bit */ + SET_BIT( heth->Instance->MTLTQOMR, ETH_MTLTQOMR_FTQ ); + + /* Disable the MAC transmission */ + CLEAR_BIT( heth->Instance->MACCR, ETH_MACCR_TE ); + + heth->gState = HAL_ETH_STATE_READY; + heth->RxState = HAL_ETH_STATE_READY; + + /* Return function status */ + return HAL_OK; + } + else + { + return HAL_ERROR; + } + } + +/** + * @brief Stop Ethernet MAC and DMA reception/transmission in Interrupt mode + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Stop_IT( ETH_HandleTypeDef * heth ) + { + ETH_DMADescTypeDef * dmarxdesc; + uint32_t descindex; + + if( heth->gState != HAL_ETH_STATE_RESET ) + { + /* Set the ETH peripheral state to BUSY */ + heth->gState = HAL_ETH_STATE_BUSY; + + /* Disable intrrupts: + * - Tx complete interrupt + * - Rx complete interrupt + * - Fatal bus interrupt + */ + __HAL_ETH_DMA_DISABLE_IT( heth, ( ETH_DMACIER_NIE | ETH_DMACIER_RIE | ETH_DMACIER_TIE | + ETH_DMACIER_FBEE | ETH_DMACIER_AIE ) ); + + /* Disable the DMA transmission */ + CLEAR_BIT( heth->Instance->DMACTCR, ETH_DMACTCR_ST ); + + /* Disable the DMA reception */ + CLEAR_BIT( heth->Instance->DMACRCR, ETH_DMACRCR_SR ); + + /* Disable the MAC reception */ + CLEAR_BIT( heth->Instance->MACCR, ETH_MACCR_RE ); + + /* Set the Flush Transmit FIFO bit */ + SET_BIT( heth->Instance->MTLTQOMR, ETH_MTLTQOMR_FTQ ); + + /* Disable the MAC transmission */ + CLEAR_BIT( heth->Instance->MACCR, ETH_MACCR_TE ); + + /* Clear IOC bit (Interrupt Enabled on Completion) to all Rx descriptors */ + for( descindex = 0; descindex < ( uint32_t ) ETH_RX_DESC_CNT; descindex++ ) + { + dmarxdesc = ( ETH_DMADescTypeDef * ) heth->RxDescList.RxDesc[ descindex ]; + CLEAR_BIT( dmarxdesc->DESC3, ETH_DMARXNDESCRF_IOC ); + } + + heth->RxDescList.ItMode = 0U; + + heth->gState = HAL_ETH_STATE_READY; + heth->RxState = HAL_ETH_STATE_READY; + + /* Return function status */ + return HAL_OK; + } + else + { + return HAL_ERROR; + } + } + +/** + * @brief Sends an Ethernet Packet in polling mode. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param pTxConfig: Hold the configuration of packet to be transmitted + * @param Timeout: timeout value + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Transmit( ETH_HandleTypeDef * heth, + ETH_TxPacketConfig * pTxConfig, + uint32_t Timeout ) + { + uint32_t tickstart; + const ETH_DMADescTypeDef * dmatxdesc; + + if( pTxConfig == NULL ) + { + heth->ErrorCode |= HAL_ETH_ERROR_PARAM; + return HAL_ERROR; + } + + if( heth->gState == HAL_ETH_STATE_READY ) + { + /* Config DMA Tx descriptor by Tx Packet info */ + if( ETH_Prepare_Tx_Descriptors( heth, pTxConfig, 0 ) != HAL_ETH_ERROR_NONE ) + { + /* Set the ETH error code */ + heth->ErrorCode |= HAL_ETH_ERROR_BUSY; + return HAL_ERROR; + } + + dmatxdesc = ( ETH_DMADescTypeDef * ) ( &heth->TxDescList )->TxDesc[ heth->TxDescList.CurTxDesc ]; + + /* Incr current tx desc index */ + INCR_TX_DESC_INDEX( heth->TxDescList.CurTxDesc, 1U ); + + /* Start transmission */ + /* issue a poll command to Tx DMA by writing address of next immediate free descriptor */ + WRITE_REG( heth->Instance->DMACTDTPR, ( uint32_t ) ( heth->TxDescList.TxDesc[ heth->TxDescList.CurTxDesc ] ) ); + + READ_REG( heth->Instance->DMACTDTPR ); + + tickstart = HAL_GetTick(); + + /* Wait for data to be transmitted or timeout occured */ + while( ( dmatxdesc->DESC3 & ETH_DMATXNDESCWBF_OWN ) != ( uint32_t ) RESET ) + { + if( ( heth->Instance->DMACSR & ETH_DMACSR_FBE ) != ( uint32_t ) RESET ) + { + heth->ErrorCode |= HAL_ETH_ERROR_DMA; + heth->DMAErrorCode = heth->Instance->DMACSR; + /* Set ETH HAL State to Ready */ + set_error_state( heth, HAL_ETH_STATE_ERROR ); + /* Return function status */ + return HAL_ERROR; + } + + /* Check for the Timeout */ + if( Timeout != HAL_MAX_DELAY ) + { + if( ( ( HAL_GetTick() - tickstart ) > Timeout ) || ( Timeout == 0U ) ) + { + heth->ErrorCode |= HAL_ETH_ERROR_TIMEOUT; + set_error_state( heth, HAL_ETH_STATE_ERROR ); + return HAL_ERROR; + } + } + } + + /* Return function status */ + return HAL_OK; + } + else + { + return HAL_ERROR; + } + } + +/** + * @brief Sends an Ethernet Packet in interrupt mode. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param pTxConfig: Hold the configuration of packet to be transmitted + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_Transmit_IT( ETH_HandleTypeDef * heth, + ETH_TxPacketConfig * pTxConfig ) + { + if( pTxConfig == NULL ) + { + heth->ErrorCode |= HAL_ETH_ERROR_PARAM; + return HAL_ERROR; + } + + if( heth->gState == HAL_ETH_STATE_READY ) + { + /* Config DMA Tx descriptor by Tx Packet info */ + if( ETH_Prepare_Tx_Descriptors( heth, pTxConfig, 1 ) != HAL_ETH_ERROR_NONE ) + { + heth->ErrorCode |= HAL_ETH_ERROR_BUSY; + return HAL_ERROR; + } + + /* Incr current tx desc index */ + INCR_TX_DESC_INDEX( heth->TxDescList.CurTxDesc, 1U ); + + /* + * Start transmission. + * issue a poll command to Tx DMA by writing address of next immediate free descriptor. + * DMACTDTPR: "Channel Tx descriptor tail pointer register (ETH_DMACTXDTPR) + * The hardware tries to transmit all packets referenced by the + * descriptors between the head and the tail pointer registers. + */ + + WRITE_REG( heth->Instance->DMACTDTPR, ( uint32_t ) ( heth->TxDescList.TxDesc[ heth->TxDescList.CurTxDesc ] ) ); + /* Memory barrier. */ + __DSB(); + /* Read-back the value just written. */ + ( void ) heth->Instance->DMACTDTPR; + + return HAL_OK; + } + else + { + return HAL_ERROR; + } + } + +/** + * @brief Checks for received Packets. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval 1: A Packet is received + * 0: no Packet received + */ + uint8_t HAL_ETH_IsRxDataAvailable( ETH_HandleTypeDef * heth ) + { + ETH_RxDescListTypeDef * dmarxdesclist = &heth->RxDescList; + uint32_t desc_index = dmarxdesclist->CurRxDesc; + ETH_DMADescTypeDef * dmarxdesc = ( ETH_DMADescTypeDef * ) dmarxdesclist->RxDesc[ desc_index ]; + uint32_t desc_scan_count = 0; + uint32_t app_desc_count = 0; /* Number of descriptors in received packet. */ + uint32_t first_app_desc_index = 0; /* Index of the first descriptor of received packet.. */ + + /* Check if descriptor is not owned by DMA */ + while( ( READ_BIT( dmarxdesc->DESC3, ETH_DMARXNDESCWBF_OWN ) == ( uint32_t ) RESET ) && + ( desc_scan_count < ( uint32_t ) ETH_RX_DESC_CNT ) ) + { + uint32_t ulDesc3 = dmarxdesc->DESC3; + desc_scan_count++; + + /* FreeRTOS+TCP only handles packets that fit in 1 descriptor. */ + configASSERT( ( ( ulDesc3 & ETH_DMATXNDESCWBF_FD ) != 0U ) && ( ( ulDesc3 & ETH_DMATXNDESCWBF_LD ) != 0U ) ); + + /* Check if last descriptor */ + if( READ_BIT( ulDesc3, ETH_DMARXNDESCWBF_LD ) != ( uint32_t ) RESET ) + { + /* Increment the number of descriptors to be passed to the application */ + app_desc_count += 1U; + + if( app_desc_count == 1U ) + { + first_app_desc_index = desc_index; + } + + /* Increment current rx descriptor index */ + INCR_RX_DESC_INDEX( desc_index, 1U ); + + /* Check for Context descriptor */ + /* Get current descriptor address */ + dmarxdesc = ( ETH_DMADescTypeDef * ) dmarxdesclist->RxDesc[ desc_index ]; + + if( READ_BIT( dmarxdesc->DESC3, ETH_DMARXNDESCWBF_OWN ) == ( uint32_t ) RESET ) + { + /* If IEEE 1588 timestamp feature is enabled, the DMA stores the timestamp + * (if available). The DMA writes the context descriptor after the last + * descriptor for the current packet (in the next available descriptor). */ + if( READ_BIT( dmarxdesc->DESC3, ETH_DMARXNDESCWBF_CTXT ) != ( uint32_t ) RESET ) + { + /* Increment the number of descriptors to be passed to the application */ + dmarxdesclist->AppContextDesc = 1; + /* Increment current rx descriptor index */ + INCR_RX_DESC_INDEX( desc_index, 1U ); + } + } + + /* Fill information to Rx descriptors list */ + dmarxdesclist->CurRxDesc = desc_index; + dmarxdesclist->FirstAppDesc = first_app_desc_index; + dmarxdesclist->AppDescNbr = app_desc_count; + + /* Return function status */ + return 1; + } + +/* / * Check if first descriptor * / */ +/* else if(READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_FD) != (uint32_t)RESET) */ +/* { */ +/*configASSERT( 1 == 0 ); */ +/* first_app_desc_index = desc_index; */ +/* / * Increment the number of descriptors to be passed to the application * / */ +/* app_desc_count = 1U; */ +/* */ +/* / * Increment current rx descriptor index * / */ +/* INCR_RX_DESC_INDEX(desc_index, 1U); */ +/* / * Get current descriptor address * / */ +/* dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[desc_index]; */ +/* } */ +/* / * It should be an intermediate descriptor * / */ +/* else */ +/* { */ +/*configASSERT( 1 == 0 ); */ +/* / * Increment the number of descriptors to be passed to the application * / */ +/* app_desc_count += 1U; */ +/* */ +/* / * Increment current rx descriptor index * / */ +/* INCR_RX_DESC_INDEX(desc_index, 1U); */ +/* / * Get current descriptor address * / */ +/* dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[desc_index]; */ +/* } */ + } /* while ( OWN == 0 ) */ + + configASSERT( app_desc_count == 0 ); + +/* / * Build Descriptors if an incomplete Packet is received * / */ +/* if(app_desc_count > 0U) */ +/* { */ +/* dmarxdesclist->CurRxDesc = desc_index; */ +/* dmarxdesclist->FirstAppDesc = first_app_desc_index; */ +/* desc_index = first_app_desc_index; */ +/* dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[desc_index]; */ +/* */ +/* for(desc_scan_count = 0; ipTRUE_BOOL; desc_scan_count++) */ +/* { */ +/* WRITE_REG(dmarxdesc->DESC0, dmarxdesc->BackupAddr0); */ +/* WRITE_REG(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF1V); */ +/* */ +/* if (READ_REG(dmarxdesc->BackupAddr1) != ((uint32_t)RESET)) */ +/* { */ +/* WRITE_REG(dmarxdesc->DESC2, dmarxdesc->BackupAddr1); */ +/* SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF2V); */ +/* } */ +/* */ +/* SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_OWN); */ +/* */ +/* if(dmarxdesclist->ItMode != ((uint32_t)RESET)) */ +/* { */ +/* SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_IOC); */ +/* } */ +/* if(desc_scan_count >= (app_desc_count - 1U)) */ +/* { */ +/* break; */ +/* } */ +/* / * Increment rx descriptor index * / */ +/* INCR_RX_DESC_INDEX(desc_index, 1U); */ +/* / * Get descriptor address * / */ +/* dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[desc_index]; */ +/* } */ +/* */ +/* / * Set the Tail pointer address to the last rx descriptor hold by the app * / */ +/* WRITE_REG(heth->Instance->DMACRDTPR, (uint32_t)dmarxdesc); */ +/* } */ + + /* Fill information to Rx descriptors list: No received Packet */ + dmarxdesclist->AppDescNbr = 0U; + + return 0; + } + +/** + * @brief This function gets the buffer address of last received Packet. + * @note Please insure to allocate the RxBuffer structure before calling this function + * how to use example: + * HAL_ETH_GetRxDataLength(heth, &Length); + * BuffersNbr = (Length / heth->Init.RxBuffLen) + 1; + * RxBuffer = (ETH_BufferTypeDef *)malloc(BuffersNbr * sizeof(ETH_BufferTypeDef)); + * HAL_ETH_GetRxDataBuffer(heth, RxBuffer); + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param RxBuffer: Pointer to a ETH_BufferTypeDef structure + * @retval HAL status + */ + size_t HAL_ETH_GetRxData( ETH_HandleTypeDef * heth, + ETH_BufferTypeDef * RxBuffer ) + { + ETH_RxDescListTypeDef * dmarxdesclist = &( heth->RxDescList ); + uint32_t desc_index; + uint32_t packet_length; + __IO const ETH_DMADescTypeDef * dma_rx_desc; + + configASSERT( RxBuffer != NULL ); + + if( HAL_ETH_IsRxDataAvailable( heth ) == 0U ) + { + /* No data to be transferred to the application */ + return 0U; + } + + desc_index = dmarxdesclist->FirstAppDesc; + dma_rx_desc = ( ETH_DMADescTypeDef * ) dmarxdesclist->RxDesc[ desc_index ]; + + configASSERT( dmarxdesclist->AppDescNbr == 1 ); + + /* last descriptor data length */ + packet_length = READ_BIT( dma_rx_desc->DESC3, ETH_DMARXNDESCWBF_PL ); + + RxBuffer->buffer = ( uint8_t * ) dma_rx_desc->BackupAddr0; + RxBuffer->len = packet_length; + + /* data is in only one buffer */ + configASSERT( packet_length <= heth->Init.RxBuffLen ); + + return packet_length; + } + +/*/ ** */ +/* * @brief This function gets the length of last received Packet. */ +/* * @param heth: pointer to a ETH_HandleTypeDef structure that contains */ +/* * the configuration information for ETHERNET module */ +/* * @param Length: parameter to hold Rx packet length */ +/* * @retval HAL Status */ +/* * / */ +/*HAL_StatusTypeDef HAL_ETH_GetRxDataLength(ETH_HandleTypeDef *heth, uint32_t *Length) */ +/*{ */ +/* ETH_RxDescListTypeDef *dmarxdesclist = &heth->RxDescList; */ +/* uint32_t descidx = dmarxdesclist->FirstAppDesc; */ +/* __IO const ETH_DMADescTypeDef *dmarxdesc; */ +/* */ +/* if(dmarxdesclist->AppDescNbr == 0U) */ +/* { */ +/* if(HAL_ETH_IsRxDataAvailable(heth) == 0U) */ +/* { */ +/* / * No data to be transferred to the application * / */ +/* return HAL_ERROR; */ +/* } */ +/* } */ +/* */ +/* / * Get index of last descriptor * / */ +/* INCR_RX_DESC_INDEX(descidx, (dmarxdesclist->AppDescNbr - 1U)); */ +/* / * Point to last descriptor * / */ +/* dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx]; */ +/* */ +/* *Length = READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_PL); */ +/* */ +/* return HAL_OK; */ +/*} */ + +/** + * @brief Get the Rx data info (Packet type, VLAN tag, Filters status, ...) + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param RxPacketInfo: parameter to hold info of received buffer + * @retval HAL status + */ +/*HAL_StatusTypeDef HAL_ETH_GetRxDataInfo(ETH_HandleTypeDef *heth, ETH_RxPacketInfo *RxPacketInfo) */ +/*{ */ +/* ETH_RxDescListTypeDef *dmarxdesclist = &heth->RxDescList; */ +/* uint32_t descidx = dmarxdesclist->FirstAppDesc; */ +/* __IO const ETH_DMADescTypeDef *dmarxdesc; */ +/* */ +/* if(dmarxdesclist->AppDescNbr == 0U) */ +/* { */ +/* if(HAL_ETH_IsRxDataAvailable(heth) == 0U) */ +/* { */ +/* / * No data to be transferred to the application * / */ +/* return HAL_ERROR; */ +/* } */ +/* } */ +/* */ +/* / * Get index of last descriptor * / */ +/* INCR_RX_DESC_INDEX(descidx, ((dmarxdesclist->AppDescNbr) - 1U)); */ +/* / * Point to last descriptor * / */ +/* dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx]; */ +/* */ +/* if((dmarxdesc->DESC3 & ETH_DMARXNDESCWBF_ES) != (uint32_t)RESET) */ +/* { */ +/* RxPacketInfo->ErrorCode = READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_ERRORS_MASK); */ +/* } */ +/* else */ +/* { */ +/* if(READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_RS0V) != 0U) */ +/* { */ +/* */ +/* if(READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_LT) == ETH_DMARXNDESCWBF_LT_DVLAN) */ +/* { */ +/* RxPacketInfo->VlanTag = READ_BIT(dmarxdesc->DESC0, ETH_DMARXNDESCWBF_OVT); */ +/* RxPacketInfo->InnerVlanTag = READ_BIT(dmarxdesc->DESC0, ETH_DMARXNDESCWBF_IVT) >> 16; */ +/* } */ +/* else */ +/* { */ +/* RxPacketInfo->VlanTag = READ_BIT(dmarxdesc->DESC0, ETH_DMARXNDESCWBF_OVT); */ +/* } */ +/* } */ +/* */ +/* if(READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_RS1V) != 0U) */ +/* { */ +/* / * Get Payload type * / */ +/* RxPacketInfo->PayloadType =READ_BIT( dmarxdesc->DESC1, ETH_DMARXNDESCWBF_PT); */ +/* / * Get Header type * / */ +/* RxPacketInfo->HeaderType = READ_BIT(dmarxdesc->DESC1, (ETH_DMARXNDESCWBF_IPV4 | ETH_DMARXNDESCWBF_IPV6)); */ +/* / * Get Checksum status * / */ +/* RxPacketInfo->Checksum = READ_BIT(dmarxdesc->DESC1, (ETH_DMARXNDESCWBF_IPCE | ETH_DMARXNDESCWBF_IPCB | ETH_DMARXNDESCWBF_IPHE)); */ +/* } */ +/* */ +/* if(READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_RS2V) != 0U) */ +/* { */ +/* RxPacketInfo->MacFilterStatus = READ_BIT(dmarxdesc->DESC2, (ETH_DMARXNDESCWBF_HF | ETH_DMARXNDESCWBF_DAF | ETH_DMARXNDESCWBF_SAF | ETH_DMARXNDESCWBF_VF)); */ +/* RxPacketInfo->L3FilterStatus = READ_BIT(dmarxdesc->DESC2, (ETH_DMARXNDESCWBF_L3FM | ETH_DMARXNDESCWBF_L3L4FM)); */ +/* RxPacketInfo->L4FilterStatus = READ_BIT(dmarxdesc->DESC2, (ETH_DMARXNDESCWBF_L4FM | ETH_DMARXNDESCWBF_L3L4FM)); */ +/* } */ +/* } */ +/* */ +/* / * Get the segment count * / */ +/* WRITE_REG(RxPacketInfo->SegmentCnt, dmarxdesclist->AppDescNbr); */ +/* */ +/* return HAL_OK; */ +/*} */ + +/** + * @brief This function gives back Rx Desc of the last received Packet + * to the DMA, so ETH DMA will be able to use these descriptors + * to receive next Packets. + * It should be called after processing the received Packet. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status. + */ + HAL_StatusTypeDef HAL_ETH_BuildRxDescriptors( ETH_HandleTypeDef * heth, + uint8_t * pucNewBuffer ) + { + ETH_RxDescListTypeDef * dmarxdesclist = &heth->RxDescList; + uint32_t desc_index = dmarxdesclist->FirstAppDesc; + __IO ETH_DMADescTypeDef * dmarxdesc = ( ETH_DMADescTypeDef * ) dmarxdesclist->RxDesc[ desc_index ]; + uint32_t totalappdescnbr = dmarxdesclist->AppDescNbr; + uint32_t descscan; + + if( dmarxdesclist->AppDescNbr == 0U ) + { + /* No Rx descriptors to build */ + return HAL_ERROR; + } + + if( dmarxdesclist->AppContextDesc != 0U ) + { + /* A context descriptor is available */ + totalappdescnbr += 1U; + } + + for( descscan = 0; ipTRUE_BOOL; descscan++ ) + { + uint32_t DESC3; + uint8_t * pucBuffer; + + if( pucNewBuffer != NULL ) + { + /* Earlier zero-copy RX only: buffer was passed to the application. */ + pucBuffer = pucNewBuffer; + dmarxdesc->BackupAddr0 = ( uint32_t ) pucNewBuffer; + } + else + { + /* Keep on using the same buffer as before. */ + pucBuffer = ( uint8_t * ) dmarxdesc->BackupAddr0; + } + + WRITE_REG( dmarxdesc->DESC0, ( uint32_t ) pucBuffer ); + /* Buffer 1 Address Valid */ + + DESC3 = READ_REG( dmarxdesc->DESC3 ); + WRITE_REG( DESC3, ETH_DMARXNDESCRF_BUF1V ); + +/* BackupAddr1 is not used in FreeRTOS+TCP */ +/* if (READ_REG(dmarxdesc->BackupAddr1) != 0U) */ +/* { */ +/* WRITE_REG(dmarxdesc->DESC2, dmarxdesc->BackupAddr1); */ +/* SET_BIT(DESC3, ETH_DMARXNDESCRF_BUF2V); */ +/* } */ + + /* Should be the last change. */ +/* SET_BIT(DESC3, ETH_DMARXNDESCRF_OWN); */ + + if( dmarxdesclist->ItMode != 0U ) + { + /* Interrupt Enabled on Completion */ + SET_BIT( DESC3, ETH_DMARXNDESCRF_IOC ); + } + + /* Now all is ready.. */ + SET_BIT( DESC3, ETH_DMARXNDESCRF_OWN ); + + WRITE_REG( dmarxdesc->DESC3, DESC3 ); + + __DSB(); + + /* And read it back. */ + ( void ) dmarxdesc->DESC3; + + if( descscan >= ( totalappdescnbr - 1U ) ) + { + break; + } + + /* Increment rx descriptor index */ + INCR_RX_DESC_INDEX( desc_index, 1U ); + /* Get descriptor address */ + dmarxdesc = ( ETH_DMADescTypeDef * ) dmarxdesclist->RxDesc[ desc_index ]; + } + + /* Set the Tail pointer address to the last rx descriptor hold by the app */ + WRITE_REG( heth->Instance->DMACRDTPR, ( uint32_t ) dmarxdesc ); + + /* reset the Application desc number */ + dmarxdesclist->AppDescNbr = 0; + + /* reset the application context descriptor */ + WRITE_REG( heth->RxDescList.AppContextDesc, 0 ); + + return HAL_OK; + } + + +/** + * @brief This function handles ETH interrupt request. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + void HAL_ETH_IRQHandler( ETH_HandleTypeDef * heth ) + { + /* ETH interrupt. See heth->DMACSR for details. + */ + + if( __HAL_ETH_DMA_GET_IT( heth, ETH_DMACSR_RI ) ) + { + if( __HAL_ETH_DMA_GET_IT_SOURCE( heth, ETH_DMACIER_RIE ) ) + { + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + { + /*Call registered Receive complete callback*/ + heth->RxCpltCallback( heth ); + } + #else + { + /* Receive complete callback */ + HAL_ETH_RxCpltCallback( heth ); + } + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + + /* Clear the Eth DMA Rx IT pending bits */ + __HAL_ETH_DMA_CLEAR_IT( heth, ETH_DMACSR_RI | ETH_DMACSR_NIS ); + } + } + + /* Packet transmitted */ + if( __HAL_ETH_DMA_GET_IT( heth, ETH_DMACSR_TI ) ) + { + if( __HAL_ETH_DMA_GET_IT_SOURCE( heth, ETH_DMACIER_TIE ) ) + { + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + { + /*Call registered Transmit complete callback*/ + heth->TxCpltCallback( heth ); + } + #else + { + /* Transfer complete callback */ + HAL_ETH_TxCpltCallback( heth ); + } + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + + /* Clear the Eth DMA Tx IT pending bits */ + __HAL_ETH_DMA_CLEAR_IT( heth, ETH_DMACSR_TI | ETH_DMACSR_NIS ); + } + } + + /* ETH DMA Error */ + if( __HAL_ETH_DMA_GET_IT( heth, ETH_DMACSR_AIS ) ) + { + if( __HAL_ETH_DMA_GET_IT_SOURCE( heth, ETH_DMACIER_AIE ) ) + { + heth->ErrorCode |= HAL_ETH_ERROR_DMA; + + /* if fatal bus error occured */ + if( __HAL_ETH_DMA_GET_IT( heth, ETH_DMACSR_FBE ) ) + { + /* Get DMA error code */ + heth->DMAErrorCode = READ_BIT( heth->Instance->DMACSR, ( ETH_DMACSR_FBE | ETH_DMACSR_TPS | ETH_DMACSR_RPS ) ); + + /* Disable all interrupts */ + __HAL_ETH_DMA_DISABLE_IT( heth, ETH_DMACIER_NIE | ETH_DMACIER_AIE ); + + /* Set HAL state to ERROR */ + set_error_state( heth, HAL_ETH_STATE_ERROR ); + } + else + { + /* Get DMA error status */ + heth->DMAErrorCode = READ_BIT( heth->Instance->DMACSR, ( ETH_DMACSR_CDE | ETH_DMACSR_ETI | ETH_DMACSR_RWT | + ETH_DMACSR_RBU | ETH_DMACSR_AIS ) ); + + /* Clear the interrupt summary flag */ + __HAL_ETH_DMA_CLEAR_IT( heth, ( ETH_DMACSR_CDE | ETH_DMACSR_ETI | ETH_DMACSR_RWT | + ETH_DMACSR_RBU | ETH_DMACSR_AIS ) ); + } + + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + { + /* Call registered DMA Error callback*/ + heth->DMAErrorCallback( heth ); + } + #else + { + /* Ethernet DMA Error callback */ + HAL_ETH_DMAErrorCallback( heth ); + } + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + } + } + + /* ETH MAC Error IT */ + if( __HAL_ETH_MAC_GET_IT( heth, ( ETH_MACIER_RXSTSIE | ETH_MACIER_TXSTSIE ) ) ) + { + /* Get MAC Rx Tx status and clear Status register pending bit */ + heth->MACErrorCode = READ_REG( heth->Instance->MACRXTXSR ); + + set_error_state( heth, HAL_ETH_STATE_ERROR ); + + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + { + /* Call registered MAC Error callback*/ + heth->DMAErrorCallback( heth ); + } + #else + { + /* Ethernet MAC Error callback */ + HAL_ETH_MACErrorCallback( heth ); + } + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + + heth->MACErrorCode = ( uint32_t ) ( 0x0U ); + } + + /* ETH PMT IT */ + if( __HAL_ETH_MAC_GET_IT( heth, ETH_MAC_PMT_IT ) ) + { + /* Get MAC Wake-up source and clear the status register pending bit */ + heth->MACWakeUpEvent = READ_BIT( heth->Instance->MACPCSR, ( ETH_MACPCSR_RWKPRCVD | ETH_MACPCSR_MGKPRCVD ) ); + + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + { + /* Call registered PMT callback*/ + heth->PMTCallback( heth ); + } + #else + { + /* Ethernet PMT callback */ + HAL_ETH_PMTCallback( heth ); + } + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + + heth->MACWakeUpEvent = ( uint32_t ) ( 0x0U ); + } + + /* ETH EEE IT */ + if( __HAL_ETH_MAC_GET_IT( heth, ETH_MAC_LPI_IT ) ) + { + /* Get MAC LPI interrupt source and clear the status register pending bit */ + heth->MACLPIEvent = READ_BIT( heth->Instance->MACPCSR, 0x0000000FU ); + + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + { + /* Call registered EEE callback*/ + heth->EEECallback( heth ); + } + #else + { + /* Ethernet EEE callback */ + HAL_ETH_EEECallback( heth ); + } + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + + heth->MACLPIEvent = ( uint32_t ) ( 0x0U ); + } + + #if defined( DUAL_CORE ) + if( HAL_GetCurrentCPUID() == CM7_CPUID ) + { + /* check ETH WAKEUP exti flag */ + if( __HAL_ETH_WAKEUP_EXTI_GET_FLAG( ETH_WAKEUP_EXTI_LINE ) != ( uint32_t ) RESET ) + { + /* Clear ETH WAKEUP Exti pending bit */ + __HAL_ETH_WAKEUP_EXTI_CLEAR_FLAG( ETH_WAKEUP_EXTI_LINE ); + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + { + /* Call registered WakeUp callback*/ + heth->WakeUpCallback( heth ); + } + #else + { + /* ETH WAKEUP callback */ + HAL_ETH_WakeUpCallback( heth ); + } + #endif + } + } + else + { + /* check ETH WAKEUP exti flag */ + if( __HAL_ETH_WAKEUP_EXTID2_GET_FLAG( ETH_WAKEUP_EXTI_LINE ) != ( uint32_t ) RESET ) + { + /* Clear ETH WAKEUP Exti pending bit */ + __HAL_ETH_WAKEUP_EXTID2_CLEAR_FLAG( ETH_WAKEUP_EXTI_LINE ); + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + { + /* Call registered WakeUp callback*/ + heth->WakeUpCallback( heth ); + } + #else + { + /* ETH WAKEUP callback */ + HAL_ETH_WakeUpCallback( heth ); + } + #endif + } + } + #else /* #if defined(DUAL_CORE) */ + /* check ETH WAKEUP exti flag */ + if( __HAL_ETH_WAKEUP_EXTI_GET_FLAG( ETH_WAKEUP_EXTI_LINE ) != ( uint32_t ) RESET ) + { + /* Clear ETH WAKEUP Exti pending bit */ + __HAL_ETH_WAKEUP_EXTI_CLEAR_FLAG( ETH_WAKEUP_EXTI_LINE ); + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + { + /* Call registered WakeUp callback*/ + heth->WakeUpCallback( heth ); + } + #else + { + /* ETH WAKEUP callback */ + HAL_ETH_WakeUpCallback( heth ); + } + #endif + } + #endif /* #if defined(DUAL_CORE) */ + } + +/** + * @brief Tx Transfer completed callbacks. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef * heth ) + { + /* Prevent unused argument(s) compilation warning */ + UNUSED( heth ); + + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_TxCpltCallback could be implemented in the user file + */ + } + +/** + * @brief Rx Transfer completed callbacks. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef * heth ) + { + /* Prevent unused argument(s) compilation warning */ + UNUSED( heth ); + + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_RxCpltCallback could be implemented in the user file + */ + } + +/** + * @brief Ethernet DMA transfer error callbacks + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_DMAErrorCallback( ETH_HandleTypeDef * heth ) + { + /* Prevent unused argument(s) compilation warning */ + UNUSED( heth ); + + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_DMAErrorCallback could be implemented in the user file + */ + } + +/** + * @brief Ethernet MAC transfer error callbacks + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_MACErrorCallback( ETH_HandleTypeDef * heth ) + { + /* Prevent unused argument(s) compilation warning */ + UNUSED( heth ); + + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_MACErrorCallback could be implemented in the user file + */ + } + +/** + * @brief Ethernet Power Management module IT callback + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_PMTCallback( ETH_HandleTypeDef * heth ) + { + /* Prevent unused argument(s) compilation warning */ + UNUSED( heth ); + + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_PMTCallback could be implemented in the user file + */ + } + +/** + * @brief Energy Efficient Etherent IT callback + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_EEECallback( ETH_HandleTypeDef * heth ) + { + /* Prevent unused argument(s) compilation warning */ + UNUSED( heth ); + + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_EEECallback could be implemented in the user file + */ + } + +/** + * @brief ETH WAKEUP interrupt callback + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + __weak void HAL_ETH_WakeUpCallback( ETH_HandleTypeDef * heth ) + { + /* Prevent unused argument(s) compilation warning */ + UNUSED( heth ); + + /* NOTE : This function Should not be modified, when the callback is needed, + * the HAL_ETH_WakeUpCallback could be implemented in the user file + */ + } + +/** + * @brief Read a PHY register + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param PHYAddr: PHY port address, must be a value from 0 to 31 + * @param PHYReg: PHY register address, must be a value from 0 to 31 + * @param pRegValue: parameter to hold read value + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_ReadPHYRegister( ETH_HandleTypeDef * heth, + uint32_t PHYAddr, + uint32_t PHYReg, + uint32_t * pRegValue ) + { + uint32_t tmpreg, tickstart; + + /* Check for the Busy flag */ + if( READ_BIT( heth->Instance->MACMDIOAR, ETH_MACMDIOAR_MB ) != 0U ) + { + return HAL_ERROR; + } + + /* Get the MACMDIOAR value */ + WRITE_REG( tmpreg, heth->Instance->MACMDIOAR ); + + /* Prepare the MDIO Address Register value + * - Set the PHY device address + * - Set the PHY register address + * - Set the read mode + * - Set the MII Busy bit */ + + MODIFY_REG( tmpreg, ETH_MACMDIOAR_PA, ( PHYAddr << 21 ) ); + MODIFY_REG( tmpreg, ETH_MACMDIOAR_RDA, ( PHYReg << 16 ) ); + MODIFY_REG( tmpreg, ETH_MACMDIOAR_MOC, ETH_MACMDIOAR_MOC_RD ); + SET_BIT( tmpreg, ETH_MACMDIOAR_MB ); + + /* Write the result value into the MDII Address register */ + WRITE_REG( heth->Instance->MACMDIOAR, tmpreg ); + + tickstart = HAL_GetTick(); + + /* Wait for the Busy flag */ + while( READ_BIT( heth->Instance->MACMDIOAR, ETH_MACMDIOAR_MB ) > 0U ) + { + if( ( ( HAL_GetTick() - tickstart ) > ETH_MDIO_BUS_TIMEOUT ) ) + { + return HAL_ERROR; + } + } + + /* Get MACMIIDR value */ + WRITE_REG( *pRegValue, ( uint16_t ) heth->Instance->MACMDIODR ); + + return HAL_OK; + } + + +/** + * @brief Writes to a PHY register. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param PHYAddr: PHY port address, must be a value from 0 to 31 + * @param PHYReg: PHY register address, must be a value from 0 to 31 + * @param RegValue: the value to write + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_WritePHYRegister( ETH_HandleTypeDef * heth, + uint32_t PHYAddr, + uint32_t PHYReg, + uint32_t RegValue ) + { + uint32_t tmpreg, tickstart; + + /* Check for the Busy flag */ + if( READ_BIT( heth->Instance->MACMDIOAR, ETH_MACMDIOAR_MB ) != 0U ) + { + return HAL_ERROR; + } + + /* Get the MACMDIOAR value */ + WRITE_REG( tmpreg, heth->Instance->MACMDIOAR ); + + /* Prepare the MDIO Address Register value + * - Set the PHY device address + * - Set the PHY register address + * - Set the write mode + * - Set the MII Busy bit */ + + MODIFY_REG( tmpreg, ETH_MACMDIOAR_PA, ( PHYAddr << 21 ) ); + MODIFY_REG( tmpreg, ETH_MACMDIOAR_RDA, ( PHYReg << 16 ) ); + MODIFY_REG( tmpreg, ETH_MACMDIOAR_MOC, ETH_MACMDIOAR_MOC_WR ); + SET_BIT( tmpreg, ETH_MACMDIOAR_MB ); + + + /* Give the value to the MII data register */ + WRITE_REG( ETH->MACMDIODR, ( uint16_t ) RegValue ); + + /* Write the result value into the MII Address register */ + WRITE_REG( ETH->MACMDIOAR, tmpreg ); + + tickstart = HAL_GetTick(); + + /* Wait for the Busy flag */ + while( READ_BIT( heth->Instance->MACMDIOAR, ETH_MACMDIOAR_MB ) > 0U ) + { + if( ( ( HAL_GetTick() - tickstart ) > ETH_MDIO_BUS_TIMEOUT ) ) + { + return HAL_ERROR; + } + } + + return HAL_OK; + } + +/** + * @} + */ + +/** @defgroup ETH_Exported_Functions_Group3 Peripheral Control functions + * @brief ETH control functions + * + * @verbatim + * ============================================================================== + ##### Peripheral Control functions ##### + #####============================================================================== + #####[..] + #####This subsection provides a set of functions allowing to control the ETH + #####peripheral. + ##### + #####@endverbatim + * @{ + */ + +/** + * @brief Get the configuration of the MAC and MTL subsystems. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param macconf: pointer to a ETH_MACConfigTypeDef structure that will hold + * the configuration of the MAC. + * @retval HAL Status + */ + HAL_StatusTypeDef HAL_ETH_GetMACConfig( ETH_HandleTypeDef * heth, + ETH_MACConfigTypeDef * macconf ) + { + if( macconf == NULL ) + { + return HAL_ERROR; + } + + /* Get MAC parameters */ + macconf->PreambleLength = READ_BIT( heth->Instance->MACCR, ETH_MACCR_PRELEN ); + macconf->DeferralCheck = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_DC ) >> 4 ) > 0U ) ? ENABLE : DISABLE; + macconf->BackOffLimit = READ_BIT( heth->Instance->MACCR, ETH_MACCR_BL ); + macconf->RetryTransmission = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_DR ) >> 8 ) == 0U ) ? ENABLE : DISABLE; + macconf->CarrierSenseDuringTransmit = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_DCRS ) >> 9 ) > 0U ) ? ENABLE : DISABLE; + macconf->ReceiveOwn = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_DO ) >> 10 ) == 0U ) ? ENABLE : DISABLE; + macconf->CarrierSenseBeforeTransmit = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_ECRSFD ) >> 11 ) > 0U ) ? ENABLE : DISABLE; + macconf->LoopbackMode = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_LM ) >> 12 ) > 0U ) ? ENABLE : DISABLE; + macconf->DuplexMode = READ_BIT( heth->Instance->MACCR, ETH_MACCR_DM ); + macconf->Speed = READ_BIT( heth->Instance->MACCR, ETH_MACCR_FES ); + macconf->JumboPacket = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_JE ) >> 16 ) > 0U ) ? ENABLE : DISABLE; + macconf->Jabber = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_JD ) >> 17 ) == 0U ) ? ENABLE : DISABLE; + macconf->Watchdog = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_WD ) >> 19 ) == 0U ) ? ENABLE : DISABLE; + macconf->AutomaticPadCRCStrip = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_ACS ) >> 20 ) > 0U ) ? ENABLE : DISABLE; + macconf->CRCStripTypePacket = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_CST ) >> 21 ) > 0U ) ? ENABLE : DISABLE; + macconf->Support2KPacket = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_S2KP ) >> 22 ) > 0U ) ? ENABLE : DISABLE; + macconf->GiantPacketSizeLimitControl = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_GPSLCE ) >> 23 ) > 0U ) ? ENABLE : DISABLE; + macconf->InterPacketGapVal = READ_BIT( heth->Instance->MACCR, ETH_MACCR_IPG ); + macconf->ChecksumOffload = ( ( READ_BIT( heth->Instance->MACCR, ETH_MACCR_IPC ) >> 27 ) > 0U ) ? ENABLE : DISABLE; + macconf->SourceAddrControl = READ_BIT( heth->Instance->MACCR, ETH_MACCR_SARC ); + + macconf->GiantPacketSizeLimit = READ_BIT( heth->Instance->MACECR, ETH_MACECR_GPSL ); + macconf->CRCCheckingRxPackets = ( ( READ_BIT( heth->Instance->MACECR, ETH_MACECR_DCRCC ) >> 16 ) == 0U ) ? ENABLE : DISABLE; + macconf->SlowProtocolDetect = ( ( READ_BIT( heth->Instance->MACECR, ETH_MACECR_SPEN ) >> 17 ) > 0U ) ? ENABLE : DISABLE; + macconf->UnicastSlowProtocolPacketDetect = ( ( READ_BIT( heth->Instance->MACECR, ETH_MACECR_USP ) >> 18 ) > 0U ) ? ENABLE : DISABLE; + macconf->ExtendedInterPacketGap = ( ( READ_BIT( heth->Instance->MACECR, ETH_MACECR_EIPGEN ) >> 24 ) > 0U ) ? ENABLE : DISABLE; + macconf->ExtendedInterPacketGapVal = READ_BIT( heth->Instance->MACECR, ETH_MACECR_EIPG ) >> 25; + + + macconf->ProgrammableWatchdog = ( ( READ_BIT( heth->Instance->MACWTR, ETH_MACWTR_PWE ) >> 8 ) > 0U ) ? ENABLE : DISABLE; + macconf->WatchdogTimeout = READ_BIT( heth->Instance->MACWTR, ETH_MACWTR_WTO ); + + macconf->TransmitFlowControl = ( ( READ_BIT( heth->Instance->MACTFCR, ETH_MACTFCR_TFE ) >> 1 ) > 0U ) ? ENABLE : DISABLE; + macconf->ZeroQuantaPause = ( ( READ_BIT( heth->Instance->MACTFCR, ETH_MACTFCR_DZPQ ) >> 7 ) == 0U ) ? ENABLE : DISABLE; + macconf->PauseLowThreshold = READ_BIT( heth->Instance->MACTFCR, ETH_MACTFCR_PLT ); + macconf->PauseTime = ( READ_BIT( heth->Instance->MACTFCR, ETH_MACTFCR_PT ) >> 16 ); + + + macconf->ReceiveFlowControl = ( READ_BIT( heth->Instance->MACRFCR, ETH_MACRFCR_RFE ) > 0U ) ? ENABLE : DISABLE; + macconf->UnicastPausePacketDetect = ( ( READ_BIT( heth->Instance->MACRFCR, ETH_MACRFCR_UP ) >> 1 ) > 0U ) ? ENABLE : DISABLE; + + macconf->TransmitQueueMode = READ_BIT( heth->Instance->MTLTQOMR, ( ETH_MTLTQOMR_TTC | ETH_MTLTQOMR_TSF ) ); + + macconf->ReceiveQueueMode = READ_BIT( heth->Instance->MTLRQOMR, ( ETH_MTLRQOMR_RTC | ETH_MTLRQOMR_RSF ) ); + macconf->ForwardRxUndersizedGoodPacket = ( ( READ_BIT( heth->Instance->MTLRQOMR, ETH_MTLRQOMR_FUP ) >> 3 ) > 0U ) ? ENABLE : DISABLE; + macconf->ForwardRxErrorPacket = ( ( READ_BIT( heth->Instance->MTLRQOMR, ETH_MTLRQOMR_FEP ) >> 4 ) > 0U ) ? ENABLE : DISABLE; + macconf->DropTCPIPChecksumErrorPacket = ( ( READ_BIT( heth->Instance->MTLRQOMR, ETH_MTLRQOMR_DISTCPEF ) >> 6 ) == 0U ) ? ENABLE : DISABLE; + + return HAL_OK; + } + +/** + * @brief Get the configuration of the DMA. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param dmaconf: pointer to a ETH_DMAConfigTypeDef structure that will hold + * the configuration of the ETH DMA. + * @retval HAL Status + */ + HAL_StatusTypeDef HAL_ETH_GetDMAConfig( ETH_HandleTypeDef * heth, + ETH_DMAConfigTypeDef * dmaconf ) + { + if( dmaconf == NULL ) + { + return HAL_ERROR; + } + + dmaconf->AddressAlignedBeats = ( ( READ_BIT( heth->Instance->DMASBMR, ETH_DMASBMR_AAL ) >> 12 ) > 0U ) ? ENABLE : DISABLE; + dmaconf->BurstMode = READ_BIT( heth->Instance->DMASBMR, ETH_DMASBMR_FB | ETH_DMASBMR_MB ); + dmaconf->RebuildINCRxBurst = ( ( READ_BIT( heth->Instance->DMASBMR, ETH_DMASBMR_RB ) >> 15 ) > 0U ) ? ENABLE : DISABLE; + + dmaconf->DMAArbitration = READ_BIT( heth->Instance->DMAMR, ( ETH_DMAMR_TXPR | ETH_DMAMR_PR | ETH_DMAMR_DA ) ); + + dmaconf->PBLx8Mode = ( ( READ_BIT( heth->Instance->DMACCR, ETH_DMACCR_8PBL ) >> 16 ) > 0U ) ? ENABLE : DISABLE; + dmaconf->MaximumSegmentSize = READ_BIT( heth->Instance->DMACCR, ETH_DMACCR_MSS ); + + dmaconf->FlushRxPacket = ( ( READ_BIT( heth->Instance->DMACRCR, ETH_DMACRCR_RPF ) >> 31 ) > 0U ) ? ENABLE : DISABLE; + dmaconf->RxDMABurstLength = READ_BIT( heth->Instance->DMACRCR, ETH_DMACRCR_RPBL ); + + dmaconf->SecondPacketOperate = ( ( READ_BIT( heth->Instance->DMACTCR, ETH_DMACTCR_OSP ) >> 4 ) > 0U ) ? ENABLE : DISABLE; + dmaconf->TCPSegmentation = ( ( READ_BIT( heth->Instance->DMACTCR, ETH_DMACTCR_TSE ) >> 12 ) > 0U ) ? ENABLE : DISABLE; + dmaconf->TxDMABurstLength = READ_BIT( heth->Instance->DMACTCR, ETH_DMACTCR_TPBL ); + + return HAL_OK; + } + +/** + * @brief Set the MAC configuration. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param macconf: pointer to a ETH_MACConfigTypeDef structure that contains + * the configuration of the MAC. + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_SetMACConfig( ETH_HandleTypeDef * heth, + ETH_MACConfigTypeDef * macconf ) + { + if( macconf == NULL ) + { + return HAL_ERROR; + } + + if( heth->RxState == HAL_ETH_STATE_READY ) + { + ETH_SetMACConfig( heth, macconf ); + + return HAL_OK; + } + else + { + return HAL_ERROR; + } + } + +/** + * @brief Set the ETH DMA configuration. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param dmaconf: pointer to a ETH_DMAConfigTypeDef structure that will hold + * the configuration of the ETH DMA. + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_SetDMAConfig( ETH_HandleTypeDef * heth, + ETH_DMAConfigTypeDef * dmaconf ) + { + if( dmaconf == NULL ) + { + return HAL_ERROR; + } + + if( heth->RxState == HAL_ETH_STATE_READY ) + { + ETH_SetDMAConfig( heth, dmaconf ); + + return HAL_OK; + } + else + { + return HAL_ERROR; + } + } + +/** + * @brief Configures the Clock range of ETH MDIO interface. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + void HAL_ETH_SetMDIOClockRange( ETH_HandleTypeDef * heth ) + { + uint32_t tmpreg, hclk; + + /* Get the ETHERNET MACMDIOAR value */ + tmpreg = ( heth->Instance )->MACMDIOAR; + + /* Clear CSR Clock Range bits */ + tmpreg &= ~ETH_MACMDIOAR_CR; + + /* Get hclk frequency value */ + hclk = HAL_RCC_GetHCLKFreq(); + + /* Set CR bits depending on hclk value */ + if( ( hclk >= 20000000U ) && ( hclk < 35000000U ) ) + { + /* CSR Clock Range between 20-35 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV16; + } + else if( ( hclk >= 35000000U ) && ( hclk < 60000000U ) ) + { + /* CSR Clock Range between 35-60 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV26; + } + else if( ( hclk >= 60000000U ) && ( hclk < 100000000U ) ) + { + /* CSR Clock Range between 60-100 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV42; + } + else if( ( hclk >= 100000000U ) && ( hclk < 150000000U ) ) + { + /* CSR Clock Range between 100-150 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV62; + } + else /* (hclk >= 150000000)&&(hclk <= 200000000) */ + { + /* CSR Clock Range between 150-200 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV102; + } + + /* Configure the CSR Clock Range */ + ( heth->Instance )->MACMDIOAR = ( uint32_t ) tmpreg; + } + +/** + * @brief Set the ETH MAC (L2) Filters configuration. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param pFilterConfig: pointer to a ETH_MACFilterConfigTypeDef structure that contains + * the configuration of the ETH MAC filters. + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_SetMACFilterConfig( ETH_HandleTypeDef * heth, + ETH_MACFilterConfigTypeDef * pFilterConfig ) + { + uint32_t filterconfig; + + if( pFilterConfig == NULL ) + { + return HAL_ERROR; + } + + filterconfig = ( ( uint32_t ) pFilterConfig->PromiscuousMode | + ( ( uint32_t ) pFilterConfig->HashUnicast << 1 ) | + ( ( uint32_t ) pFilterConfig->HashMulticast << 2 ) | + ( ( uint32_t ) pFilterConfig->DestAddrInverseFiltering << 3 ) | + ( ( uint32_t ) pFilterConfig->PassAllMulticast << 4 ) | + ( ( uint32_t ) ( ( pFilterConfig->BroadcastFilter == DISABLE ) ? 1U : 0U ) << 5 ) | + ( ( uint32_t ) pFilterConfig->SrcAddrInverseFiltering << 8 ) | + ( ( uint32_t ) pFilterConfig->SrcAddrFiltering << 9 ) | + ( ( uint32_t ) pFilterConfig->HachOrPerfectFilter << 10 ) | + ( ( uint32_t ) pFilterConfig->ReceiveAllMode << 31 ) | + pFilterConfig->ControlPacketsFilter ); + + MODIFY_REG( heth->Instance->MACPFR, ETH_MACPFR_MASK, filterconfig ); + + return HAL_OK; + } + +/** + * @brief Get the ETH MAC (L2) Filters configuration. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param pFilterConfig: pointer to a ETH_MACFilterConfigTypeDef structure that will hold + * the configuration of the ETH MAC filters. + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_GetMACFilterConfig( ETH_HandleTypeDef * heth, + ETH_MACFilterConfigTypeDef * pFilterConfig ) + { + if( pFilterConfig == NULL ) + { + return HAL_ERROR; + } + + pFilterConfig->PromiscuousMode = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_PR ) ) > 0U ) ? ENABLE : DISABLE; + pFilterConfig->HashUnicast = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_HUC ) >> 1 ) > 0U ) ? ENABLE : DISABLE; + pFilterConfig->HashMulticast = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_HMC ) >> 2 ) > 0U ) ? ENABLE : DISABLE; + pFilterConfig->DestAddrInverseFiltering = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_DAIF ) >> 3 ) > 0U ) ? ENABLE : DISABLE; + pFilterConfig->PassAllMulticast = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_PM ) >> 4 ) > 0U ) ? ENABLE : DISABLE; + pFilterConfig->BroadcastFilter = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_DBF ) >> 5 ) == 0U ) ? ENABLE : DISABLE; + pFilterConfig->ControlPacketsFilter = READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_PCF ); + pFilterConfig->SrcAddrInverseFiltering = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_SAIF ) >> 8 ) > 0U ) ? ENABLE : DISABLE; + pFilterConfig->SrcAddrFiltering = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_SAF ) >> 9 ) > 0U ) ? ENABLE : DISABLE; + pFilterConfig->HachOrPerfectFilter = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_HPF ) >> 10 ) > 0U ) ? ENABLE : DISABLE; + pFilterConfig->ReceiveAllMode = ( ( READ_BIT( heth->Instance->MACPFR, ETH_MACPFR_RA ) >> 31 ) > 0U ) ? ENABLE : DISABLE; + + return HAL_OK; + } + +/** + * @brief Set the source MAC Address to be matched. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param AddrNbr: The MAC address to configure + * This parameter must be a value of the following: + * ETH_MAC_ADDRESS1 + * ETH_MAC_ADDRESS2 + * ETH_MAC_ADDRESS3 + * @param pMACAddr: Pointer to MAC address buffer data (6 bytes) + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_SetSourceMACAddrMatch( ETH_HandleTypeDef * heth, + uint32_t AddrNbr, + uint8_t * pMACAddr ) + { + uint32_t macaddrhr, macaddrlr; + + if( pMACAddr == NULL ) + { + return HAL_ERROR; + } + + /* Get mac addr high reg offset */ + macaddrhr = ( ( uint32_t ) &( heth->Instance->MACA0HR ) + AddrNbr ); + /* Get mac addr low reg offset */ + macaddrlr = ( ( uint32_t ) &( heth->Instance->MACA0LR ) + AddrNbr ); + + /* Set MAC addr bits 32 to 47 */ + ( *( __IO uint32_t * ) macaddrhr ) = ( ( ( uint32_t ) ( pMACAddr[ 5 ] ) << 8 ) | ( uint32_t ) pMACAddr[ 4 ] ); + /* Set MAC addr bits 0 to 31 */ + ( *( __IO uint32_t * ) macaddrlr ) = ( ( ( uint32_t ) ( pMACAddr[ 3 ] ) << 24 ) | ( ( uint32_t ) ( pMACAddr[ 2 ] ) << 16 ) | + ( ( uint32_t ) ( pMACAddr[ 1 ] ) << 8 ) | ( uint32_t ) pMACAddr[ 0 ] ); + + /* Enable address and set source address bit */ + ( *( __IO uint32_t * ) macaddrhr ) |= ( ETH_MACAHR_SA | ETH_MACAHR_AE ); + + return HAL_OK; + } + +/** + * @brief Set the ETH Hash Table Value. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param pHashTable: pointer to a table of two 32 bit values, that contains + * the 64 bits of the hash table. + * @retval HAL status + */ + HAL_StatusTypeDef HAL_ETH_SetHashTable( ETH_HandleTypeDef * heth, + uint32_t * pHashTable ) + { + if( pHashTable == NULL ) + { + return HAL_ERROR; + } + + heth->Instance->MACHT0R = pHashTable[ 0 ]; + heth->Instance->MACHT1R = pHashTable[ 1 ]; + + return HAL_OK; + } + +/** + * @brief Set the VLAN Identifier for Rx packets + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param ComparisonBits: 12 or 16 bit comparison mode + * must be a value of @ref ETH_VLAN_Tag_Comparison + * @param VLANIdentifier: VLAN Identifier value + * @retval None + */ + void HAL_ETH_SetRxVLANIdentifier( ETH_HandleTypeDef * heth, + uint32_t ComparisonBits, + uint32_t VLANIdentifier ) + { + if( ComparisonBits == ETH_VLANTAGCOMPARISON_16BIT ) + { + MODIFY_REG( heth->Instance->MACVTR, ETH_MACVTR_VL, VLANIdentifier ); + CLEAR_BIT( heth->Instance->MACVTR, ETH_MACVTR_ETV ); + } + else + { + MODIFY_REG( heth->Instance->MACVTR, ETH_MACVTR_VL_VID, VLANIdentifier ); + SET_BIT( heth->Instance->MACVTR, ETH_MACVTR_ETV ); + } + } + +/** + * @brief Enters the Power down mode. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param pPowerDownConfig: a pointer to ETH_PowerDownConfigTypeDef structure + * that contains the Power Down configration + * @retval None. + */ + void HAL_ETH_EnterPowerDownMode( ETH_HandleTypeDef * heth, + ETH_PowerDownConfigTypeDef * pPowerDownConfig ) + { + uint32_t powerdownconfig; + + powerdownconfig = ( ( ( uint32_t ) pPowerDownConfig->MagicPacket << 1 ) | + ( ( uint32_t ) pPowerDownConfig->WakeUpPacket << 2 ) | + ( ( uint32_t ) pPowerDownConfig->GlobalUnicast << 9 ) | + ( ( uint32_t ) pPowerDownConfig->WakeUpForward << 10 ) | + ETH_MACPCSR_PWRDWN ); + + /* Enable PMT interrupt */ + __HAL_ETH_MAC_ENABLE_IT( heth, ETH_MACIER_PMTIE ); + + MODIFY_REG( heth->Instance->MACPCSR, ETH_MACPCSR_MASK, powerdownconfig ); + } + +/*/ ** */ +/* * @brief Exits from the Power down mode. */ +/* * @param heth: pointer to a ETH_HandleTypeDef structure that contains */ +/* * the configuration information for ETHERNET module */ +/* * @retval None. */ +/* * / */ +/*void HAL_ETH_ExitPowerDownMode(ETH_HandleTypeDef *heth) */ +/*{ */ +/* / * clear wake up sources * / */ +/* CLEAR_BIT(heth->Instance->MACPCSR, ETH_MACPCSR_RWKPKTEN | ETH_MACPCSR_MGKPKTEN | ETH_MACPCSR_GLBLUCAST | ETH_MACPCSR_RWKPFE); */ +/* */ +/* if(READ_BIT(heth->Instance->MACPCSR, ETH_MACPCSR_PWRDWN) != 0U) */ +/* { */ +/* / * Exit power down mode * / */ +/* CLEAR_BIT(heth->Instance->MACPCSR, ETH_MACPCSR_PWRDWN); */ +/* } */ +/* */ +/* / * Disable PMT interrupt * / */ +/* __HAL_ETH_MAC_DISABLE_IT(heth, ETH_MACIER_PMTIE); */ +/*} */ +/* */ +/*/ ** */ +/* * @brief Set the WakeUp filter. */ +/* * @param heth: pointer to a ETH_HandleTypeDef structure that contains */ +/* * the configuration information for ETHERNET module */ +/* * @param pFilter: pointer to filter registers values */ +/* * @param Count: number of filter registers, must be from 1 to 8. */ +/* * @retval None. */ +/* * / */ +/*HAL_StatusTypeDef HAL_ETH_SetWakeUpFilter(ETH_HandleTypeDef *heth, uint32_t *pFilter, uint32_t Count) */ +/*{ */ +/* uint32_t regindex; */ +/* */ +/* if(pFilter == NULL) */ +/* { */ +/* return HAL_ERROR; */ +/* } */ +/* */ +/* / * Reset Filter Pointer * / */ +/* SET_BIT(heth->Instance->MACPCSR, ETH_MACPCSR_RWKFILTRST); */ +/* */ +/* / * Wake up packet filter config * / */ +/* for(regindex = 0; regindex < Count; regindex++) */ +/* { */ +/* / * Write filter regs * / */ +/* WRITE_REG(heth->Instance->MACRWKPFR, pFilter[regindex]); */ +/* } */ +/* */ +/* return HAL_OK; */ +/*} */ + +/** + * @} + */ + +/** @defgroup ETH_Exported_Functions_Group4 Peripheral State and Errors functions + * @brief ETH State and Errors functions + * + * @verbatim + * ============================================================================== + ##### Peripheral State and Errors functions ##### + #####============================================================================== + #####[..] + #####This subsection provides a set of functions allowing to return the State of + #####ETH communication process, return Peripheral Errors occurred during communication + #####process + ##### + ##### + #####@endverbatim + * @{ + */ + +/** + * @brief Returns the ETH state. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL state + */ + HAL_ETH_StateTypeDef HAL_ETH_GetState( ETH_HandleTypeDef * heth ) + { + HAL_ETH_StateTypeDef ret; + HAL_ETH_StateTypeDef gstate = heth->gState; + HAL_ETH_StateTypeDef rxstate = heth->RxState; + + ret = gstate; + ret |= rxstate; + return ret; + } + +/** + * @brief Returns the ETH error code + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval ETH Error Code + */ + uint32_t HAL_ETH_GetError( ETH_HandleTypeDef * heth ) + { + return heth->ErrorCode; + } + +/** + * @brief Returns the ETH DMA error code + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval ETH DMA Error Code + */ + uint32_t HAL_ETH_GetDMAError( ETH_HandleTypeDef * heth ) + { + return heth->DMAErrorCode; + } + +/** + * @brief Returns the ETH MAC error code + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval ETH MAC Error Code + */ + uint32_t HAL_ETH_GetMACError( ETH_HandleTypeDef * heth ) + { + return heth->MACErrorCode; + } + +/** + * @brief Returns the ETH MAC WakeUp event source + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval ETH MAC WakeUp event source + */ + uint32_t HAL_ETH_GetMACWakeUpSource( ETH_HandleTypeDef * heth ) + { + return heth->MACWakeUpEvent; + } + +/** + * @} + */ + +/** + * @} + */ + +/** @addtogroup ETH_Private_Functions ETH Private Functions + * @{ + */ + + static void ETH_SetMACConfig( ETH_HandleTypeDef * heth, + ETH_MACConfigTypeDef * macconf ) + { + uint32_t macregval; + + /*------------------------ MACCR Configuration --------------------*/ + macregval = ( macconf->InterPacketGapVal | + macconf->SourceAddrControl | + ( ( uint32_t ) macconf->ChecksumOffload << 27 ) | + ( ( uint32_t ) macconf->GiantPacketSizeLimitControl << 23 ) | + ( ( uint32_t ) macconf->Support2KPacket << 22 ) | + ( ( uint32_t ) macconf->CRCStripTypePacket << 21 ) | + ( ( uint32_t ) macconf->AutomaticPadCRCStrip << 20 ) | + ( ( uint32_t ) ( ( macconf->Watchdog == DISABLE ) ? 1U : 0U ) << 19 ) | + ( ( uint32_t ) ( ( macconf->Jabber == DISABLE ) ? 1U : 0U ) << 17 ) | + ( ( uint32_t ) macconf->JumboPacket << 16 ) | + macconf->Speed | + macconf->DuplexMode | + ( ( uint32_t ) macconf->LoopbackMode << 12 ) | + ( ( uint32_t ) macconf->CarrierSenseBeforeTransmit << 11 ) | + ( ( uint32_t ) ( ( macconf->ReceiveOwn == DISABLE ) ? 1U : 0U ) << 10 ) | + ( ( uint32_t ) macconf->CarrierSenseDuringTransmit << 9 ) | + ( ( uint32_t ) ( ( macconf->RetryTransmission == DISABLE ) ? 1U : 0U ) << 8 ) | + macconf->BackOffLimit | + ( ( uint32_t ) macconf->DeferralCheck << 4 ) | + macconf->PreambleLength ); + + /* Write to MACCR */ + MODIFY_REG( heth->Instance->MACCR, ETH_MACCR_MASK, macregval ); + + /*------------------------ MACECR Configuration --------------------*/ + macregval = ( ( macconf->ExtendedInterPacketGapVal << 25 ) | + ( ( uint32_t ) macconf->ExtendedInterPacketGap << 24 ) | + ( ( uint32_t ) macconf->UnicastSlowProtocolPacketDetect << 18 ) | + ( ( uint32_t ) macconf->SlowProtocolDetect << 17 ) | + ( ( uint32_t ) ( ( macconf->CRCCheckingRxPackets == DISABLE ) ? 1U : 0U ) << 16 ) | + macconf->GiantPacketSizeLimit ); + + /* Write to MACECR */ + MODIFY_REG( heth->Instance->MACECR, ETH_MACECR_MASK, macregval ); + + /*------------------------ MACWTR Configuration --------------------*/ + macregval = ( ( ( uint32_t ) macconf->ProgrammableWatchdog << 8 ) | + macconf->WatchdogTimeout ); + + /* Write to MACWTR */ + MODIFY_REG( heth->Instance->MACWTR, ETH_MACWTR_MASK, macregval ); + + /*------------------------ MACTFCR Configuration --------------------*/ + macregval = ( ( ( uint32_t ) macconf->TransmitFlowControl << 1 ) | + macconf->PauseLowThreshold | + ( ( uint32_t ) ( ( macconf->ZeroQuantaPause == DISABLE ) ? 1U : 0U ) << 7 ) | + ( macconf->PauseTime << 16 ) ); + + /* Write to MACTFCR */ + MODIFY_REG( heth->Instance->MACTFCR, ETH_MACTFCR_MASK, macregval ); + + /*------------------------ MACRFCR Configuration --------------------*/ + macregval = ( ( uint32_t ) macconf->ReceiveFlowControl | + ( ( uint32_t ) macconf->UnicastPausePacketDetect << 1 ) ); + + /* Write to MACRFCR */ + MODIFY_REG( heth->Instance->MACRFCR, ETH_MACRFCR_MASK, macregval ); + + /*------------------------ MTLTQOMR Configuration --------------------*/ + /* Write to MTLTQOMR */ + MODIFY_REG( heth->Instance->MTLTQOMR, ETH_MTLTQOMR_MASK, macconf->TransmitQueueMode ); + + /*------------------------ MTLRQOMR Configuration --------------------*/ + macregval = ( macconf->ReceiveQueueMode | + ( ( uint32_t ) ( ( macconf->DropTCPIPChecksumErrorPacket == DISABLE ) ? 1U : 0U ) << 6 ) | + ( ( uint32_t ) macconf->ForwardRxErrorPacket << 4 ) | + ( ( uint32_t ) macconf->ForwardRxUndersizedGoodPacket << 3 ) ); + + /* Write to MTLRQOMR */ + MODIFY_REG( heth->Instance->MTLRQOMR, ETH_MTLRQOMR_MASK, macregval ); + } + + static void ETH_SetDMAConfig( ETH_HandleTypeDef * heth, + ETH_DMAConfigTypeDef * dmaconf ) + { + uint32_t dmaregval; + + /*------------------------ DMAMR Configuration --------------------*/ + MODIFY_REG( heth->Instance->DMAMR, ETH_DMAMR_MASK, dmaconf->DMAArbitration ); + + /*------------------------ DMASBMR Configuration --------------------*/ + dmaregval = ( ( ( uint32_t ) dmaconf->AddressAlignedBeats << 12 ) | + dmaconf->BurstMode | + ( ( uint32_t ) dmaconf->RebuildINCRxBurst << 15 ) ); + + MODIFY_REG( heth->Instance->DMASBMR, ETH_DMASBMR_MASK, dmaregval ); + + /*------------------------ DMACCR Configuration --------------------*/ + dmaregval = ( ( ( uint32_t ) dmaconf->PBLx8Mode << 16 ) | + dmaconf->MaximumSegmentSize ); + + MODIFY_REG( heth->Instance->DMACCR, ETH_DMACCR_MASK, dmaregval ); + + /*------------------------ DMACTCR Configuration --------------------*/ + dmaregval = ( dmaconf->TxDMABurstLength | + ( ( uint32_t ) dmaconf->SecondPacketOperate << 4 ) | + ( ( uint32_t ) dmaconf->TCPSegmentation << 12 ) ); + + MODIFY_REG( heth->Instance->DMACTCR, ETH_DMACTCR_MASK, dmaregval ); + + /*------------------------ DMACRCR Configuration --------------------*/ + dmaregval = ( ( ( uint32_t ) dmaconf->FlushRxPacket << 31 ) | + dmaconf->RxDMABurstLength ); + + /* Write to DMACRCR */ + MODIFY_REG( heth->Instance->DMACRCR, ETH_DMACRCR_MASK, dmaregval ); + } + +/** + * @brief Configures Ethernet MAC and DMA with default parameters. + * called by HAL_ETH_Init() API. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval HAL status + */ + static void ETH_MACDMAConfig( ETH_HandleTypeDef * heth ) + { + ETH_MACConfigTypeDef macDefaultConf; + ETH_DMAConfigTypeDef dmaDefaultConf; + + /*--------------- ETHERNET MAC registers default Configuration --------------*/ + macDefaultConf.AutomaticPadCRCStrip = ENABLE; + macDefaultConf.BackOffLimit = ETH_BACKOFFLIMIT_10; + macDefaultConf.CarrierSenseBeforeTransmit = DISABLE; + macDefaultConf.CarrierSenseDuringTransmit = DISABLE; + macDefaultConf.ChecksumOffload = ENABLE; + macDefaultConf.CRCCheckingRxPackets = ENABLE; + macDefaultConf.CRCStripTypePacket = ENABLE; + macDefaultConf.DeferralCheck = DISABLE; + macDefaultConf.DropTCPIPChecksumErrorPacket = ENABLE; + macDefaultConf.DuplexMode = ETH_FULLDUPLEX_MODE; + macDefaultConf.ExtendedInterPacketGap = DISABLE; + macDefaultConf.ExtendedInterPacketGapVal = 0x0; + macDefaultConf.ForwardRxErrorPacket = DISABLE; + macDefaultConf.ForwardRxUndersizedGoodPacket = DISABLE; + macDefaultConf.GiantPacketSizeLimit = 0x618; + macDefaultConf.GiantPacketSizeLimitControl = DISABLE; + macDefaultConf.InterPacketGapVal = ETH_INTERPACKETGAP_96BIT; + macDefaultConf.Jabber = ENABLE; + macDefaultConf.JumboPacket = DISABLE; + macDefaultConf.LoopbackMode = DISABLE; + macDefaultConf.PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS_4; + macDefaultConf.PauseTime = 0x0; + macDefaultConf.PreambleLength = ETH_PREAMBLELENGTH_7; + macDefaultConf.ProgrammableWatchdog = DISABLE; + macDefaultConf.ReceiveFlowControl = DISABLE; + macDefaultConf.ReceiveOwn = ENABLE; + macDefaultConf.ReceiveQueueMode = ETH_RECEIVESTOREFORWARD; + macDefaultConf.RetryTransmission = ENABLE; + macDefaultConf.SlowProtocolDetect = DISABLE; + macDefaultConf.SourceAddrControl = ETH_SOURCEADDRESS_REPLACE_ADDR0; + macDefaultConf.Speed = ETH_SPEED_100M; + macDefaultConf.Support2KPacket = DISABLE; + macDefaultConf.TransmitQueueMode = ETH_TRANSMITSTOREFORWARD; + macDefaultConf.TransmitFlowControl = DISABLE; + macDefaultConf.UnicastPausePacketDetect = DISABLE; + macDefaultConf.UnicastSlowProtocolPacketDetect = DISABLE; + macDefaultConf.Watchdog = ENABLE; + macDefaultConf.WatchdogTimeout = ETH_MACWTR_WTO_2KB; + macDefaultConf.ZeroQuantaPause = ENABLE; + + /* MAC default configuration */ + ETH_SetMACConfig( heth, &macDefaultConf ); + + /*--------------- ETHERNET DMA registers default Configuration --------------*/ + dmaDefaultConf.AddressAlignedBeats = ENABLE; + dmaDefaultConf.BurstMode = ETH_BURSTLENGTH_FIXED; + dmaDefaultConf.DMAArbitration = ETH_DMAARBITRATION_RX1_TX1; + dmaDefaultConf.FlushRxPacket = DISABLE; + dmaDefaultConf.PBLx8Mode = DISABLE; + dmaDefaultConf.RebuildINCRxBurst = DISABLE; + dmaDefaultConf.RxDMABurstLength = ETH_RXDMABURSTLENGTH_32BEAT; + dmaDefaultConf.SecondPacketOperate = DISABLE; + dmaDefaultConf.TxDMABurstLength = ETH_TXDMABURSTLENGTH_32BEAT; + dmaDefaultConf.TCPSegmentation = DISABLE; + dmaDefaultConf.MaximumSegmentSize = 536; + + /* DMA default configuration */ + ETH_SetDMAConfig( heth, &dmaDefaultConf ); + } + +/** + * @brief Configures the Clock range of SMI interface. + * called by HAL_ETH_Init() API. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_MAC_MDIO_ClkConfig( ETH_HandleTypeDef * heth ) + { + uint32_t tmpreg, hclk; + + /* Get the ETHERNET MACMDIOAR value */ + tmpreg = ( heth->Instance )->MACMDIOAR; + + /* Clear CSR Clock Range bits */ + tmpreg &= ~ETH_MACMDIOAR_CR; + + /* Get hclk frequency value */ + hclk = HAL_RCC_GetHCLKFreq(); + + /* Set CR bits depending on hclk value */ + if( ( hclk >= 20000000U ) && ( hclk < 35000000U ) ) + { + /* CSR Clock Range between 20-35 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV16; + } + else if( ( hclk >= 35000000U ) && ( hclk < 60000000U ) ) + { + /* CSR Clock Range between 35-60 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV26; + } + else if( ( hclk >= 60000000U ) && ( hclk < 100000000U ) ) + { + /* CSR Clock Range between 60-100 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV42; + } + else if( ( hclk >= 100000000U ) && ( hclk < 150000000U ) ) + { + /* CSR Clock Range between 100-150 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV62; + } + else /* (hclk >= 150000000)&&(hclk <= 200000000) */ + { + /* CSR Clock Range between 150-200 MHz */ + tmpreg |= ( uint32_t ) ETH_MACMDIOAR_CR_DIV102; + } + + /* Configure the CSR Clock Range */ + ( heth->Instance )->MACMDIOAR = ( uint32_t ) tmpreg; + } + +/** + * @brief Initializes the DMA Tx descriptors. + * called by HAL_ETH_Init() API. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_DMATxDescListInit( ETH_HandleTypeDef * heth ) + { + ETH_DMADescTypeDef * dmatxdesc; + uint32_t i; + + /* Fill each DMATxDesc descriptor with the right values */ + for( i = 0; i < ( uint32_t ) ETH_TX_DESC_CNT; i++ ) + { + dmatxdesc = heth->Init.TxDesc + i; + + WRITE_REG( dmatxdesc->DESC0, 0x0 ); + WRITE_REG( dmatxdesc->DESC1, 0x0 ); + WRITE_REG( dmatxdesc->DESC2, 0x0 ); + WRITE_REG( dmatxdesc->DESC3, 0x0 ); + + WRITE_REG( heth->TxDescList.TxDesc[ i ], ( uint32_t ) dmatxdesc ); + } + + heth->TxDescList.CurTxDesc = 0; + heth->TxDescList.TailTxDesc = 0; + + /* Set Transmit Descriptor Ring Length */ + WRITE_REG( heth->Instance->DMACTDRLR, ( ETH_TX_DESC_CNT - 1 ) ); + + /* Set Transmit Descriptor List Address */ + /* Channel Tx descriptor list address register (ETH_DMACTXDLAR)). */ + WRITE_REG( heth->Instance->DMACTDLAR, ( uint32_t ) heth->Init.TxDesc ); + + /* Set Transmit Descriptor Tail pointer */ + WRITE_REG( heth->Instance->DMACTDTPR, ( uint32_t ) heth->Init.TxDesc ); + } + +/** + * @brief Initializes the DMA Rx descriptors in chain mode. + * called by HAL_ETH_Init() API. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @retval None + */ + static void ETH_DMARxDescListInit( ETH_HandleTypeDef * heth ) + { + ETH_DMADescTypeDef * dmarxdesc; + uint32_t i; + + for( i = 0; i < ( uint32_t ) ETH_RX_DESC_CNT; i++ ) + { + dmarxdesc = heth->Init.RxDesc + i; + + WRITE_REG( dmarxdesc->DESC0, 0x0 ); + WRITE_REG( dmarxdesc->DESC1, 0x0 ); + WRITE_REG( dmarxdesc->DESC2, 0x0 ); + WRITE_REG( dmarxdesc->DESC3, 0x0 ); + WRITE_REG( dmarxdesc->BackupAddr0, 0x0 ); + WRITE_REG( dmarxdesc->BackupAddr1, 0x0 ); + + /* Set Rx descritors adresses */ + WRITE_REG( heth->RxDescList.RxDesc[ i ], ( uint32_t ) dmarxdesc ); + } + + WRITE_REG( heth->RxDescList.CurRxDesc, 0 ); + WRITE_REG( heth->RxDescList.FirstAppDesc, 0 ); + WRITE_REG( heth->RxDescList.AppDescNbr, 0 ); + WRITE_REG( heth->RxDescList.ItMode, 0 ); + WRITE_REG( heth->RxDescList.AppContextDesc, 0 ); + + /* Set Receive Descriptor Ring Length */ + WRITE_REG( heth->Instance->DMACRDRLR, ( uint32_t ) ( ETH_RX_DESC_CNT - 1 ) ); + + /* Set Receive Descriptor List Address */ + /* Channel Rx descriptor list address register (ETH_DMACRXDLAR)). */ + WRITE_REG( heth->Instance->DMACRDLAR, ( uint32_t ) heth->Init.RxDesc ); + + /* Set Receive Descriptor Tail pointer Address */ + WRITE_REG( heth->Instance->DMACRDTPR, ( ( uint32_t ) ( heth->Init.RxDesc + ( uint32_t ) ( ETH_RX_DESC_CNT - 1 ) ) ) ); + } + + void ETH_Clear_Tx_Descriptors( ETH_HandleTypeDef * heth ) + { + uint32_t ulTailTxDesc = heth->TxDescList.TailTxDesc; + + while( ( uxSemaphoreGetCount( xTXDescriptorSemaphore ) ) != ETH_TX_DESC_CNT ) + { + ETH_DMADescTypeDef * xDMATxDescriptor = ( ETH_DMADescTypeDef * ) heth->TxDescList.TxDesc[ ulTailTxDesc ]; + + if( ( xDMATxDescriptor->DESC3 & ETH_DMATXNDESCRF_OWN ) != 0 ) + { + /* No buffer is assigned or DMA still OWNs this descriptor. */ + break; + } + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + NetworkBufferDescriptor_t * pxNetworkBuffer; + uint8_t * ucPayLoad; + + ucPayLoad = ( uint8_t * ) xDMATxDescriptor->DESC0; + + if( ucPayLoad == NULL ) + { + /* No buffer is assigned or DMA still OWNs this descriptor. */ + break; + } + + pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad ); + + if( pxNetworkBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + } + #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */ + + xDMATxDescriptor->DESC0 = ( uint32_t ) 0u; + + INCR_TX_DESC_INDEX( ulTailTxDesc, 1U ); + heth->TxDescList.TailTxDesc = ulTailTxDesc; + + __DSB(); + + xSemaphoreGive( xTXDescriptorSemaphore ); + } + } + +/** + * @brief Prepare Tx DMA descriptor before transmission. + * called by HAL_ETH_Transmit_IT and HAL_ETH_Transmit_IT() API. + * @param heth: pointer to a ETH_HandleTypeDef structure that contains + * the configuration information for ETHERNET module + * @param pTxConfig: Tx packet configuration + * @param ItMode: Enable or disable Tx EOT interrept + * @retval Status + */ + static uint32_t ETH_Prepare_Tx_Descriptors( ETH_HandleTypeDef * heth, + ETH_TxPacketConfig * pTxConfig, + uint32_t ItMode ) + { + ETH_TxDescListTypeDef * dmatxdesclist = &heth->TxDescList; + uint32_t firstdescidx = dmatxdesclist->CurTxDesc; + uint32_t DESC3; + ETH_DMADescTypeDef * dmatxdesc = ( ETH_DMADescTypeDef * ) dmatxdesclist->TxDesc[ firstdescidx ]; + ETH_BufferTypeDef * txbuffer = pTxConfig->TxBuffer; + + /* FreeRTOS+TCP doesn't support linked buffers. */ + txbuffer->next = NULL; + DESC3 = READ_REG( dmatxdesc->DESC3 ); + + /* Current TX Descriptor Owned by DMA: cannot be used by the application */ + if( READ_BIT( DESC3, ETH_DMATXNDESCWBF_OWN ) != 0U ) + { + /* Should not get here because TX descriptors are protected by a counting semaphore. */ + return HAL_ETH_ERROR_BUSY; + } + + /***************************************************************************/ + /***************** Normal descriptors configuration *****************/ + /***************************************************************************/ + + /* Set header or buffer 1 address */ + WRITE_REG( dmatxdesc->DESC0, ( uint32_t ) txbuffer->buffer ); + /* Set header or buffer 1 Length */ + MODIFY_REG( dmatxdesc->DESC2, ETH_DMATXNDESCRF_B1L, txbuffer->len ); + + WRITE_REG( dmatxdesc->DESC1, 0x0 ); + /* Set buffer 2 Length to zero */ + MODIFY_REG( dmatxdesc->DESC2, ETH_DMATXNDESCRF_B2L, 0x0U ); + + MODIFY_REG( DESC3, ETH_DMATXNDESCRF_FL, pTxConfig->Length ); + + if( READ_BIT( pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_CSUM ) != 0U ) + { + MODIFY_REG( DESC3, ETH_DMATXNDESCRF_CIC, pTxConfig->ChecksumCtrl ); + } + + if( READ_BIT( pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_CRCPAD ) != 0U ) + { + MODIFY_REG( DESC3, ETH_DMATXNDESCRF_CPC, pTxConfig->CRCPadCtrl ); + } + + /* Mark it as First and the last Descriptor */ + SET_BIT( DESC3, ETH_DMATXNDESCRF_FD | ETH_DMATXNDESCRF_LD ); + + /* Mark it as NORMAL descriptor */ + CLEAR_BIT( DESC3, ETH_DMATXNDESCRF_CTXT ); + + /* set OWN bit of FIRST descriptor */ + SET_BIT( DESC3, ETH_DMATXNDESCRF_OWN ); + + if( ItMode != ( ( uint32_t ) RESET ) ) + { + /* Set Interrupt on competition bit */ + SET_BIT( dmatxdesc->DESC2, ETH_DMATXNDESCRF_IOC ); + } + else + { + /* Clear Interrupt on competition bit */ + CLEAR_BIT( dmatxdesc->DESC2, ETH_DMATXNDESCRF_IOC ); + } + + WRITE_REG( dmatxdesc->DESC3, DESC3 ); + + /* Read back the value. */ + if( READ_REG( dmatxdesc->DESC3 ) ) + { + } + + __DSB(); + + /* Return function status */ + return HAL_ETH_ERROR_NONE; + } + + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + static void ETH_InitCallbacksToDefault( ETH_HandleTypeDef * heth ) + { + /* Init the ETH Callback settings */ + heth->TxCpltCallback = HAL_ETH_TxCpltCallback; /* Legacy weak TxCpltCallback */ + heth->RxCpltCallback = HAL_ETH_RxCpltCallback; /* Legacy weak RxCpltCallback */ + heth->DMAErrorCallback = HAL_ETH_DMAErrorCallback; /* Legacy weak DMAErrorCallback */ + heth->MACErrorCallback = HAL_ETH_MACErrorCallback; /* Legacy weak MACErrorCallback */ + heth->PMTCallback = HAL_ETH_PMTCallback; /* Legacy weak PMTCallback */ + heth->EEECallback = HAL_ETH_EEECallback; /* Legacy weak EEECallback */ + heth->WakeUpCallback = HAL_ETH_WakeUpCallback; /* Legacy weak WakeUpCallback */ + } + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + +/** + * @} + */ + +/** + * @} + */ + + #endif /* ETH */ + +#endif /* HAL_ETH_MODULE_ENABLED */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/stm32hxx_hal_eth.h b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/stm32hxx_hal_eth.h new file mode 100644 index 0000000..749dd26 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/STM32Hxx/stm32hxx_hal_eth.h @@ -0,0 +1,1877 @@ +/** + ****************************************************************************** + * @file stm32hxx_hal_eth.h + * @author MCD Application Team + * @brief Header file of ETH HAL module. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2017 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32Hxx_HAL_ETH_H + #define STM32Hxx_HAL_ETH_H + + #define STM32H7xx_HAL_ETH_H + + #ifdef __cplusplus + extern "C" { + #endif + + +/* Includes ------------------------------------------------------------------*/ + #include "stm32h7xx_hal_def.h" + + #if defined( ETH ) + +/** @addtogroup STM32H7xx_HAL_Driver + * @{ + */ + +/** @addtogroup ETH + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ + #ifndef ETH_TX_DESC_CNT + #error Please define ETH_TX_DESC_CNT in your stm32h7xx_hal_conf.h + #endif + + #ifndef ETH_RX_DESC_CNT + #error Please define ETH_RX_DESC_CNT in your stm32h7xx_hal_conf.h + #endif + +/*********************** Descriptors struct def section ************************/ + +/** @defgroup ETH_Exported_Types ETH Exported Types + * @{ + */ + struct xErrorFields + { + uint16_t + ERR_IHE : 1, /* IP Header Error */ + ERR_DB : 1, /* Deferred Bit */ + ERR_Umderflow : 1, /* Underflow Error */ + ERR_ExcDefer : 1, /* Excessive Deferral */ + ERR_CC : 4, /* Collision count. */ + ERR_EC : 1, /* Excessive Collision */ + ERR_LC : 1, /* Late collision. */ + ERR_NC : 1, /* No carrier. */ + ERR_LoC : 1, /* Loss of Carrier: carrier lost during transmission */ + ERR_PCE : 1, /* Payload Checksum Error */ + ERR_FF : 1, /* Packet Flushed: DMA/MTL flushed the packet due to SW flush */ + ERR_JT : 1, /* Jabber Timeout */ + ERR_SUMMARY : 1; + } + __attribute__( ( packed ) ); + +/** + * @brief ETH DMA Descriptor structure definition + */ + typedef struct + { + union + { + __IO uint32_t DESC0; /* The buffer */ + uint32_t Buffer_1____; + }; + union + { + __IO uint32_t DESC1; + uint32_t Buffer_2____; + }; + union + { + __IO uint32_t DESC2; /* Buffer 1 length (0x00003FFFU) Buffer 2 Length (0x3FFF0000) */ + struct + { + unsigned + Buff1_Length : 14, /* Buffer 1 Length */ + VTIR________ : 2, /* VLAN Tag Insertion or Replacement mask */ + Buff2_Length : 14, /* Buffer 2 Length */ + TTSE________ : 1, /* Transmit Timestamp Enable */ + IOC_________ : 1; /* Interrupt on Completion */ + }; + }; + union + { + __IO uint32_t DESC3; /* bit 31 is the OWN (by DMA) bit */ + struct + { + struct xErrorFields ERR_FIELDS; + struct + { + uint16_t + + Reserved_1__ : 1, + TIMESTAMP___ : 1, /*!< Tx Timestamp Status */ + Reserved_3__ : 10, + LAST_DESC___ : 1, /*!< Last Descriptor */ + FIRST_DESC__ : 1, /*!< First Descriptor */ + STATUS_CTX__ : 1, /*!< Context Type */ + OWN_________ : 1; + } __attribute__( ( packed ) ); + }; + }; + uint32_t BackupAddr0; /* used to store rx buffer 1 address */ + uint32_t BackupAddr1; /* used to store rx buffer 2 address */ + } ETH_DMADescTypeDef; + +/* + * Channel status register ( see field DMACSR, or "ETH_DMACSR". + */ + typedef struct + { + union + { + uint32_t ulValue; + struct + { + uint32_t + TI_Transmit_Interrupt__________ : 1, + TPS_Transmit_Process_Stopped___ : 1, + TBU_Transmit_Buffer_Unavailable : 1, + R_0 : 3, + RI_Receive_Interrupt___________ : 1, + RBU_Receive_Buffer_Unavailable_ : 1, + RPS_Receive_Process_Stopped____ : 1, + RWT_Receive_Watchdog_Timeout___ : 1, + ETI_Early_Transmit_Interrupt___ : 1, + ERI_Early_Receive_Interrupt____ : 1, + FBE_Fatal_Bus_Error____________ : 1, + CDE_Context_Descriptor_Error___ : 1, + AIS_Abnormal_Interrupt_Summary_ : 1, + NIS_Normal_Interrupt_Summary___ : 1, + + REB_0_Error_during_read_transfer_when_1__________ : 1, + REB_1_Error_during_descriptor_access_when_1______ : 1, + REB_2_Error_during_data_transfer_by_Rx_DMA_when_1 : 1, + + TEB_0_Error_during_read_transfer_when_1__________ : 1, + TEB_1_Error_during_descriptor_access_when_1______ : 1, + TEB_2_Error_during_data_transfer_by_Tx_DMA_when_1 : 1, + + R_1 : 10; + }; + }; + } IntStatus_t; + +/** + * + */ + +/** + * @brief ETH Buffers List structure definition + */ + typedef struct __ETH_BufferTypeDef + { + uint8_t * buffer; /*gState = HAL_ETH_STATE_RESET; \ + ( __HANDLE__ )->RxState = HAL_ETH_STATE_RESET; \ + ( __HANDLE__ )->MspInitCallback = NULL; \ + ( __HANDLE__ )->MspDeInitCallback = NULL; \ + } while( 0 ) + #else + #define __HAL_ETH_RESET_HANDLE_STATE( __HANDLE__ ) \ + do { \ + ( __HANDLE__ )->gState = HAL_ETH_STATE_RESET; \ + ( __HANDLE__ )->RxState = HAL_ETH_STATE_RESET; \ + } while( 0 ) + #endif /*USE_HAL_ETH_REGISTER_CALLBACKS */ + +/** + * @brief Enables the specified ETHERNET DMA interrupts. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the ETHERNET DMA interrupt sources to be + * enabled @ref ETH_DMA_Interrupts + * @retval None + */ + #define __HAL_ETH_DMA_ENABLE_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->DMACIER |= ( __INTERRUPT__ ) ) + +/** + * @brief Disables the specified ETHERNET DMA interrupts. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the ETHERNET DMA interrupt sources to be + * disabled. @ref ETH_DMA_Interrupts + * @retval None + */ + #define __HAL_ETH_DMA_DISABLE_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->DMACIER &= ~( __INTERRUPT__ ) ) + +/** + * @brief Gets the ETHERNET DMA IT source enabled or disabled. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the interrupt source to get . @ref ETH_DMA_Interrupts + * @retval The ETH DMA IT Source enabled or disabled + */ + #define __HAL_ETH_DMA_GET_IT_SOURCE( __HANDLE__, __INTERRUPT__ ) ( ( ( __HANDLE__ )->Instance->DMACIER & ( __INTERRUPT__ ) ) == ( __INTERRUPT__ ) ) + +/** + * @brief Gets the ETHERNET DMA IT pending bit. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the interrupt source to get . @ref ETH_DMA_Interrupts + * @retval The state of ETH DMA IT (SET or RESET) + */ + #define __HAL_ETH_DMA_GET_IT( __HANDLE__, __INTERRUPT__ ) ( ( ( __HANDLE__ )->Instance->DMACSR & ( __INTERRUPT__ ) ) == ( __INTERRUPT__ ) ) + +/** + * @brief Clears the ETHERNET DMA IT pending bit. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the interrupt pending bit to clear. @ref ETH_DMA_Interrupts + * @retval None + */ + #define __HAL_ETH_DMA_CLEAR_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->DMACSR = ( __INTERRUPT__ ) ) + +/** + * @brief Checks whether the specified ETHERNET DMA flag is set or not. + * @param __HANDLE__: ETH Handle + * @param __FLAG__: specifies the flag to check. @ref ETH_DMA_Status_Flags + * @retval The state of ETH DMA FLAG (SET or RESET). + */ + #define __HAL_ETH_DMA_GET_FLAG( __HANDLE__, __FLAG__ ) ( ( ( __HANDLE__ )->Instance->DMACSR & ( __FLAG__ ) ) == ( __FLAG__ ) ) + +/** + * @brief Clears the specified ETHERNET DMA flag. + * @param __HANDLE__: ETH Handle + * @param __FLAG__: specifies the flag to check. @ref ETH_DMA_Status_Flags + * @retval The state of ETH DMA FLAG (SET or RESET). + */ + #define __HAL_ETH_DMA_CLEAR_FLAG( __HANDLE__, __FLAG__ ) ( ( __HANDLE__ )->Instance->DMACSR = ( __FLAG__ ) ) + +/** + * @brief Enables the specified ETHERNET MAC interrupts. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the ETHERNET MAC interrupt sources to be + * enabled @ref ETH_MAC_Interrupts + * @retval None + */ + #define __HAL_ETH_MAC_ENABLE_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->MACIER |= ( __INTERRUPT__ ) ) + +/** + * @brief Disables the specified ETHERNET MAC interrupts. + * @param __HANDLE__ : ETH Handle + * @param __INTERRUPT__: specifies the ETHERNET MAC interrupt sources to be + * enabled @ref ETH_MAC_Interrupts + * @retval None + */ + #define __HAL_ETH_MAC_DISABLE_IT( __HANDLE__, __INTERRUPT__ ) ( ( __HANDLE__ )->Instance->MACIER &= ~( __INTERRUPT__ ) ) + +/** + * @brief Checks whether the specified ETHERNET MAC flag is set or not. + * @param __HANDLE__: ETH Handle + * @param __INTERRUPT__: specifies the flag to check. @ref ETH_MAC_Interrupts + * @retval The state of ETH MAC IT (SET or RESET). + */ + #define __HAL_ETH_MAC_GET_IT( __HANDLE__, __INTERRUPT__ ) ( ( ( __HANDLE__ )->Instance->MACISR & ( __INTERRUPT__ ) ) == ( __INTERRUPT__ ) ) + +/*!< External interrupt line 86 Connected to the ETH wakeup EXTI Line */ + #define ETH_WAKEUP_EXTI_LINE ( ( uint32_t ) 0x00400000U ) /* !< 86 - 64 = 22 */ + +/** + * @brief Enable the ETH WAKEUP Exti Line. + * @param __EXTI_LINE__: specifies the ETH WAKEUP Exti sources to be enabled. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval None. + */ + #define __HAL_ETH_WAKEUP_EXTI_ENABLE_IT( __EXTI_LINE__ ) ( EXTI_D1->IMR3 |= ( __EXTI_LINE__ ) ) + +/** + * @brief checks whether the specified ETH WAKEUP Exti interrupt flag is set or not. + * @param __EXTI_LINE__: specifies the ETH WAKEUP Exti sources to be cleared. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval EXTI ETH WAKEUP Line Status. + */ + #define __HAL_ETH_WAKEUP_EXTI_GET_FLAG( __EXTI_LINE__ ) ( EXTI_D1->PR3 & ( __EXTI_LINE__ ) ) + +/** + * @brief Clear the ETH WAKEUP Exti flag. + * @param __EXTI_LINE__: specifies the ETH WAKEUP Exti sources to be cleared. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval None. + */ + #define __HAL_ETH_WAKEUP_EXTI_CLEAR_FLAG( __EXTI_LINE__ ) ( EXTI_D1->PR3 = ( __EXTI_LINE__ ) ) + + #if defined( DUAL_CORE ) + +/** + * @brief Enable the ETH WAKEUP Exti Line by Core2. + * @param __EXTI_LINE__: specifies the ETH WAKEUP Exti sources to be enabled. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval None. + */ + #define __HAL_ETH_WAKEUP_EXTID2_ENABLE_IT( __EXTI_LINE__ ) ( EXTI_D2->IMR3 |= ( __EXTI_LINE__ ) ) + +/** + * @brief checks whether the specified ETH WAKEUP Exti interrupt flag is set or not. + * @param __EXTI_LINE__: specifies the ETH WAKEUP Exti sources to be cleared. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval EXTI ETH WAKEUP Line Status. + */ + #define __HAL_ETH_WAKEUP_EXTID2_GET_FLAG( __EXTI_LINE__ ) ( EXTI_D2->PR3 & ( __EXTI_LINE__ ) ) + +/** + * @brief Clear the ETH WAKEUP Exti flag. + * @param __EXTI_LINE__: specifies the ETH WAKEUP Exti sources to be cleared. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval None. + */ + #define __HAL_ETH_WAKEUP_EXTID2_CLEAR_FLAG( __EXTI_LINE__ ) ( EXTI_D2->PR3 = ( __EXTI_LINE__ ) ) + #endif + +/** + * @brief enable rising edge interrupt on selected EXTI line. + * @param __EXTI_LINE__: specifies the ETH WAKEUP EXTI sources to be disabled. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_ENABLE_RISING_EDGE( __EXTI_LINE__ ) \ + ( EXTI->FTSR3 &= ~( __EXTI_LINE__ ) ); \ + ( EXTI->RTSR3 |= ( __EXTI_LINE__ ) ) + +/** + * @brief enable falling edge interrupt on selected EXTI line. + * @param __EXTI_LINE__: specifies the ETH WAKEUP EXTI sources to be disabled. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_ENABLE_FALLING_EDGE( __EXTI_LINE__ ) \ + ( EXTI->RTSR3 &= ~( __EXTI_LINE__ ) ); \ + ( EXTI->FTSR3 |= ( __EXTI_LINE__ ) ) + +/** + * @brief enable falling edge interrupt on selected EXTI line. + * @param __EXTI_LINE__: specifies the ETH WAKEUP EXTI sources to be disabled. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_ENABLE_RISING_FALLING_EDGE( __EXTI_LINE__ ) \ + ( EXTI->RTSR3 |= ( __EXTI_LINE__ ) ); \ + ( EXTI->FTSR3 |= ( __EXTI_LINE__ ) ) + +/** + * @brief Generates a Software interrupt on selected EXTI line. + * @param __EXTI_LINE__: specifies the ETH WAKEUP EXTI sources to be disabled. + * @arg ETH_WAKEUP_EXTI_LINE + * @retval None + */ + #define __HAL_ETH_WAKEUP_EXTI_GENERATE_SWIT( __EXTI_LINE__ ) ( EXTI->SWIER3 |= ( __EXTI_LINE__ ) ) + +/** + * @} + */ + +/* Include ETH HAL Extension module */ + #include "stm32h7xx_hal_eth_ex.h" + +/* Exported functions --------------------------------------------------------*/ + +/** @addtogroup ETH_Exported_Functions + * @{ + */ + +/** @addtogroup ETH_Exported_Functions_Group1 + * @{ + */ +/* Initialization and de initialization functions **********************************/ + HAL_StatusTypeDef HAL_ETH_Init( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_DeInit( ETH_HandleTypeDef * heth ); + void HAL_ETH_MspInit( ETH_HandleTypeDef * heth ); + void HAL_ETH_MspDeInit( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_DescAssignMemory( ETH_HandleTypeDef * heth, + uint32_t Index, + uint8_t * pBuffer1, + uint8_t * pBuffer2 ); + +/* Callbacks Register/UnRegister functions ***********************************/ + #if ( USE_HAL_ETH_REGISTER_CALLBACKS == 1 ) + HAL_StatusTypeDef HAL_ETH_RegisterCallback( ETH_HandleTypeDef * heth, + HAL_ETH_CallbackIDTypeDef CallbackID, + pETH_CallbackTypeDef pCallback ); + HAL_StatusTypeDef HAL_ETH_UnRegisterCallback( ETH_HandleTypeDef * heth, + HAL_ETH_CallbackIDTypeDef CallbackID ); + #endif /* USE_HAL_ETH_REGISTER_CALLBACKS */ + +/** + * @} + */ + +/** @addtogroup ETH_Exported_Functions_Group2 + * @{ + */ +/* IO operation functions *******************************************************/ + HAL_StatusTypeDef HAL_ETH_Start( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_Start_IT( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_Stop( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_Stop_IT( ETH_HandleTypeDef * heth ); + + uint8_t HAL_ETH_IsRxDataAvailable( ETH_HandleTypeDef * heth ); +/* The following 2 functions are replaced with a single function: HAL_ETH_GetRxData(). */ +/* HAL_StatusTypeDef HAL_ETH_GetRxDataBuffer(ETH_HandleTypeDef *heth, ETH_BufferTypeDef *RxBuffer); */ +/* HAL_StatusTypeDef HAL_ETH_GetRxDataLength(ETH_HandleTypeDef *heth, uint32_t *Length); */ + + size_t HAL_ETH_GetRxData( ETH_HandleTypeDef * heth, + ETH_BufferTypeDef * RxBuffer ); + + HAL_StatusTypeDef HAL_ETH_GetRxDataInfo( ETH_HandleTypeDef * heth, + ETH_RxPacketInfo * RxPacketInfo ); + HAL_StatusTypeDef HAL_ETH_BuildRxDescriptors( ETH_HandleTypeDef * heth, + uint8_t * pucNewBuffer ); + + HAL_StatusTypeDef HAL_ETH_Transmit( ETH_HandleTypeDef * heth, + ETH_TxPacketConfig * pTxConfig, + uint32_t Timeout ); + HAL_StatusTypeDef HAL_ETH_Transmit_IT( ETH_HandleTypeDef * heth, + ETH_TxPacketConfig * pTxConfig ); + + void ETH_Clear_Tx_Descriptors( ETH_HandleTypeDef * heth ); + + + HAL_StatusTypeDef HAL_ETH_WritePHYRegister( ETH_HandleTypeDef * heth, + uint32_t PHYAddr, + uint32_t PHYReg, + uint32_t RegValue ); + HAL_StatusTypeDef HAL_ETH_ReadPHYRegister( ETH_HandleTypeDef * heth, + uint32_t PHYAddr, + uint32_t PHYReg, + uint32_t * pRegValue ); + + void HAL_ETH_IRQHandler( ETH_HandleTypeDef * heth ); + void HAL_ETH_TxCpltCallback( ETH_HandleTypeDef * heth ); + void HAL_ETH_RxCpltCallback( ETH_HandleTypeDef * heth ); + void HAL_ETH_DMAErrorCallback( ETH_HandleTypeDef * heth ); + void HAL_ETH_MACErrorCallback( ETH_HandleTypeDef * heth ); + void HAL_ETH_PMTCallback( ETH_HandleTypeDef * heth ); + void HAL_ETH_EEECallback( ETH_HandleTypeDef * heth ); + void HAL_ETH_WakeUpCallback( ETH_HandleTypeDef * heth ); + +/** + * @} + */ + +/** @addtogroup ETH_Exported_Functions_Group3 + * @{ + */ +/* Peripheral Control functions **********************************************/ +/* MAC & DMA Configuration APIs **********************************************/ + HAL_StatusTypeDef HAL_ETH_GetMACConfig( ETH_HandleTypeDef * heth, + ETH_MACConfigTypeDef * macconf ); + HAL_StatusTypeDef HAL_ETH_GetDMAConfig( ETH_HandleTypeDef * heth, + ETH_DMAConfigTypeDef * dmaconf ); + HAL_StatusTypeDef HAL_ETH_SetMACConfig( ETH_HandleTypeDef * heth, + ETH_MACConfigTypeDef * macconf ); + HAL_StatusTypeDef HAL_ETH_SetDMAConfig( ETH_HandleTypeDef * heth, + ETH_DMAConfigTypeDef * dmaconf ); + void HAL_ETH_SetMDIOClockRange( ETH_HandleTypeDef * heth ); + +/* MAC VLAN Processing APIs ************************************************/ + void HAL_ETH_SetRxVLANIdentifier( ETH_HandleTypeDef * heth, + uint32_t ComparisonBits, + uint32_t VLANIdentifier ); + +/* MAC L2 Packet Filtering APIs **********************************************/ + HAL_StatusTypeDef HAL_ETH_GetMACFilterConfig( ETH_HandleTypeDef * heth, + ETH_MACFilterConfigTypeDef * pFilterConfig ); + HAL_StatusTypeDef HAL_ETH_SetMACFilterConfig( ETH_HandleTypeDef * heth, + ETH_MACFilterConfigTypeDef * pFilterConfig ); + HAL_StatusTypeDef HAL_ETH_SetHashTable( ETH_HandleTypeDef * heth, + uint32_t * pHashTable ); + HAL_StatusTypeDef HAL_ETH_SetSourceMACAddrMatch( ETH_HandleTypeDef * heth, + uint32_t AddrNbr, + uint8_t * pMACAddr ); + +/* MAC Power Down APIs *****************************************************/ + void HAL_ETH_EnterPowerDownMode( ETH_HandleTypeDef * heth, + ETH_PowerDownConfigTypeDef * pPowerDownConfig ); + void HAL_ETH_ExitPowerDownMode( ETH_HandleTypeDef * heth ); + HAL_StatusTypeDef HAL_ETH_SetWakeUpFilter( ETH_HandleTypeDef * heth, + uint32_t * pFilter, + uint32_t Count ); + +/** + * @} + */ + +/** @addtogroup ETH_Exported_Functions_Group4 + * @{ + */ +/* Peripheral State functions **************************************************/ + HAL_ETH_StateTypeDef HAL_ETH_GetState( ETH_HandleTypeDef * heth ); + uint32_t HAL_ETH_GetError( ETH_HandleTypeDef * heth ); + uint32_t HAL_ETH_GetDMAError( ETH_HandleTypeDef * heth ); + uint32_t HAL_ETH_GetMACError( ETH_HandleTypeDef * heth ); + uint32_t HAL_ETH_GetMACWakeUpSource( ETH_HandleTypeDef * heth ); + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + + #endif /* ETH */ + + #ifdef __cplusplus + } + #endif + +#endif /* STM32Hxx_HAL_ETH_H */ + + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/FreeRTOS/source/portable/NetworkInterface/TM4C/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/TM4C/NetworkInterface.c new file mode 100644 index 0000000..a3f37c3 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/TM4C/NetworkInterface.c @@ -0,0 +1,793 @@ +/** + * @file: NetworkInterface.c + * @author: jscott + * @date: Feb 1, 2022 + * @copyright: Hotstart 2022 Hotstart Thermal Management. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @brief:Network Interface driver for the Texas Instruments TM4C line of microcontrollers. + * + * This driver was written and tested with the TM4C1294NCPDT, which includes a built-in MAC and + * PHY. The expectation is that this driver should function correctly across all the MAC/PHY + * integrated parts of the TM4C129X parts. + */ + +#include +#include +#include +#include + +#include "inc/hw_ints.h" +#include "inc/hw_emac.h" +#include "inc/hw_memmap.h" +#include "inc/hw_nvic.h" + +#include "driverlib/flash.h" +#include "driverlib/interrupt.h" +#include "driverlib/gpio.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/systick.h" +#include "driverlib/emac.h" + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" +#include "phyHandling.h" + +#define BUFFER_SIZE ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING ) +#define BUFFER_SIZE_ROUNDED_UP ( ( BUFFER_SIZE + 7 ) & ~0x7UL ) +#define PHY_PHYS_ADDR 0 + +#ifndef niEMAC_SYSCONFIG_HZ + #define niEMAC_SYSCONFIG_HZ configCPU_CLOCK_HZ +#endif + +#ifndef niEMAC_TX_DMA_DESC_COUNT + #define niEMAC_TX_DMA_DESC_COUNT 8 +#endif + +#ifndef niEMAC_RX_DMA_DESC_COUNT + #define niEMAC_RX_DMA_DESC_COUNT 8 +#endif + +#if ipconfigUSE_LINKED_RX_MESSAGES + #error Linked RX Messages are not supported by this driver +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to twice + * the size of the stack used by the idle task - but allow this to be overridden in + * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE ) +#endif + +#ifndef niEMAC_HANDLER_TASK_PRIORITY + #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1 +#endif + +#if !defined( ipconfigETHERNET_AN_ENABLE ) + /* Enable auto-negotiation */ + #define ipconfigETHERNET_AN_ENABLE 1 +#endif + +#if !defined( ipconfigETHERNET_USE_100MB ) + #define ipconfigETHERNET_USE_100MB 1 +#endif + +#if !defined( ipconfigETHERNET_USE_FULL_DUPLEX ) + #define ipconfigETHERNET_USE_FULL_DUPLEX 1 +#endif + +typedef struct +{ + uint32_t number_descriptors; + uint32_t write; + uint32_t read; +} tDescriptorList; + +typedef enum +{ + eMACInit, /* Must initialise MAC. */ + eMACPass, /* Initialisation was successful. */ + eMACFailed, /* Initialisation failed. */ +} eMAC_INIT_STATUS_TYPE; + +typedef enum +{ + eMACInterruptNone = 0, /* No interrupts need servicing */ + eMACInterruptRx = ( 1 << 0 ), /* Service RX interrupt */ + eMACInterruptTx = ( 1 << 1 ), /* Service TX interrupt */ +} eMAC_INTERRUPT_STATUS_TYPE; + +static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit; + +static volatile eMAC_INTERRUPT_STATUS_TYPE xMacInterruptStatus = eMACInterruptNone; + +static tEMACDMADescriptor _tx_descriptors[ niEMAC_TX_DMA_DESC_COUNT ]; +static tEMACDMADescriptor _rx_descriptors[ niEMAC_RX_DMA_DESC_COUNT ]; + +static tDescriptorList _tx_descriptor_list = { .number_descriptors = niEMAC_TX_DMA_DESC_COUNT, 0 }; +static tDescriptorList _rx_descriptor_list = { .number_descriptors = niEMAC_RX_DMA_DESC_COUNT, 0 }; + +static uint8_t _network_buffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ BUFFER_SIZE_ROUNDED_UP ]; +#pragma DATA_ALIGN(_network_buffers, 4) + +static EthernetPhy_t xPhyObject; + +static TaskHandle_t _deferred_task_handle = NULL; + +const PhyProperties_t xPHYProperties = +{ + #if ( ipconfigETHERNET_AN_ENABLE != 0 ) + .ucSpeed = PHY_SPEED_AUTO, + .ucDuplex = PHY_DUPLEX_AUTO, + #else + #if ( ipconfigETHERNET_USE_100MB != 0 ) + .ucSpeed = PHY_SPEED_100, + #else + .ucSpeed = PHY_SPEED_10, + #endif + + #if ( ipconfigETHERNET_USE_FULL_DUPLEX != 0 ) + .ucDuplex = PHY_DUPLEX_FULL, + #else + .ucDuplex = PHY_DUPLEX_HALF, + #endif + #endif /* if ( ipconfigETHERNET_AN_ENABLE != 0 ) */ +}; + +/** + * Reads the Ethernet MAC from user Flash. + * @param mac_address_bytes[out] The byte array which will hold the MAC address + * @return pdPASS on success, pdFAIL if the MAC is invalid from user Flash + */ +static BaseType_t _ethernet_mac_get( uint8_t * mac_address_bytes ); + +/** + * Initialize DMA descriptors + */ +static void _dma_descriptors_init( void ); + +/** + * Frees previously sent network buffers + */ +static void _process_transmit_complete( void ); + +/** + * Processes received packets and forwards those acceptable to the network stack + */ +static BaseType_t _process_received_packet( void ); + +/** + * Processes PHY interrupts. + */ +static void _process_phy_interrupts( void ); + +/** + * Thread to forward received packets from the ISR to the network stack + * @param parameters Not used + */ +static void _deferred_task( void * parameters ); + +/** + * Phy read implementation for the TM4C + * @param xAddress + * @param xRegister + * @param pulValue + * @return + */ +static BaseType_t xTM4C_PhyRead( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t * pulValue ); + +/** + * Phy write implementation for the TM4C + * @param xAddress + * @param xRegister + * @param ulValue + * @return + */ +static BaseType_t xTM4C_PhyWrite( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t ulValue ); + +/** + * Probe the PHY + */ +static void vMACBProbePhy( void ); + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + uint8_t mac_address_bytes[ 6 ]; + uint16_t ui16Val; + BaseType_t xResult = pdFAIL; + + if( eMACInit == xMacInitStatus ) + { + /* Create the RX packet forwarding task */ + if( pdFAIL == xTaskCreate( _deferred_task, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &_deferred_task_handle ) ) + { + xMacInitStatus = eMACFailed; + } + else + { + /* Read the MAC from user Flash */ + if( pdPASS != _ethernet_mac_get( &mac_address_bytes[ 0 ] ) ) + { + xMacInitStatus = eMACFailed; + } + else + { + MAP_SysCtlPeripheralReset( SYSCTL_PERIPH_EMAC0 ); + + while( !MAP_SysCtlPeripheralReady( SYSCTL_PERIPH_EMAC0 ) ) + { + } + + MAP_SysCtlPeripheralReset( SYSCTL_PERIPH_EPHY0 ); + + while( !MAP_SysCtlPeripheralReady( SYSCTL_PERIPH_EPHY0 ) ) + { + } + + MAP_EMACInit( EMAC0_BASE, niEMAC_SYSCONFIG_HZ, + EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, 4, + 4, 0 ); + + MAP_EMACConfigSet( + EMAC0_BASE, + ( + EMAC_CONFIG_100MBPS | + EMAC_CONFIG_FULL_DUPLEX | + EMAC_CONFIG_CHECKSUM_OFFLOAD | + EMAC_CONFIG_7BYTE_PREAMBLE | + EMAC_CONFIG_IF_GAP_96BITS | + EMAC_CONFIG_USE_MACADDR0 | + EMAC_CONFIG_SA_FROM_DESCRIPTOR | + EMAC_CONFIG_BO_LIMIT_1024 | + EMAC_CONFIG_STRIP_CRC + ), + ( + EMAC_MODE_RX_STORE_FORWARD | + EMAC_MODE_TX_STORE_FORWARD | + EMAC_MODE_RX_THRESHOLD_64_BYTES | + EMAC_MODE_TX_THRESHOLD_64_BYTES ), + 0 ); + + + /* Clear any stray MISR1 PHY interrupts that may be set. */ + ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1 ); + /* Enable link status change interrupts */ + ui16Val |= + ( EPHY_MISR1_LINKSTATEN | + EPHY_MISR1_SPEEDEN | + EPHY_MISR1_DUPLEXMEN | + EPHY_MISR1_ANCEN + ); + MAP_EMACPHYWrite( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1, ui16Val ); + + /* Clear any stray MISR2 PHY interrupts that may be set. */ + ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR2 ); + + /* Configure and enable PHY interrupts */ + ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_SCR ); + ui16Val |= ( EPHY_SCR_INTEN_EXT | EPHY_SCR_INTOE_EXT ); + MAP_EMACPHYWrite( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_SCR, ui16Val ); + + /* Read the PHY interrupt status to clear any stray events. */ + ui16Val = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1 ); + + /* Set MAC filtering options. We receive all broadcast and mui32ticast */ + /* packets along with those addressed specifically for us. */ + MAP_EMACFrameFilterSet( EMAC0_BASE, ( EMAC_FRMFILTER_HASH_AND_PERFECT | + EMAC_FRMFILTER_PASS_MULTICAST ) ); + + /* Set the MAC address */ + MAP_EMACAddrSet( EMAC0_BASE, 0, &mac_address_bytes[ 0 ] ); + + /* Clears any previously asserted interrupts */ + MAP_EMACIntClear( EMAC0_BASE, EMACIntStatus( EMAC0_BASE, false ) ); + + /* Initialize the DMA descriptors */ + _dma_descriptors_init(); + + /* Enable TX/RX */ + MAP_EMACTxEnable( EMAC0_BASE ); + MAP_EMACRxEnable( EMAC0_BASE ); + + /* Set the interrupt to a lower priority than the OS scheduler interrupts */ + MAP_IntPrioritySet( INT_EMAC0, ( 6 << ( 8 - configPRIO_BITS ) ) ); + + /* Probe the PHY with the stack driver */ + vMACBProbePhy(); + + xMacInitStatus = eMACPass; + } + } + } + + if( eMACPass == xMacInitStatus ) + { + /* Wait for the link status to come up before enabling interrupts */ + if( xPhyObject.ulLinkStatusMask != 0U ) + { + /* Enable the Ethernet RX and TX interrupt source. */ + MAP_EMACIntEnable( EMAC0_BASE, ( EMAC_INT_RECEIVE | EMAC_INT_TRANSMIT | + EMAC_INT_TX_STOPPED | EMAC_INT_RX_NO_BUFFER | + EMAC_INT_RX_STOPPED | EMAC_INT_PHY ) ); + + /* Enable EMAC interrupts */ + MAP_IntEnable( INT_EMAC0 ); + + xResult = pdPASS; + } + } + + return xResult; +} + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t xReleaseAfterSend ) +{ + BaseType_t success = pdTRUE; + tEMACDMADescriptor * dma_descriptor; + + /* As this driver is strictly zero-copy, assert that the stack does not call this function with */ + /* xReleaseAfterSend as false */ + configASSERT( 0 != xReleaseAfterSend ); + + dma_descriptor = &_tx_descriptors[ _tx_descriptor_list.write ]; + + /* If the DMA controller still owns the descriptor, all DMA descriptors are in use, bail out */ + if( 0U == ( dma_descriptor->ui32CtrlStatus & DES0_RX_CTRL_OWN ) ) + { + /* Assign the buffer to the DMA descriptor */ + dma_descriptor->pvBuffer1 = pxNetworkBuffer->pucEthernetBuffer; + + /* Inform the DMA of the size of the packet */ + dma_descriptor->ui32Count = ( pxNetworkBuffer->xDataLength & DES1_TX_CTRL_BUFF1_SIZE_M ) << DES1_TX_CTRL_BUFF1_SIZE_S; + + /* Inform the DMA that this is the first and last segment of the packet, calculate the checksums, the descriptors are */ + /* chained, and to use interrupts */ + dma_descriptor->ui32CtrlStatus = DES0_TX_CTRL_FIRST_SEG | DES0_TX_CTRL_IP_ALL_CKHSUMS | DES0_TX_CTRL_CHAINED + | DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_REPLACE_CRC; + + /* Advance the index in the list */ + _tx_descriptor_list.write++; + + /* Wrap around if required */ + if( _tx_descriptor_list.write == niEMAC_TX_DMA_DESC_COUNT ) + { + _tx_descriptor_list.write = 0; + } + + /* Give the DMA descriptor to the DMA controller */ + dma_descriptor->ui32CtrlStatus |= DES0_TX_CTRL_OWN; + + /* Inform the DMA it has a new descriptor */ + MAP_EMACTxDMAPollDemand( EMAC0_BASE ); + + iptraceNETWORK_INTERFACE_TRANSMIT(); + } + else + { + /* Release the stack descriptor and buffer to prevent memory leaks. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + + success = pdFALSE; + } + + return success; +} + +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + BaseType_t i; + + for( i = 0; i < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; i++ ) + { + /* Assign buffers to each descriptor */ + pxNetworkBuffers[ i ].pucEthernetBuffer = &_network_buffers[ i ][ ipBUFFER_PADDING ]; + + /* Set the 'hidden' reference to the descriptor for use in DMA interrupts */ + *( ( uint32_t * ) &_network_buffers[ i ][ 0 ] ) = ( uint32_t ) &( ( pxNetworkBuffers[ i ] ) ); + } +} + +static BaseType_t _ethernet_mac_get( uint8_t * mac_address_bytes ) +{ + BaseType_t success = pdPASS; + uint32_t mac_address_words[ 2 ] = { 0 }; + + /* Attempt to read the MAC address */ + MAP_FlashUserGet( &mac_address_words[ 0 ], &mac_address_words[ 1 ] ); + + /* If the MAC is not set, fail */ + if( ( 0xFFFFFFFF == mac_address_words[ 0 ] ) || ( 0xFFFFFFFF == mac_address_words[ 1 ] ) ) + { + success = pdFAIL; + } + else + { + /* Otherwise return the MAC address in a usable format for the driver */ + *( mac_address_bytes + 0 ) = ( mac_address_words[ 0 ] >> 0 ) & 0xFF; + *( mac_address_bytes + 1 ) = ( mac_address_words[ 0 ] >> 8 ) & 0xFF; + *( mac_address_bytes + 2 ) = ( mac_address_words[ 0 ] >> 16 ) & 0xFF; + *( mac_address_bytes + 3 ) = ( mac_address_words[ 1 ] >> 0 ) & 0xFF; + *( mac_address_bytes + 4 ) = ( mac_address_words[ 1 ] >> 8 ) & 0xFF; + *( mac_address_bytes + 5 ) = ( mac_address_words[ 1 ] >> 16 ) & 0xFF; + } + + return success; +} + +static void _dma_descriptors_init( void ) +{ + uint32_t i; + size_t buffer_size_requested; + NetworkBufferDescriptor_t * stack_descriptor; + + /* Initialize the TX DMA descriptors */ + for( i = 0; i < niEMAC_TX_DMA_DESC_COUNT; i++ ) + { + /* Clear the length of the packet */ + _tx_descriptors[ i ].ui32Count = 0; + + /* Clear the reference to the buffer */ + _tx_descriptors[ i ].pvBuffer1 = NULL; + + /* Set the next link in the DMA descriptor chain, either the next in the chain or the first descriptor in the event */ + /* that this is the last descriptor */ + _tx_descriptors[ i ].DES3.pLink = ( + ( i == ( niEMAC_TX_DMA_DESC_COUNT - 1 ) ) ? + &_tx_descriptors[ 0 ] : &_tx_descriptors[ i + 1 ] ); + _tx_descriptors[ i ].ui32CtrlStatus = DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_CHAINED + | DES0_TX_CTRL_IP_ALL_CKHSUMS; + } + + /* Set the TX descriptor index */ + _tx_descriptor_list.write = 0; + _tx_descriptor_list.read = 0; + + for( i = 0; i < niEMAC_RX_DMA_DESC_COUNT; i++ ) + { + stack_descriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 ); + + configASSERT( NULL != stack_descriptor ); + + /* Get a buffer from the stack and assign it to the DMA Descriptor */ + _rx_descriptors[ i ].pvBuffer1 = stack_descriptor->pucEthernetBuffer; + + /* Inform the DMA controller that the descriptors are chained and the size of the buffer */ + _rx_descriptors[ i ].ui32Count = DES1_RX_CTRL_CHAINED | ( ( buffer_size_requested << DES1_TX_CTRL_BUFF1_SIZE_S ) & DES1_TX_CTRL_BUFF1_SIZE_M ); + + /* Give the DMA descriptor to the DMA controller */ + _rx_descriptors[ i ].ui32CtrlStatus = DES0_RX_CTRL_OWN; + + /* Set the next link the DMA descriptor chain */ + _rx_descriptors[ i ].DES3.pLink = ( ( i == ( niEMAC_RX_DMA_DESC_COUNT - 1 ) ) ? &_rx_descriptors[ 0 ] : &_rx_descriptors[ i + 1 ] ); + } + + /* Set the RX descriptor index */ + _rx_descriptor_list.write = 0; + + /* Set the head of the DMA descriptor list in the EMAC peripheral */ + MAP_EMACTxDMADescriptorListSet( EMAC0_BASE, &_tx_descriptors[ 0 ] ); + MAP_EMACRxDMADescriptorListSet( EMAC0_BASE, &_rx_descriptors[ 0 ] ); +} + +void freertos_tcp_ethernet_int( void ) +{ + uint32_t status; + BaseType_t higher_priority_task_woken = pdFALSE; + + /* Read the interrupt status */ + status = EMACIntStatus( EMAC0_BASE, true ); + + /* Handle power management interrupts */ + if( status & EMAC_INT_POWER_MGMNT ) + { + MAP_EMACTxEnable( EMAC0_BASE ); + MAP_EMACRxEnable( EMAC0_BASE ); + + MAP_EMACPowerManagementStatusGet( EMAC0_BASE ); + + status &= ~( EMAC_INT_POWER_MGMNT ); + } + + if( status ) + { + MAP_EMACIntClear( EMAC0_BASE, status ); + } + + /* Handle PHY interrupts */ + if( EMAC_INT_PHY & status ) + { + _process_phy_interrupts(); + } + + /* Handle Transmit Complete interrupts */ + if( EMAC_INT_TRANSMIT & status ) + { + xMacInterruptStatus |= eMACInterruptTx; + } + + /* Handle Receive interrupts */ + if( ( EMAC_INT_RECEIVE | EMAC_INT_RX_NO_BUFFER | EMAC_INT_RX_STOPPED ) & status ) + { + xMacInterruptStatus |= eMACInterruptRx; + } + + /* If interrupts of concern were found, wake the task if present */ + if( ( 0 != xMacInterruptStatus ) && ( NULL != _deferred_task_handle ) ) + { + vTaskNotifyGiveFromISR( _deferred_task_handle, &higher_priority_task_woken ); + + portYIELD_FROM_ISR( higher_priority_task_woken ); + } +} + +static void _process_transmit_complete( void ) +{ + uint32_t i; + tEMACDMADescriptor * dma_descriptor; + NetworkBufferDescriptor_t * stack_descriptor; + + for( i = 0; ( ( i < _tx_descriptor_list.number_descriptors ) && ( _tx_descriptor_list.read != _tx_descriptor_list.write ) ); i++ ) + { + /* Get a reference to the current DMA descriptor */ + dma_descriptor = &_tx_descriptors[ _tx_descriptor_list.read ]; + + /* If the descriptor is still owned by the DMA controller, exit */ + if( dma_descriptor->ui32CtrlStatus & DES0_TX_CTRL_OWN ) + { + break; + } + + /* Get the 'hidden' reference to the stack descriptor from the buffer */ + stack_descriptor = pxPacketBuffer_to_NetworkBuffer( dma_descriptor->pvBuffer1 ); + + configASSERT( NULL != stack_descriptor ); + + /* Release the stack descriptor */ + vReleaseNetworkBufferAndDescriptor( stack_descriptor ); + + _tx_descriptor_list.read++; + + if( _tx_descriptor_list.read == _tx_descriptor_list.number_descriptors ) + { + _tx_descriptor_list.read = 0; + } + } +} + +static BaseType_t _process_received_packet( void ) +{ + NetworkBufferDescriptor_t * new_stack_descriptor; + NetworkBufferDescriptor_t * cur_stack_descriptor; + tEMACDMADescriptor * dma_descriptor; + uint32_t i; + IPStackEvent_t event; + BaseType_t result = pdTRUE; + const TickType_t max_block_time = pdMS_TO_MIN_TICKS( 50 ); + + /* Go through the list of RX DMA descriptors */ + for( i = 0; i < niEMAC_RX_DMA_DESC_COUNT; i++ ) + { + /* Get a reference to the descriptor */ + dma_descriptor = &_rx_descriptors[ _rx_descriptor_list.write ]; + + /* Make sure the buffer is non-null */ + configASSERT( NULL != dma_descriptor->pvBuffer1 ); + + /* If the descriptor is still in use by DMA, stop processing here */ + if( DES0_RX_CTRL_OWN == ( dma_descriptor->ui32CtrlStatus & DES0_RX_CTRL_OWN ) ) + { + break; + } + + /* If there is NOT an error in the frame */ + if( 0U == ( dma_descriptor->ui32CtrlStatus & DES0_RX_STAT_ERR ) ) + { + /* Get a new empty descriptor */ + new_stack_descriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, max_block_time ); + + /* If a descriptor was provided, else this packet is dropped */ + if( NULL != new_stack_descriptor ) + { + /* Get a reference to the current stack descriptor held by the DMA descriptor */ + cur_stack_descriptor = pxPacketBuffer_to_NetworkBuffer( dma_descriptor->pvBuffer1 ); + + /* Set the length of the buffer on the current descriptor */ + cur_stack_descriptor->xDataLength = ( dma_descriptor->ui32CtrlStatus & DES0_RX_STAT_FRAME_LENGTH_M ) >> DES0_RX_STAT_FRAME_LENGTH_S; + + /* Assign the new stack descriptor to the DMA descriptor */ + dma_descriptor->pvBuffer1 = new_stack_descriptor->pucEthernetBuffer; + + /* Ask the stack if it wants to process the frame. */ + if( eProcessBuffer == eConsiderFrameForProcessing( cur_stack_descriptor->pucEthernetBuffer ) ) + { + /* Setup the event */ + event.eEventType = eNetworkRxEvent; + event.pvData = cur_stack_descriptor; + + /* Forward the event */ + if( pdFALSE == xSendEventStructToIPTask( &event, 0 ) ) + { + /* Release the buffer if an error was encountered */ + vReleaseNetworkBufferAndDescriptor( cur_stack_descriptor ); + + iptraceETHERNET_RX_EVENT_LOST(); + } + else + { + iptraceNETWORK_INTERFACE_RECEIVE(); + + result = pdTRUE; + } + } + else + { + /* Free the descriptor */ + vReleaseNetworkBufferAndDescriptor( cur_stack_descriptor ); + } + } /* end if descriptor is available */ + else + { + /* No stack descriptor was available for the next RX DMA descriptor so this packet */ + /* is dropped */ + + /* Mark the RX event as lost */ + iptraceETHERNET_RX_EVENT_LOST(); + } + } /* end if frame had error. In this case, give the buffer back to the DMA for the next RX */ + + /* Set up the DMA descriptor for the next receive transaction */ + dma_descriptor->ui32Count = DES1_RX_CTRL_CHAINED | ipTOTAL_ETHERNET_FRAME_SIZE; + dma_descriptor->ui32CtrlStatus = DES0_RX_CTRL_OWN; + + _rx_descriptor_list.write++; + + if( _rx_descriptor_list.write == _rx_descriptor_list.number_descriptors ) + { + _rx_descriptor_list.write = 0; + } + } + + return result; +} + +/** + * This deferred interrupt handler process changes from the PHY auto-negotiation to configure the + * MAC as appropriate. + */ +static void _process_phy_interrupts( void ) +{ + uint16_t value; + uint16_t status; + uint32_t configuration; + uint32_t mode; + uint32_t max_frame_size; + + /* Read the PHY interrupts status */ + value = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_MISR1 ); + status = MAP_EMACPHYRead( EMAC0_BASE, PHY_PHYS_ADDR, EPHY_STS ); + + if( value & ( EPHY_MISR1_SPEED | EPHY_MISR1_DUPLEXM | EPHY_MISR1_ANC ) ) + { + /* If the speed or duplex has changed */ + + MAP_EMACConfigGet( EMAC0_BASE, &configuration, &mode, &max_frame_size ); + + if( status & EPHY_STS_SPEED ) + { + configuration &= ~EMAC_CONFIG_100MBPS; + } + else + { + configuration |= EMAC_CONFIG_100MBPS; + } + + if( status & EPHY_STS_DUPLEX ) + { + configuration |= EMAC_CONFIG_FULL_DUPLEX; + } + else + { + configuration &= ~EMAC_CONFIG_FULL_DUPLEX; + } + + MAP_EMACConfigSet( EMAC0_BASE, configuration, mode, max_frame_size ); + } +} + +static void _deferred_task( void * parameters ) +{ + BaseType_t had_reception; + IPStackEvent_t link_down_event; + const TickType_t max_block_time = pdMS_TO_TICKS( 100 ); + + /* Ignore parameters */ + ( void ) parameters; + + for( ; ; ) + { + had_reception = pdFALSE; + + ulTaskNotifyTake( pdTRUE, max_block_time ); + + if( eMACInterruptTx == ( xMacInterruptStatus & eMACInterruptTx ) ) + { + xMacInterruptStatus &= ~( eMACInterruptTx ); + + _process_transmit_complete(); + } + + if( eMACInterruptRx == ( xMacInterruptStatus & eMACInterruptRx ) ) + { + xMacInterruptStatus &= ~( eMACInterruptRx ); + + had_reception = _process_received_packet(); + } + + if( pdTRUE == xPhyCheckLinkStatus( &xPhyObject, had_reception ) ) + { + /* The link has gone done */ + if( 0 == xPhyObject.ulLinkStatusMask ) + { + link_down_event.eEventType = eNetworkDownEvent; + link_down_event.pvData = NULL; + + xSendEventStructToIPTask( &link_down_event, 0 ); + } + } + } +} + +static void vMACBProbePhy( void ) +{ + vPhyInitialise( &xPhyObject, xTM4C_PhyRead, xTM4C_PhyWrite ); + xPhyDiscover( &xPhyObject ); + xPhyConfigure( &xPhyObject, &xPHYProperties ); +} + +static BaseType_t xTM4C_PhyRead( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t * pulValue ) +{ + *pulValue = MAP_EMACPHYRead( EMAC0_BASE, ( uint8_t ) xAddress, ( uint8_t ) xRegister ); + + return 0; +} + +static BaseType_t xTM4C_PhyWrite( BaseType_t xAddress, + BaseType_t xRegister, + uint32_t ulValue ) +{ + MAP_EMACPHYWrite( EMAC0_BASE, ( uint8_t ) xAddress, ( uint8_t ) xRegister, ulValue ); + + return 0; +} diff --git a/FreeRTOS/source/portable/NetworkInterface/TM4C/TM4C-README.md b/FreeRTOS/source/portable/NetworkInterface/TM4C/TM4C-README.md new file mode 100644 index 0000000..cf976a8 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/TM4C/TM4C-README.md @@ -0,0 +1,12 @@ +# Ethernet Driver for TM4C129X MCUs +*JD Scott - jscott@hotstart.com* + +This driver was written and tested using the Texas Instruments (TI) TM4C1294NCPDT microcontroller using version the TivaWare((C) TI) driver library. This MCU includes built-in MAC and PHY which this driver assumes is to be used. + +This is a zero-copy driver uses the TivaWare ((C) TI) ROM function mapping macros, is intended for use with FreeRTOS+TCP BufferManagmeent_1, and is loosely modeled after the example lwIP Ethernet driver provided by TI in their TivaWare library. The driver utilizes the Ethernet (MAC) DMA engine. + +## Vendor Provided Version Numbers Used +The following versions for tools/libraries were used during development and testing of this driver: +- Code Composer Studio - 11.1.0.000111 +- TI ARM Code Generation Tools ((C) Texas Instruments) (CGT) - 20.2.6.LTS +- TivaWare - 2.2.0.295 diff --git a/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkInterface.c new file mode 100644 index 0000000..48ad870 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkInterface.c @@ -0,0 +1,1015 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Driver code: + * Copyright (C) Nicholas J. Kinar , Centre for Hydrology, University of Saskatchewan + * + * MSP432 Driverlib (C) 2017-2019 Texas Instruments Incorporated + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#include +#include +#include +#include + +#include "FreeRTOS.h" +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_private.h" +#include "FreeRTOSIPConfigDefaults.h" +#include "task.h" +#include "NetworkBufferManagement.h" + +#include "NetworkInterface.h" + +/* + * NOTES: + * This is a driver for the internal MAC of the MSP432E401Y microcontroller from Texas Instruments. + * + * (1) See below for defines that configure and override FreeRTOS defines so that the driver can be used. + * (2) The driver provides link up and down detection that also takes into consideration remote faults + * and Ethernet cable plug/unplug events during data transmission. + * (3) The EMAC hardware is automatically re-started if there is an internal error. + * (4) The MAC address can be set from outside of the driver or read from internal flash. + * (5) The EMAC hardware can be turned on or off from external tasks. + * (6) The EMAC hardware is set to 10BASE-T Half Duplex to ensure that the FreeRTOS+TCP stack does not receive + * data too quickly. This is because the 120 MHz processor cannot respond fast enough to ping floods or + * other events. The hardware is thereby set up so that it can respond in a robust fashion. + * (7) Supports tickless interrupts. + * (8) To turn on and off the EMAC, it is best to do a hard microcontroller reset. + * + * Recommended settings in FreeRTOSIPConfig.h: + #define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES 1 + #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 + #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 + #define ipconfigIP_TASK_STACK_SIZE_WORDS ( configMINIMAL_STACK_SIZE * 100 ) + #define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS 60 + #define configTASK_NOTIFICATION_ARRAY_ENTRIES 2 + * + #define ipconfigHAS_DEBUG_PRINTF 0 + #define ipconfigHAS_PRINTF 0 + * + * Recommended settings in FreeRTOSConfig.h: + #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) + #define configUSE_PREEMPTION 1 + #define configUSE_TIME_SLICING 0 + #define configCPU_CLOCK_HZ ( ( unsigned long ) 120000000 ) + #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 256 ) + #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 0x28000 ) ) + #define ipconfigTCP_WIN_SEG_COUNT 240 + * + * REFERENCES: + * [1] FreeRTOS Forum + * [2] Texas Instruments Driverlib code examples + */ + +#define RTOS_NET_UP_DOWN_TASK_NAME "NetUpDownS" +#define RTOS_NET_RX_TASK_NAME "NetRX" +#define RTOS_NET_TX_TASK_NAME "NetTX" +#define RTOS_NET_CHECK_TASK_SIZE configMINIMAL_STACK_SIZE +#define RTOS_RX_POLL_MAC_TASK_SIZE 3 * configMINIMAL_STACK_SIZE +#define RTOS_TX_POLL_MAC_TASK_SIZE 3 * configMINIMAL_STACK_SIZE +#define NUM_TX_DESCRIPTORS 8U +#define NUM_RX_DESCRIPTORS 8U + +#define ETH_DOWN_DELAY_MS 1000U +#define ETH_MAX_TIMEOUT_CYCLES 12000000U +#define ETH_STARTUP_TIMEOUT ETH_MAX_TIMEOUT_CYCLES +#define CHECK_LINK_UP_DOWN_DELAY_MS 1000U + +#define ETH_RX_QUEUE_LEN NUM_TX_DESCRIPTORS +#define ETH_TX_QUEUE_LEN NUM_RX_DESCRIPTORS + +/* TX Queue positions */ +#define TX_QUEPOS_FIRST_EVENT 0 +#define TX_QUEPOS_SECOND_EVENT 1 + +/* Minimum packet length is required to ensure that all of the bytes are transmitted + * even when there are issues with the network (i.e. a network cable pulled out during TX or RX) */ +#define ETHERNET_MIN_PACKET_BYTES 60 + +/* Ensure that the RTOS settings work well for this driver */ +#if configTASK_NOTIFICATION_ARRAY_ENTRIES < 2 + #undef configTASK_NOTIFICATION_ARRAY_ENTRIES + #define configTASK_NOTIFICATION_ARRAY_ENTRIES 2 +#endif +#ifndef ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM + #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 +#endif +#ifndef ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM + #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 +#endif +#ifdef ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM + #undef ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM + #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 +#endif +#ifdef ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM + #undef ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM + #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 +#endif +#ifndef ipconfigETHERNET_MINIMUM_PACKET_BYTES + #define ipconfigETHERNET_MINIMUM_PACKET_BYTES ETHERNET_MIN_PACKET_BYTES +#endif +#ifdef ipconfigETHERNET_MINIMUM_PACKET_BYTES + #undef ipconfigETHERNET_MINIMUM_PACKET_BYTES + #define ipconfigETHERNET_MINIMUM_PACKET_BYTES ETHERNET_MIN_PACKET_BYTES +#endif +#if ipconfigHAS_DEBUG_PRINTF == 1 + #warning The network interface may not work properly if ipconfigHAS_DEBUG_PRINTF 1. Please set ipconfigHAS_DEBUG_PRINTF 0 for production use. +#endif +#ifndef __MSP432E401Y__ + #define __MSP432E401Y__ +#endif + +static BaseType_t loadMACInternal(); +static BaseType_t setupEMAC(); +static void offEMAC(); +static void initDescriptors( uint32_t ul32Base ); +static uint32_t packetTransmit( uint8_t * pui8Buf, + uint32_t ul32BufLen ); +static void ethernetIntHandler( void ); +static uint32_t processReceivedPacket( void ); +static void applicationProcessFrameRX( uint32_t ul32FrameLen, + uint8_t * uc8Buf, + uint32_t ulindex ); +static void prvCheckLinkUpOrDownNetStateTask( void * pvParameters ); +static void prvEMACDeferredInterruptHandlerTaskRX( void * pvParameters ); +static BaseType_t isEMACLinkUp(); +static void DMAFreeDescriptorRX( uint32_t ulindx ); +static void prvEMACDeferredInterfaceOutputTaskTX( void * pvParameters ); +static void vSetNetworkInterfaceConfig( const struct InternalNetworkInterfaceMSP432EConfig config ); + +/* EMAC interrupts */ +#define EMAC_INTERRUPTS EMAC_INT_RECEIVE | EMAC_INT_TRANSMIT | EMAC_INT_BUS_ERROR | EMAC_INT_TX_STOPPED | EMAC_INT_RX_STOPPED + +/* DMA descriptors */ +tEMACDMADescriptor g_psRxDescriptor[ NUM_TX_DESCRIPTORS ]; +tEMACDMADescriptor g_psTxDescriptor[ NUM_RX_DESCRIPTORS ]; +uint32_t ulg_ui32RxDescIndex; +uint32_t ulg_ui32TxDescIndex; + +/* Define the maximum length of a packet buffer. 1536 is ideal because it + * allows for a perfect alignment. */ +#define ETH_MAX_BUFFER_SIZE 1536 +#define ETH_RX_BUFFER_SIZE ETH_MAX_BUFFER_SIZE +#define ETH_TX_BUFFER_SIZE ETH_MAX_BUFFER_SIZE +uint8_t ucg_ppui8RxBuffer[ NUM_RX_DESCRIPTORS ][ ETH_RX_BUFFER_SIZE ]; + +/* Task handles */ +TaskHandle_t xTaskToNotifyEthernetRX = NULL; +TaskHandle_t xTaskToNotifyEthernetTX = NULL; +TaskHandle_t xHandleCheckLinkUpOrDown = NULL; +TaskHandle_t xHandleCheckNet = NULL; + +/* Queue handles */ +QueueHandle_t xQueueRX; +QueueHandle_t xQueueTX; + +/* MAC address */ +uint8_t ucpui8MACAddr[ ipMAC_ADDRESS_LENGTH_BYTES ]; + +/* State variable that indicates whether the network is up or down */ +static BaseType_t networkUP = pdFALSE; +/* Check to see if the device has been setup */ +static BaseType_t hasBeenSetup = pdFALSE; +/* Check to see if the network needs to be reset */ +static BaseType_t resetNetwork = pdFALSE; + +/* RX data input buffer */ +struct NetworkInterfaceDataIn +{ + uint8_t * ucbuff; /* buffer to the data */ + uint32_t ulbuff_siz; /* buffer size */ + uint32_t ulindx; /* index of buffer in DMA descriptor */ +}; + +/* TX data output buffer */ +struct NetworkInterfaceDataOut +{ + NetworkBufferDescriptor_t * pxDescriptor; + BaseType_t xReleaseAfterSend; +}; + +/* Local config struct */ +static struct InternalNetworkInterfaceMSP432EConfig configLocal; + + +/* Call this function to check if the network interface is up. + * The function can be called from code external to this file. + */ +BaseType_t vPublicCheckNetworkInterfaceUp() +{ + return networkUP; +} /* end */ + + +static BaseType_t loadMACInternal() +{ + uint32_t ui32User0, ui32User1; + + /* Read the MAC address from internal flash. Bit[23:0] are stored in user register0, and Bit[47:24] are stored in user register1. + * The MAC address can be loaded from an external IC or can be loaded from the internal registers if the registers are not zero. + * The evaluation kit hardware uses reading from internal flash variables but this can also be used for production as well + * if the internal flash is programmed on the assembly line. */ + FlashUserGet( &ui32User0, &ui32User1 ); + + if( ( ( FlashUserGet( &ui32User0, &ui32User1 ) ) != 0 ) || + ( ui32User0 == 0xffffffff ) || + ( ui32User1 == 0xffffffff ) ) + { + return pdFALSE; + } + + configLocal.ucMACAddr[ 0 ] = ( ( ui32User0 >> 0 ) & 0xffU ); + configLocal.ucMACAddr[ 1 ] = ( ( ui32User0 >> 8 ) & 0xffU ); + configLocal.ucMACAddr[ 2 ] = ( ( ui32User0 >> 16 ) & 0xffU ); + configLocal.ucMACAddr[ 3 ] = ( ( ui32User1 >> 0 ) & 0xffU ); + configLocal.ucMACAddr[ 4 ] = ( ( ui32User1 >> 8 ) & 0xffU ); + configLocal.ucMACAddr[ 5 ] = ( ( ui32User1 >> 16 ) & 0xffU ); + return pdTRUE; +} + + +/* This function sets up the EMAC. Not to be called directly from outside of this file. */ +static BaseType_t setupEMAC() +{ + uint32_t ul32Loop; + BaseType_t rv; + BaseType_t interruptsMasked = pdFALSE; + + if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) + { + taskENTER_CRITICAL(); + interruptsMasked = pdTRUE; + } + + if( configLocal.setMACAddrInternal == pdTRUE ) + { + rv = loadMACInternal(); + + if( rv == pdFALSE ) + { + return pdFALSE; + } + } + + /* enable and reset the internal MAC */ + SysCtlPeripheralPowerOn( SYSCTL_PERIPH_EMAC0 ); + SysCtlPeripheralPowerOn( SYSCTL_PERIPH_EPHY0 ); + SysCtlPeripheralEnable( SYSCTL_PERIPH_EMAC0 ); + SysCtlPeripheralEnable( SYSCTL_PERIPH_EPHY0 ); + SysCtlPeripheralReset( SYSCTL_PERIPH_EMAC0 ); + SysCtlPeripheralReset( SYSCTL_PERIPH_EPHY0 ); + + /* The peripheral should always start, but there is a wait with timeout here to prevent an infinite loop*/ + ul32Loop = 0; + + while( !SysCtlPeripheralReady( SYSCTL_PERIPH_EMAC0 ) && ( ul32Loop < ETH_STARTUP_TIMEOUT ) ) + { + SysCtlDelay( 1 ); + ul32Loop += 1; + } + + if( ul32Loop == ETH_STARTUP_TIMEOUT ) + { + return pdFALSE; + } + + /* configure the internal PHY */ + EMACPHYConfigSet( EMAC0_BASE, + ( EMAC_PHY_TYPE_INTERNAL | + EMAC_PHY_INT_MDIX_EN | + EMAC_PHY_AN_10B_T_HALF_DUPLEX ) ); + + /* reset the MAC to latch the configuration */ + EMACReset( EMAC0_BASE ); + + /* wait */ + SysCtlDelay( ETH_STARTUP_TIMEOUT ); + + /* init the EMAC */ + EMACInit( EMAC0_BASE, configCPU_CLOCK_HZ, + EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, 4, 4, + 0 ); + + /* + * Since the checksum is computed in the hardware using EMAC_CONFIG_CHECKSUM_OFFLOAD, + * there is also a need to turn on defines in FreeRTOS+TCP as well. This is done automatically + * using the defines in this file. + * + #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 + #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 + */ + EMACConfigSet( EMAC0_BASE, + ( EMAC_CONFIG_HALF_DUPLEX | + EMAC_CONFIG_CHECKSUM_OFFLOAD | + EMAC_CONFIG_IF_GAP_96BITS | + EMAC_CONFIG_USE_MACADDR0 | + EMAC_CONFIG_BO_LIMIT_1024 ), + ( EMAC_MODE_RX_STORE_FORWARD | EMAC_MODE_TX_STORE_FORWARD + ), ETH_MAX_BUFFER_SIZE ); + + /* DMA descriptors init */ + initDescriptors( EMAC0_BASE ); + + /* program MAC address from the cached address */ + EMACAddrSet( EMAC0_BASE, 0, configLocal.ucMACAddr ); + + /* Set address filtering */ + EMACAddrFilterSet( EMAC0_BASE, 0, EMAC_FILTER_ADDR_ENABLE | EMAC_FILTER_SOURCE_ADDR ); + + /* Set MAC filtering options */ + EMACFrameFilterSet( EMAC0_BASE, EMAC_FRMFILTER_SADDR | EMAC_FRMFILTER_PASS_NO_CTRL ); + + /* indicate that the receive descriptors are available to the DMA to start the receive processing */ + for( ul32Loop = 0; ul32Loop < NUM_RX_DESCRIPTORS; ul32Loop++ ) + { + g_psRxDescriptor[ ul32Loop ].ui32CtrlStatus |= DES0_RX_CTRL_OWN; + } + + /* Enable the Ethernet MAC transmitter and receiver */ + EMACTxEnable( EMAC0_BASE ); + EMACRxEnable( EMAC0_BASE ); + + /* Enable the Ethernet interrupt */ + IntPrioritySet( INT_EMAC0, 0x20 ); /* (0x01 << 5) to set the priority into the last three bits for Cortex M4F */ + + /* register the interrupt handler for the Ethernet MAC */ + EMACIntRegister( EMAC0_BASE, ethernetIntHandler ); + + /* enable the interrupts for the EMAC */ + EMACIntClear( EMAC0_BASE, EMACIntStatus( EMAC0_BASE, pdFALSE ) ); + IntEnable( INT_EMAC0 ); + EMACIntEnable( EMAC0_BASE, EMAC_INTERRUPTS ); + + /* exit the critical section */ + if( interruptsMasked == pdTRUE ) + { + taskEXIT_CRITICAL(); + } + + return pdTRUE; +} + + +/* This function only turns off the Ethernet EMAC. This function does not turn off any + * of the processing tasks and therefore should not be called directly from external tasks. */ +static void offEMAC() +{ + uint32_t ulstatus, ulcnt; + BaseType_t interruptsMasked; + + interruptsMasked = pdFALSE; + + if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) + { + taskENTER_CRITICAL(); + interruptsMasked = pdTRUE; + } + + networkUP = pdFALSE; + + ulcnt = 0; + ulstatus = EMACStatusGet( EMAC0_BASE ); + + while( ulstatus ) + { + ulstatus = EMACStatusGet( EMAC0_BASE ); + ulcnt++; + + if( ulcnt == ETH_STARTUP_TIMEOUT ) + { + if( interruptsMasked == pdTRUE ) + { + taskEXIT_CRITICAL(); + } + + return; + } + } + + IntDisable( INT_EMAC0 ); + SysCtlPeripheralDisable( SYSCTL_PERIPH_EMAC0 ); + SysCtlPeripheralDisable( SYSCTL_PERIPH_EPHY0 ); + SysCtlPeripheralPowerOff( SYSCTL_PERIPH_EMAC0 ); + SysCtlPeripheralPowerOff( SYSCTL_PERIPH_EPHY0 ); + + if( interruptsMasked == pdTRUE ) + { + taskEXIT_CRITICAL(); + } +} + + +/* Interrupt handler */ +static void ethernetIntHandler( void ) +{ + uint32_t ui32Temp; + + ui32Temp = EMACIntStatus( EMAC0_BASE, pdTRUE ); + + if( ui32Temp & EMAC_INT_RECEIVE ) + { + /* RX */ + processReceivedPacket(); + } + + if( ui32Temp & EMAC_INT_TRANSMIT ) + { + BaseType_t xHigherPriorityTaskWokenTX = pdFALSE; + /* TX */ + vTaskNotifyGiveIndexedFromISR( xTaskToNotifyEthernetTX, TX_QUEPOS_SECOND_EVENT, &xHigherPriorityTaskWokenTX ); + portYIELD_FROM_ISR( xHigherPriorityTaskWokenTX ); + } + + if( ( ui32Temp & EMAC_INT_BUS_ERROR ) || ( ui32Temp & EMAC_INT_TX_STOPPED ) || ( ui32Temp & EMAC_INT_RX_STOPPED ) ) + { + /* Reset the network since something has gone wrong */ + resetNetwork = pdTRUE; + } + + /* clear the interrupt */ + EMACIntClear( EMAC0_BASE, ui32Temp ); + /* as per Application Note SLAA739 - June 2017 */ + #if configUSE_TICKLESS_IDLE == 1 + SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk; + __DSB(); + #endif +} + + +/* Init the DMA descriptors */ +static void initDescriptors( uint32_t ul32Base ) +{ + uint32_t ul32Loop; + + for( ul32Loop = 0U; ul32Loop < NUM_TX_DESCRIPTORS; ul32Loop++ ) + { + g_psTxDescriptor[ ul32Loop ].ui32Count = DES1_TX_CTRL_SADDR_INSERT; + g_psTxDescriptor[ ul32Loop ].DES3.pLink = + ( ul32Loop == ( NUM_TX_DESCRIPTORS - 1 ) ) ? + g_psTxDescriptor : &g_psTxDescriptor[ ul32Loop + 1 ]; + g_psTxDescriptor[ ul32Loop ].ui32CtrlStatus = + ( DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG | + DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_CHAINED | + DES0_TX_CTRL_IP_ALL_CKHSUMS ); + } + + for( ul32Loop = 0; ul32Loop < NUM_RX_DESCRIPTORS; ul32Loop++ ) + { + g_psRxDescriptor[ ul32Loop ].ui32CtrlStatus = 0; + g_psRxDescriptor[ ul32Loop ].ui32Count = + ( DES1_RX_CTRL_CHAINED | + ( ETH_RX_BUFFER_SIZE << DES1_RX_CTRL_BUFF1_SIZE_S ) ); + g_psRxDescriptor[ ul32Loop ].pvBuffer1 = ucg_ppui8RxBuffer[ ul32Loop ]; + g_psRxDescriptor[ ul32Loop ].DES3.pLink = + ( ul32Loop == ( NUM_RX_DESCRIPTORS - 1 ) ) ? + g_psRxDescriptor : &g_psRxDescriptor[ ul32Loop + 1 ]; + } + + EMACRxDMADescriptorListSet( ul32Base, g_psRxDescriptor ); + EMACTxDMADescriptorListSet( ul32Base, g_psTxDescriptor ); + + ulg_ui32RxDescIndex = 0; + ulg_ui32TxDescIndex = NUM_TX_DESCRIPTORS - 1; +} + + +/* Transmit a packet - call this function from the network stack + * pui8Buf = the buffer + * i32BufLen = length of the buffer + */ +static uint32_t packetTransmit( uint8_t * pui8Buf, + uint32_t ul32BufLen ) +{ + uint8_t bufferTX[ ETH_TX_BUFFER_SIZE ]; + + ulg_ui32TxDescIndex++; + + if( ulg_ui32TxDescIndex == NUM_TX_DESCRIPTORS ) + { + ulg_ui32TxDescIndex = 0; + } + + /* The copy needs to be done here since directly assigning a pointer does not seem to work + * and causes the transmitter to stop. Some testing indicates that that this can be quicker than + * simply assigning the pointer for smaller packets. */ + memcpy( bufferTX, pui8Buf, ul32BufLen ); + + g_psTxDescriptor[ ulg_ui32TxDescIndex ].ui32Count = ul32BufLen; + g_psTxDescriptor[ ulg_ui32TxDescIndex ].pvBuffer1 = bufferTX; + g_psTxDescriptor[ ulg_ui32TxDescIndex ].ui32CtrlStatus = + ( DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG | + DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_IP_ALL_CKHSUMS | + DES0_TX_CTRL_CHAINED | DES0_TX_CTRL_OWN ); + + EMACTxDMAPollDemand( EMAC0_BASE ); + return( ul32BufLen ); +} + + +/* Function to process the received packet */ +static uint32_t processReceivedPacket( void ) +{ + uint32_t ul32FrameLen; + + ul32FrameLen = 0; + + if( !( g_psRxDescriptor[ ulg_ui32RxDescIndex ].ui32CtrlStatus & DES0_RX_CTRL_OWN ) ) + { + /* Does it have a valid frame? */ + if( !( g_psRxDescriptor[ ulg_ui32RxDescIndex ].ui32CtrlStatus & + DES0_RX_STAT_ERR ) ) + { + if( g_psRxDescriptor[ ulg_ui32RxDescIndex ].ui32CtrlStatus & + DES0_RX_STAT_LAST_DESC ) + { + /* get the frame length */ + ul32FrameLen = + ( ( g_psRxDescriptor[ ulg_ui32RxDescIndex ].ui32CtrlStatus & + DES0_RX_STAT_FRAME_LENGTH_M ) >> + DES0_RX_STAT_FRAME_LENGTH_S ); + + /* call the function that sends the data to the task */ + applicationProcessFrameRX( ul32FrameLen, + g_psRxDescriptor[ ulg_ui32RxDescIndex ].pvBuffer1, ulg_ui32RxDescIndex ); + } + } + + /* Move on to the next RX packet */ + ulg_ui32RxDescIndex++; + + if( ulg_ui32RxDescIndex == NUM_RX_DESCRIPTORS ) + { + ulg_ui32RxDescIndex = 0; + } + } /* end if */ + + /* return the frame length */ + return ul32FrameLen; +} + + +/* This function passes the framelength and the RX buffer into the FreeRTOS task. + * The function is called from the ISR and therefore must use portYIELD_FROM_ISR() */ +static void applicationProcessFrameRX( uint32_t ul32FrameLen, + uint8_t * uc8Buf, + uint32_t ulindex ) +{ + BaseType_t rv; + struct NetworkInterfaceDataIn NIDataIn; + BaseType_t xHigherPriorityTaskWokenRX = pdFALSE; + + NIDataIn.ucbuff = uc8Buf; + NIDataIn.ulbuff_siz = ul32FrameLen; + NIDataIn.ulindx = ulindex; + + /* send the data into the queue */ + rv = xQueueSendFromISR( xQueueRX, &NIDataIn, &xHigherPriorityTaskWokenRX ); + + if( rv == pdTRUE ) + { + vTaskNotifyGiveFromISR( xTaskToNotifyEthernetRX, &xHigherPriorityTaskWokenRX ); + } + else + { + DMAFreeDescriptorRX( ulindex ); + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWokenRX ); +} + +/* Function to free the RX descriptor */ +static void DMAFreeDescriptorRX( uint32_t ulindx ) +{ + g_psRxDescriptor[ ulindx ].ui32CtrlStatus = DES0_RX_CTRL_OWN; +} + +/* + * ------------------------------------------------------------- + * FREERTOS+TCP FUNCTIONS + * ------------------------------------------------------------- + */ + +/* Call this function to populate the driver defaults struct with defaults */ +void vGetInternalNetworkInterfaceMSP432EConfigDefaults( struct InternalNetworkInterfaceMSP432EConfig * config ) +{ + uint32_t k; + + config->turnOnEMAC = pdTRUE; + config->setMACAddrInternal = pdFALSE; + + for( k = 0; k < ipMAC_ADDRESS_LENGTH_BYTES; k++ ) + { + config->ucMACAddr[ k ] = 0xFF; + } +} + + +static void vSetNetworkInterfaceConfig( const struct InternalNetworkInterfaceMSP432EConfig config ) +{ + configLocal = config; +} + + +/* Call this function to setup the network */ +BaseType_t vPublicSetupEMACNetwork( const struct InternalNetworkInterfaceMSP432EConfig config ) +{ + BaseType_t rv; + + rv = pdFALSE; + BaseType_t tv; + + /* setup the MAC address and turn on the EMAC if required */ + vSetNetworkInterfaceConfig( config ); + + if( config.turnOnEMAC ) + { + rv = setupEMAC(); + } + + if( rv == pdFALSE ) + { + return pdFALSE; + } + + /* ensure that the code can only be run once to create the tasks */ + if( hasBeenSetup == pdTRUE ) + { + return pdTRUE; + } + + /* create the queues */ + xQueueRX = xQueueCreate( ETH_RX_QUEUE_LEN, sizeof( struct NetworkInterfaceDataIn ) ); + + if( xQueueRX == NULL ) + { + return pdFALSE; + } + + xQueueTX = xQueueCreate( ETH_TX_QUEUE_LEN, sizeof( struct NetworkInterfaceDataOut ) ); + + if( xQueueTX == NULL ) + { + return pdFALSE; + } + + /* RX task */ + tv = xTaskCreate( prvEMACDeferredInterruptHandlerTaskRX, + RTOS_NET_RX_TASK_NAME, + RTOS_RX_POLL_MAC_TASK_SIZE, + NULL, + configMAX_PRIORITIES - 1, + &xTaskToNotifyEthernetRX ); + + if( tv != pdPASS ) + { + return pdFALSE; + } + + /* TX task */ + tv = xTaskCreate( prvEMACDeferredInterfaceOutputTaskTX, + RTOS_NET_TX_TASK_NAME, + RTOS_TX_POLL_MAC_TASK_SIZE, + NULL, + configMAX_PRIORITIES - 1, + &xTaskToNotifyEthernetTX ); + + if( tv != pdPASS ) + { + return pdFALSE; + } + + /* Link Up/Down/Network task */ + tv = xTaskCreate( prvCheckLinkUpOrDownNetStateTask, + RTOS_NET_UP_DOWN_TASK_NAME, + RTOS_NET_CHECK_TASK_SIZE, + NULL, + tskIDLE_PRIORITY, + &xHandleCheckLinkUpOrDown ); + + if( tv != pdPASS ) + { + return pdFALSE; + } + + /* latch the setup state */ + hasBeenSetup = pdTRUE; + + /* the setup has succeeded */ + return pdTRUE; +} + + +/* FreeRTOS task that handles the RX interrupt */ +static void prvEMACDeferredInterruptHandlerTaskRX( void * pvParameters ) +{ + NetworkBufferDescriptor_t * pxDescriptor = NULL; + IPStackEvent_t xRxEvent; + struct NetworkInterfaceDataIn NIDataReceived; + uint32_t xBytesReceived; + + for( ; ; ) + { + ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); + xQueueReceive( xQueueRX, &NIDataReceived, 0 ); + + if( eConsiderFrameForProcessing( NIDataReceived.ucbuff ) == eProcessBuffer ) + { + xBytesReceived = NIDataReceived.ulbuff_siz - ipSIZE_OF_ETH_CRC_BYTES; /* do not include the CRC bytes in the received size */ + pxDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 ); + + if( pxDescriptor != NULL ) + { + memcpy( pxDescriptor->pucEthernetBuffer, NIDataReceived.ucbuff, NIDataReceived.ulbuff_siz ); + pxDescriptor->xDataLength = xBytesReceived; + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = ( void * ) pxDescriptor; + + if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdTRUE ) + { + /* The buffer does not need to be released here since this will be done internally by the network stack */ + iptraceNETWORK_INTERFACE_RECEIVE(); + } + else + { + /* The buffer needs to be released since we cannot send to the network stack */ + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + iptraceETHERNET_RX_EVENT_LOST(); + } + } + } + + /* Release the DMA descriptor at a specific index */ + taskENTER_CRITICAL(); + DMAFreeDescriptorRX( NIDataReceived.ulindx ); + taskEXIT_CRITICAL(); + } /* end for */ +} + + +/* Network initialization is already done outside of this function, so this function will only + * report if the network is initialized by returning a flag. Called directly by FreeRTOS. */ +BaseType_t xNetworkInterfaceInitialise( void ) +{ + if( networkUP == pdTRUE ) + { + return pdTRUE; + } + + return pdFALSE; +} + + +/* Task to output the data to the network interface. This allows the network stack to continue + * processing while the data is able to be sent. */ +static void prvEMACDeferredInterfaceOutputTaskTX( void * pvParameters ) +{ + struct NetworkInterfaceDataOut NIDataOutput; + + for( ; ; ) + { + ulTaskNotifyTakeIndexed( TX_QUEPOS_FIRST_EVENT, pdTRUE, portMAX_DELAY ); + xQueueReceive( xQueueTX, &NIDataOutput, 0 ); + + /* For ICMP packets, the checksum must be zero if the network hardware computes the checksum or the buffer will not be properly sent + * For this driver it is required that ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 */ + ProtocolPacket_t * pxPacket; + pxPacket = ( ProtocolPacket_t * ) ( NIDataOutput.pxDescriptor->pucEthernetBuffer ); + + if( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) + { + pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t ) 0u; + } + + taskENTER_CRITICAL(); + packetTransmit( NIDataOutput.pxDescriptor->pucEthernetBuffer, ( uint32_t ) NIDataOutput.pxDescriptor->xDataLength ); + taskEXIT_CRITICAL(); + + /* wait until transmit */ + ulTaskNotifyTakeIndexed( TX_QUEPOS_SECOND_EVENT, pdTRUE, portMAX_DELAY ); + iptraceNETWORK_INTERFACE_TRANSMIT(); + + if( NIDataOutput.xReleaseAfterSend ) + { + vReleaseNetworkBufferAndDescriptor( NIDataOutput.pxDescriptor ); + } + } +} + + +/* Function to output packets to the network. + * Called directly by FreeRTOS. This function should not block and therefore passes + * all output data to a queue. If the function blocks the network stack can become unresponsive + * so all of the output work is thereby done by another task. */ +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t xReleaseAfterSend ) +{ + struct NetworkInterfaceDataOut NIDataOutput; + BaseType_t txResp; + BaseType_t returnValue = pdFALSE; + BaseType_t xDescriptorUsed = pdFALSE; + + /* When the following assert is hit, please make sure that + * ipconfigZERO_COPY_TX_DRIVER is defined as '1' in your + * FreeRTOSIPConfig.h header file. */ + configASSERT( xReleaseAfterSend != pdFALSE ); + + if( networkUP != pdFALSE ) + { + NIDataOutput.pxDescriptor = pxDescriptor; + NIDataOutput.xReleaseAfterSend = xReleaseAfterSend; + txResp = xQueueSend( xQueueTX, &NIDataOutput, 0 ); + + if( txResp == pdTRUE ) + { + xTaskNotifyGiveIndexed( xTaskToNotifyEthernetTX, 0 ); + returnValue = pdTRUE; + xDescriptorUsed = pdTRUE; + } + } + else + { + /* The PHY has no Link Status, packet shall be dropped. */ + } + + if( xDescriptorUsed == pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return returnValue; +} + + +/* + * ----------------------------------------------------------------------------------------------------- + * Network interface UP or DOWN tasks and associated functions + * ----------------------------------------------------------------------------------------------------- + */ + +/* Function to check and see if the network is up or down by polling a register in the Ethernet driver. + * Apparently there is no other way to do this other than polling. The register is checked for link status, + * but also for remote faults and this takes into consideration a pulled Ethernet cable during RX and TX. + */ +static BaseType_t isEMACLinkUp() +{ + uint8_t uci8PHYAddr = 0; /* refers to the internal PHY */ + uint16_t check; + BaseType_t returnValue = pdFALSE; + + check = EMACPHYRead( EMAC0_BASE, uci8PHYAddr, EPHY_BMSR ); + + if( ( ( check & EPHY_BMSR_LINKSTAT ) != 0 ) && !( check & EPHY_BMSR_RFAULT ) ) + { + returnValue = pdTRUE; /* link is up */ + } + + return returnValue; /* return link status */ +} + + +/* Call this network function to physically turn on the EMAC from an internal task. */ +static BaseType_t vPrivateTurnOnEMAC() +{ + if( hasBeenSetup == pdFALSE ) + { + return pdFALSE; + } + + setupEMAC(); + + if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) + { + FreeRTOS_NetworkDown(); + vTaskResume( xHandleCheckLinkUpOrDown ); + vTaskResume( xHandleCheckNet ); + vTaskResume( xTaskToNotifyEthernetRX ); + vTaskResume( xTaskToNotifyEthernetTX ); + } + + return pdTRUE; +} + + +/* Call this function to physically turn off the EMAC from an external task. + * It is recommended to check the network stack for open sockets before calling this task, + * and only call the task after all sockets are closed and there are no connected clients. + * In an operational sense, the best way to turn off the EMAC is to do a hard reset. */ +static BaseType_t vPrivateTurnOffEMAC() +{ + if( hasBeenSetup == pdFALSE ) + { + return pdFALSE; /* make sure that the MAC has been setup */ + } + + if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) + { + vTaskSuspend( xHandleCheckLinkUpOrDown ); + vTaskSuspend( xHandleCheckNet ); + vTaskSuspend( xTaskToNotifyEthernetRX ); + vTaskSuspend( xTaskToNotifyEthernetTX ); + } + + networkUP = pdFALSE; + + if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) + { + FreeRTOS_NetworkDown(); /* Indicate to FreeRTOS that the network is down */ + vTaskDelay( pdMS_TO_TICKS( ETH_DOWN_DELAY_MS ) ); /* Wait until FreeRTOS has finished processing */ + } + + offEMAC(); /* Turn off the physical hardware */ + return pdTRUE; +} + + +/* A task to check and see if the link is up or down by polling an EMAC register */ +static void prvCheckLinkUpOrDownNetStateTask( void * pvParameters ) +{ + BaseType_t checkLinkStatus = pdFALSE; + + for( ; ; ) + { + checkLinkStatus = isEMACLinkUp(); + + if( ( checkLinkStatus == pdTRUE ) && ( networkUP == pdFALSE ) ) + { + networkUP = pdTRUE; + } + else if( ( checkLinkStatus == pdFALSE ) && ( networkUP == pdTRUE ) ) + { + /* FreeRTOS will poll xNetworkInterfaceInitialise() to check if the network is up. + * So after FreeRTOS_NetworkDown() is called, there is no corresponding FreeRTOS_NetworkUp() function... + */ + networkUP = pdFALSE; + FreeRTOS_NetworkDown(); + } + + if( resetNetwork == pdTRUE ) + { + vPrivateTurnOffEMAC(); + vPrivateTurnOnEMAC(); + resetNetwork = pdFALSE; + } + } +} + + + +/* Call this function to obtain the MAC address used by the driver */ +void vPublicGetMACAddr( uint8_t uc8MACAddrGet[ ipMAC_ADDRESS_LENGTH_BYTES ] ) +{ + memcpy( uc8MACAddrGet, configLocal.ucMACAddr, ipMAC_ADDRESS_LENGTH_BYTES ); +} + + +BaseType_t xGetPhyLinkStatus( void ) +{ + return networkUP; +} + + +/*----------------------------------------------------------------------------------------------------- + * For BufferAllocation_1.c (see the FreeRTOS documentation for further details and examples) + * ----------------------------------------------------------------------------------------------------- */ +#define BUFFER_SIZE_ALLOC1 ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING ) +#define BUFFER_SIZE_ALLOC1_ROUNDED_UP ( ( BUFFER_SIZE_ALLOC1 + 7 ) & ~0x07UL ) +static uint8_t ucBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ BUFFER_SIZE_ALLOC1_ROUNDED_UP ]; +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + BaseType_t x; + + for( x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ ) + { + /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the + * beginning of the allocated buffer. */ + pxNetworkBuffers[ x ].pucEthernetBuffer = &( ucBuffers[ x ][ ipBUFFER_PADDING ] ); + + /* The following line is also required, but will not be required in + * future versions. */ + *( ( uint32_t * ) &ucBuffers[ x ][ 0 ] ) = ( uint32_t ) &( pxNetworkBuffers[ x ] ); + } +} diff --git a/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkInterface.h b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkInterface.h new file mode 100644 index 0000000..36b1ca0 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkInterface.h @@ -0,0 +1,48 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Driver code: + * Copyright (C) Nicholas J. Kinar , Centre for Hydrology, University of Saskatchewan + * + * MSP432 Driverlib (C) 2017-2019 Texas Instruments Incorporated + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#pragma once +#include +#include "FreeRTOS.h" +#include "FreeRTOS_IP.h" + +struct InternalNetworkInterfaceMSP432EConfig +{ + BaseType_t turnOnEMAC; /* true to turn on the EMAC initially on startup */ + uint8_t ucMACAddr[ ipMAC_ADDRESS_LENGTH_BYTES ]; /* MAC address */ + BaseType_t setMACAddrInternal; /* true to set the MAC address from internal registers */ +}; /* end */ + +BaseType_t vPublicCheckNetworkInterfaceUp(); +void vPublicGetMACAddr( uint8_t pui8MACAddrGet[ ipMAC_ADDRESS_LENGTH_BYTES ] ); +BaseType_t vPublicSetupEMACNetwork( const struct InternalNetworkInterfaceMSP432EConfig config ); +void vGetInternalNetworkInterfaceMSP432EConfigDefaults( struct InternalNetworkInterfaceMSP432EConfig * config ); diff --git a/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkMiddleware.c b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkMiddleware.c new file mode 100644 index 0000000..02dbcde --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkMiddleware.c @@ -0,0 +1,293 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Driver code: + * Copyright (C) Nicholas J. Kinar , Centre for Hydrology, University of Saskatchewan + * + * MSP432 Driverlib (C) 2017-2019 Texas Instruments Incorporated + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#include + +#include "FreeRTOS.h" +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_private.h" +#include "FreeRTOS_Sockets.h" + +#include "NetworkMiddleware.h" +#include "NetworkInterface.h" + +/* Waiting time between network EMAC hardware up and down */ +#define TIME_TO_WAIT_BETWEEN_NETUP_DOWN 2000 + +/* Stack for up and down thread */ +#define NETWORK_TASK_MIDDLEWARE_STACK 1000 + +/* Holds the device name used for LLMNR */ +static char DEV_NAME[ MAX_NAME_LLMNR ]; + +/* This function is provided by external code to obtain a random number */ +extern uint32_t obtain_rand32(); + +static SemaphoreHandle_t xSemaphore = NULL; +static TaskHandle_t xTaskToNotifyReset = NULL; +static uint32_t xDelay; + +static void prvNetworkResetTask( void * pvParameters ); + + +/* Call this function before starting the scheduler and after the MAC address and device name has been loaded. + * The function can only be called once to set up the tasks. */ +void vPublicSetupFreeRTOSTasks( const struct InternalNetworkMiddlewareData data ) +{ + /* setup a device name */ + vPublicSetupDeviceName( data.deviceName ); + + /* get the MAC address from the driver code (assuming this is also set up) */ + uint8_t uc8MACAddr[ ipMAC_ADDRESS_LENGTH_BYTES ]; + vPublicGetMACAddr( uc8MACAddr ); + + /* set up the task to reset the network every so often */ + if( data.resetNetworkTaskRunning == pdTRUE ) + { + xDelay = data.resetNetworkTaskEveryXSeconds; + xSemaphore = xSemaphoreCreateMutex(); + xTaskCreate( prvNetworkResetTask, + "resetNet", + NETWORK_TASK_MIDDLEWARE_STACK, + NULL, + tskIDLE_PRIORITY, + &xTaskToNotifyReset ); + } + + /* init the network stack */ + FreeRTOS_IPInit( data.ucIPAddress, + data.ucNetMask, + data.ucGatewayAddress, + data.ucDNSServerAddress, + uc8MACAddr ); +} + + +/* Helper function to assign bytes to an array used to indicate the IP address */ +void vConvertOctetsToAddr( uint8_t arr[ ipIP_ADDRESS_LENGTH_BYTES ], + uint8_t b0, + uint8_t b1, + uint8_t b2, + uint8_t b3 ) +{ + arr[ 0 ] = b0; + arr[ 1 ] = b1; + arr[ 2 ] = b2; + arr[ 3 ] = b3; +} /* end */ + + +/* Task that resets the network every so often */ +void prvNetworkResetTask( void * pvParameters ) +{ + uint32_t cnt; + + cnt = 0; + + for( ; ; ) + { + vTaskDelay( pdMS_TO_TICKS( 1000 ) ); + cnt++; + + if( cnt > xDelay ) + { + cnt = 0; + + if( xSemaphoreTake( xSemaphore, 0 ) == pdTRUE ) + { + FreeRTOS_NetworkDown(); + xSemaphoreGive( xSemaphore ); + } + } + } +} + + +/* Call this function from a task to prevent a network reset during a critical section of the code */ +BaseType_t publicPreventNetworkReset( const BaseType_t preventReset, + const uint32_t waitTime ) +{ + if( preventReset == pdTRUE ) + { + if( xSemaphoreTake( xSemaphore, pdMS_TO_TICKS( waitTime ) ) == pdTRUE ) + { + return pdTRUE; + } + else + { + return pdFALSE; + } + } + else /* do not prevent reset */ + { + xSemaphoreGive( xSemaphore ); + } + + return pdTRUE; +} + + +/* CALLED BY FREERTOS + * Function that sets pulNumber to a random number, and then returns pdTRUE. + * If the random number could not be obtained, then the function will return pdFALSE. */ +BaseType_t xApplicationGetRandomNumber( uint32_t * pulNumber ) +{ + *pulNumber = 0; + uint32_t num = obtain_rand32(); + + if( num == 0 ) + { + return pdFALSE; + } + + *pulNumber = num; + return pdTRUE; +} + +/* CALLED BY FREERTOS + * Function that returns a random number for TCP. This is taken to be a random number. */ +uint32_t ulApplicationGetNextSequenceNumber( uint32_t ulSourceAddress, + uint16_t usSourcePort, + uint32_t ulDestinationAddress, + uint16_t usDestinationPort ) +{ + uint32_t pulNumber = 0; + + xApplicationGetRandomNumber( &pulNumber ); + return pulNumber; +} + + +/* CALLED BY FREERTOS + * Function to obtain random number */ +UBaseType_t uxRand() +{ + uint32_t num = obtain_rand32(); + + return num; +} + + +/* CALLED BY FREERTOS + * Function called when the network connects or disconnects */ +void vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent ) +{ + uint32_t ulIPAddress, ulNetMask, ulGatewayAddress, ulDNSServerAddress; + static BaseType_t xNetworkTasksAlreadyCreated = pdFALSE; + char cBuffer[ 16 ]; + + if( eNetworkEvent == eNetworkUp ) + { + if( xNetworkTasksAlreadyCreated == pdFALSE ) + { + /* Unblock any necessary tasks here when the network is up + * Set a flag to indicate that the tasks do not need to be created again */ + xNetworkTasksAlreadyCreated = pdTRUE; + } + + FreeRTOS_GetAddressConfiguration( &ulIPAddress, + &ulNetMask, + &ulGatewayAddress, + &ulDNSServerAddress ); + + FreeRTOS_inet_ntoa( ulIPAddress, cBuffer ); + vLoggingPrintf( "IP Address: %s\r\n", cBuffer ); + + FreeRTOS_inet_ntoa( ulNetMask, cBuffer ); + vLoggingPrintf( "Subnet Mask: %s\r\n", cBuffer ); + + FreeRTOS_inet_ntoa( ulGatewayAddress, cBuffer ); + vLoggingPrintf( "Gateway IP Address: %s\r\n", cBuffer ); + + FreeRTOS_inet_ntoa( ulDNSServerAddress, cBuffer ); + vLoggingPrintf( "DNS server IP Address: %s\r\n", cBuffer ); + } /* end if */ + else if( eNetworkEvent == eNetworkDown ) + { + xNetworkTasksAlreadyCreated = pdFALSE; /* clear a flag to indicate that the tasks needs to be created again */ + /* Stop or block any running tasks here */ + } /* end if */ +} /* end */ + + +/* CALLED BY FREERTOS + * Function that indicates there is a ping response + * Called from IPTraceMacroDefaults.h + * NOTE: This function can cause the stack to slow down, so it should + * only be used for debugging. */ + +/* + #ifndef iptraceSENDING_PING_REPLY + * extern void pingReply( uint32_t ulIPAddress ); + #define iptraceSENDING_PING_REPLY( ulIPAddress ) pingReply(ulIPAddress ) + #endif + */ +void pingReply( uint32_t ulIPAddress ) +{ + #ifdef SEND_PING_PRINT_REPLY + char cBuffer[ 16 ]; + FreeRTOS_inet_ntoa( ulIPAddress, cBuffer ); + vLoggingPrintf( "Ping response to: %s\r\n", cBuffer ); + #endif +} + + +/* CALLED BY FREERTOS when conducting a DNS query + * Function that returns pdTRUE if the pcName matches the LLMNR node name */ +BaseType_t xApplicationDNSQueryHook( const char * pcName ) +{ + if( strcmp( pcName, DEV_NAME ) ) + { + return pdTRUE; + } + + return pdFALSE; +} + + +/* CALLED BY FREERTOS + * Hook to return a human-readable name */ +const char * pcApplicationHostnameHook( void ) +{ + const char * name; + + name = DEV_NAME; + return name; +} + + +/* Call this function to assign a device name before the stack is up */ +void vPublicSetupDeviceName( const char * deviceName ) +{ + memset( DEV_NAME, 0, sizeof( DEV_NAME ) ); + strncpy( DEV_NAME, deviceName, MAX_NAME_LLMNR - 1 ); +} diff --git a/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkMiddleware.h b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkMiddleware.h new file mode 100644 index 0000000..7c83615 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/NetworkMiddleware.h @@ -0,0 +1,60 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Driver code: + * Copyright (C) Nicholas J. Kinar , Centre for Hydrology, University of Saskatchewan + * + * MSP432 Driverlib (C) 2017-2019 Texas Instruments Incorporated + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#pragma once +#include +#include "FreeRTOS.h" +#include "FreeRTOS_IP.h" +#include "semphr.h" + +#define MAX_NAME_LLMNR 32 /* maximum length of the LLMNR name used in this project */ + +struct InternalNetworkMiddlewareData +{ + uint8_t ucIPAddress[ ipIP_ADDRESS_LENGTH_BYTES ]; + uint8_t ucNetMask[ ipIP_ADDRESS_LENGTH_BYTES ]; + uint8_t ucGatewayAddress[ ipIP_ADDRESS_LENGTH_BYTES ]; + uint8_t ucDNSServerAddress[ ipIP_ADDRESS_LENGTH_BYTES ]; + BaseType_t resetNetworkTaskRunning; + uint32_t resetNetworkTaskEveryXSeconds; + char deviceName[ MAX_NAME_LLMNR ]; +}; /* end */ + +void vPublicSetupFreeRTOSTasks( const struct InternalNetworkMiddlewareData data ); +void vPublicSetupDeviceName( const char * deviceName ); +BaseType_t vPublicPreventNetworkReset( const BaseType_t preventReset, + const uint32_t waitTime ); +void vConvertOctetsToAddr( uint8_t arr[ ipIP_ADDRESS_LENGTH_BYTES ], + uint8_t b0, + uint8_t b1, + uint8_t b2, + uint8_t b3 ); diff --git a/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/README.md b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/README.md new file mode 100644 index 0000000..f0eb30f --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ThirdParty/MSP432/README.md @@ -0,0 +1,75 @@ +# Scope +This is a driver and network middleware for the MSP432E401Y microcontroller +with built-in Ethernet MAC. + +# Prerequisites + +Ensure that driverlib for the MSP432E4 is installed and added to the include +path for the project. + +# Recommendation +When a MAC address changes or when there is a change in the network setup, +it is recommended to perform a hard reset of the microcontroller in lieu +of resetting only the MAC hardware. + +# List of Tasks + +The tasks listed in the table below are implemented internally by the driver (NetworkInterface.c). + +| Task Name | Purpose | +|---------------------------------------|-------------------------------------------------------------------| +| prvEMACDeferredInterruptHandlerTaskRX | RX Task | +| prvEMACDeferredInterfaceOutputTaskTX | TX Task | +| prvCheckLinkUpOrDownNetStateTask | Network State Checking Task (link up/down, network state & reset) | + +The tasks listed in the table below are implemented by additional code provided as an example +related to how the driver might be used in an application (NetworkMiddleware.c). +The additional code does not have to be used and is only provided to be useful. + +| Task Name | Purpose | +|---------------------------------------|-------------------------------------------------------------------| +| prvNetworkResetTask | Task to periodically reset the network (if required) + +# Example Code + +``` +#include "NetworkInterface.h" +#include "NetworkMiddleware.h" + +void setup_wired_ethernet() +{ + struct InternalNetworkInterfaceMSP432EConfig config; + vGetInternalNetworkInterfaceMSP432EConfigDefaults(&config); + config.setMACAddrInternal = false; /* true if the MAC address is to be read from microcontroller flash */ + config.MACAddr[0] = 0x70; /* replace with a custom MAC address */ + config.MACAddr[1] = 0xFF; + config.MACAddr[2] = 0x76; + config.MACAddr[3] = 0x1C; + config.MACAddr[4] = 0xC1; + config.MACAddr[5] = 0xD0; + vPublicSetupEMACNetwork(config); + + /* setup the network stack middleware */ + const char *devname = "device"; + struct InternalNetworkMiddlewareData setupData; + strncpy(setupData.deviceName, devname, strlen(devname)); + setupData.resetNetworkTaskEveryXSeconds = 86400; /* Restart the network every 24 hours (86400 seconds) only when setupData.resetNetworkTaskRunning == true */ + setupData.resetNetworkTaskRunning = false; /* Run the network task to reset the network every so often (i.e. to periodically obtain a new IP address) */ + + /* set the static IP address */ + vConvertOctetsToAddr(setupData.ucIPAddress, 192, 168, 1, 9); + vConvertOctetsToAddr(setupData.ucNetMask, 255, 255, 255, 0); + vConvertOctetsToAddr(setupData.ucGatewayAddress, 192, 168, 1, 1); + vConvertOctetsToAddr(setupData.ucDNSServerAddress, 192, 168, 1, 1); + + vPublicSetupFreeRTOSTasks(setupData); + /* + Start the RTOS scheduler by calling vTaskStartScheduler() + Use publicPreventNetworkReset() to block the network reset during a critical section of the code + Set the device name using vPublicSetupDeviceName() + */ +} + +``` +# Contact +Nicholas J. Kinar (n.kinar@usask.ca) or FreeRTOS Forums diff --git a/FreeRTOS/source/portable/NetworkInterface/WinPCap/FaultInjection.c b/FreeRTOS/source/portable/NetworkInterface/WinPCap/FaultInjection.c new file mode 100644 index 0000000..fadcf0a --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/WinPCap/FaultInjection.c @@ -0,0 +1,188 @@ +#define xBUFFER_CACHE_SIZE 10 +#define xMAX_FAULT_INJECTION_RATE 15 +#define xMIN_FAULT_INJECTION_RATE 3 +#define xNUM_FAULT_TYPES 1 + +static NetworkBufferDescriptor_t * xNetworkBufferCache[ xBUFFER_CACHE_SIZE ] = { 0 }; + +#define xFAULT_LOG_SIZE 2048 +uint32_t ulInjectedFault[ xFAULT_LOG_SIZE ]; +uint32_t ulFaultLogIndex = 0; + +static BaseType_t prvCachePacket( NetworkBufferDescriptor_t * pxNetworkBufferIn ) +{ + BaseType_t x, xReturn = pdFALSE; + + for( x = 0; x < xBUFFER_CACHE_SIZE; x++ ) + { + if( xNetworkBufferCache[ x ] == NULL ) + { + xNetworkBufferCache[ x ] = pxNetworkBufferIn; + xReturn = pdTRUE; + break; + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static NetworkBufferDescriptor_t * prvGetCachedPacket( void ) +{ + BaseType_t x; + NetworkBufferDescriptor_t * pxReturn = NULL; + + for( x = ( xBUFFER_CACHE_SIZE - 1 ); x >= 0; x-- ) + { + if( xNetworkBufferCache[ x ] != NULL ) + { + pxReturn = xNetworkBufferCache[ x ]; + xNetworkBufferCache[ x ] = NULL; + break; + } + } + + return pxReturn; +} +/*-----------------------------------------------------------*/ + +static NetworkBufferDescriptor_t * prvDuplicatePacket( NetworkBufferDescriptor_t * pxOriginalPacket, + const uint8_t * pucPacketData ) +{ + NetworkBufferDescriptor_t * pxReturn; + + /* Obtain a new descriptor. */ + pxReturn = pxGetNetworkBufferWithDescriptor( pxOriginalPacket->xDataLength, 0 ); + + if( pxReturn != NULL ) + { + /* Copy in the packet data. */ + pxReturn->xDataLength = pxOriginalPacket->xDataLength; + memcpy( pxReturn->pucEthernetBuffer, pucPacketData, pxOriginalPacket->xDataLength ); + } + + return pxReturn; +} +/*-----------------------------------------------------------*/ + +static NetworkBufferDescriptor_t * prvRxFaultInjection( NetworkBufferDescriptor_t * pxNetworkBufferIn, + const uint8_t * pucPacketData ) +{ + static uint32_t ulCallCount = 0, ulNextFaultCallCount = 0; + NetworkBufferDescriptor_t * pxReturn = pxNetworkBufferIn; + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + uint32_t ulFault; + + return pxNetworkBufferIn; + + ulCallCount++; + + if( ulCallCount > ulNextFaultCallCount ) + { + xApplicationGetRandomNumber( &( ulNextFaultCallCount ) ); + ulNextFaultCallCount = ulNextFaultCallCount % xMAX_FAULT_INJECTION_RATE; + + if( ulNextFaultCallCount < xMIN_FAULT_INJECTION_RATE ) + { + ulNextFaultCallCount = xMIN_FAULT_INJECTION_RATE; + } + + ulCallCount = 0; + + xApplicationGetRandomNumber( &( ulFault ) ); + ulFault = ulFault % xNUM_FAULT_TYPES; + + if( ulFaultLogIndex < xFAULT_LOG_SIZE ) + { + ulInjectedFault[ ulFaultLogIndex ] = ulFault; + ulFaultLogIndex++; + } + + switch( ulFault ) + { + case 0: + /* Just drop the packet. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBufferIn ); + pxReturn = NULL; + break; + + case 1: + + /* Store the packet in the cache for later. */ + if( prvCachePacket( pxNetworkBufferIn ) == pdTRUE ) + { + /* The packet may get sent later, it is not being sent + * now. */ + pxReturn = NULL; + } + + break; + + case 2: + /* Send a cached packet. */ + pxReturn = prvGetCachedPacket(); + + if( pxReturn != NULL ) + { + /* A cached packet was obtained so drop the original + * packet. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBufferIn ); + } + else + { + /* Could not obtain a packet from the cache so just return + * the packet that was passed in. */ + pxReturn = pxNetworkBufferIn; + } + + break; + + case 4: + + /* Send a duplicate of the packet right away. */ + pxReturn = prvDuplicatePacket( pxNetworkBufferIn, pucPacketData ); + + /* Send the original packet to the stack. */ + xRxEvent.pvData = ( void * ) pxNetworkBufferIn; + + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBufferIn ); + } + + break; + + case 5: + + /* Send both a cached packet and the current packet. */ + xRxEvent.pvData = ( void * ) prvGetCachedPacket(); + + if( xRxEvent.pvData != NULL ) + { + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBufferIn ); + } + } + + break; + + case 6: + case 7: + case 8: + + /* Store the packet in the cache for later. */ + if( prvCachePacket( pxNetworkBufferIn ) == pdTRUE ) + { + /* The packet may get sent later, it is not being sent + * now. */ + pxReturn = NULL; + } + + break; + } + } + + return pxReturn; +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/WinPCap/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/WinPCap/NetworkInterface.c new file mode 100644 index 0000000..02338bc --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/WinPCap/NetworkInterface.c @@ -0,0 +1,723 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#include + +/* WinPCap includes. */ +#define HAVE_REMOTE +#include "pcap.h" + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" + +/* Thread-safe circular buffers are being used to pass data to and from the PCAP + * access functions. */ +#include "Win32-Extensions.h" +#include "FreeRTOS_Stream_Buffer.h" + +/* Sizes of the thread safe circular buffers used to pass data to and from the + * WinPCAP Windows threads. */ +#define xSEND_BUFFER_SIZE 32768 +#define xRECV_BUFFER_SIZE 32768 + +/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet + * driver will filter incoming packets and only pass the stack those packets it + * considers need processing. */ +#if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +/* Used to insert test code only. */ +#define niDISRUPT_PACKETS 0 + +/*-----------------------------------------------------------*/ + +/* + * Windows threads that are outside of the control of the FreeRTOS simulator are + * used to interface with the WinPCAP libraries. + */ +DWORD WINAPI prvWinPcapRecvThread( void * pvParam ); +DWORD WINAPI prvWinPcapSendThread( void * pvParam ); + +/* + * Print out a numbered list of network interfaces that are available on the + * host computer. + */ +static pcap_if_t * prvPrintAvailableNetworkInterfaces( void ); + +/* + * Open the network interface. The number of the interface to be opened is set + * by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h. + */ +static void prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces ); +static int prvOpenInterface( const char * pucName ); + +/* + * Configure the capture filter to allow blocking reads, and to filter out + * packets that are not of interest to this demo. + */ +static void prvConfigureCaptureBehaviour( void ); + +/* + * A function that simulates Ethernet interrupts by periodically polling the + * WinPCap interface for new data. + */ +static void prvInterruptSimulatorTask( void * pvParameters ); + +/* + * Create the buffers that are used to pass data between the FreeRTOS simulator + * and the Win32 threads that manage WinPCAP. + */ +static void prvCreateThreadSafeBuffers( void ); + +/* + * Utility function used to format print messages only. + */ +static const char * prvRemoveSpaces( char * pcBuffer, + int aBuflen, + const char * pcMessage ); + +/*-----------------------------------------------------------*/ + +/* Required by the WinPCap library. */ +static char cErrorBuffer[ PCAP_ERRBUF_SIZE ]; + +/* An event used to wake up the Win32 thread that sends data through the WinPCAP + * library. */ +static void * pvSendEvent = NULL; + +/* _HT_ made the PCAP interface number configurable through the program's + * parameters in order to test in different machines. */ +static BaseType_t xConfigNetworkInterfaceToUse = configNETWORK_INTERFACE_TO_USE; + +/* Handles to the Windows threads that handle the PCAP IO. */ +static HANDLE vWinPcapRecvThreadHandle = NULL; +static HANDLE vWinPcapSendThreadHandle = NULL; + +/* The interface being used by WinPCap. */ +static pcap_t * pxOpenedInterfaceHandle = NULL; + +/* Circular buffers used by the PCAP Win32 threads. */ +static StreamBuffer_t * xSendBuffer = NULL; +static StreamBuffer_t * xRecvBuffer = NULL; + +/* Logs the number of WinPCAP send failures, for viewing in the debugger only. */ +static volatile uint32_t ulWinPCAPSendFailures = 0; + +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + BaseType_t xReturn = pdFALSE; + pcap_if_t * pxAllNetworkInterfaces; + + /* Query the computer the simulation is being executed on to find the + * network interfaces it has installed. */ + pxAllNetworkInterfaces = prvPrintAvailableNetworkInterfaces(); + + /* Open the network interface. The number of the interface to be opened is + * set by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h. + * Calling this function will set the pxOpenedInterfaceHandle variable. If, + * after calling this function, pxOpenedInterfaceHandle is equal to NULL, then + * the interface could not be opened. */ + if( pxAllNetworkInterfaces != NULL ) + { + prvOpenSelectedNetworkInterface( pxAllNetworkInterfaces ); + } + + if( pxOpenedInterfaceHandle != NULL ) + { + xReturn = pdPASS; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvCreateThreadSafeBuffers( void ) +{ + /* The buffer used to pass data to be transmitted from a FreeRTOS task to + * the Win32 thread that sends via the WinPCAP library. */ + if( xSendBuffer == NULL ) + { + xSendBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) + xSEND_BUFFER_SIZE + 1 ); + configASSERT( xSendBuffer ); + memset( xSendBuffer, '\0', sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) ); + xSendBuffer->LENGTH = xSEND_BUFFER_SIZE + 1; + } + + /* The buffer used to pass received data from the Win32 thread that receives + * via the WinPCAP library to the FreeRTOS task. */ + if( xRecvBuffer == NULL ) + { + xRecvBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) + xRECV_BUFFER_SIZE + 1 ); + configASSERT( xRecvBuffer ); + memset( xRecvBuffer, '\0', sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) ); + xRecvBuffer->LENGTH = xRECV_BUFFER_SIZE + 1; + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t bReleaseAfterSend ) +{ + size_t xSpace; + + iptraceNETWORK_INTERFACE_TRANSMIT(); + configASSERT( xIsCallingFromIPTask() == pdTRUE ); + + /* Both the length of the data being sent and the actual data being sent + * are placed in the thread safe buffer used to pass data between the FreeRTOS + * tasks and the Win32 thread that sends data via the WinPCAP library. Drop + * the packet if there is insufficient space in the buffer to hold both. */ + xSpace = uxStreamBufferGetSpace( xSendBuffer ); + + if( ( pxNetworkBuffer->xDataLength <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) && + ( xSpace >= ( pxNetworkBuffer->xDataLength + sizeof( pxNetworkBuffer->xDataLength ) ) ) ) + { + /* First write in the length of the data, then write in the data + * itself. */ + uxStreamBufferAdd( xSendBuffer, 0, ( const uint8_t * ) &( pxNetworkBuffer->xDataLength ), sizeof( pxNetworkBuffer->xDataLength ) ); + uxStreamBufferAdd( xSendBuffer, 0, ( const uint8_t * ) pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); + } + else + { + FreeRTOS_debug_printf( ( "xNetworkInterfaceOutput: send buffers full to store %lu\n", pxNetworkBuffer->xDataLength ) ); + } + + /* Kick the Tx task in either case in case it doesn't know the buffer is + * full. */ + SetEvent( pvSendEvent ); + + /* The buffer has been sent so can be released. */ + if( bReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + return pdPASS; +} +/*-----------------------------------------------------------*/ + +static pcap_if_t * prvPrintAvailableNetworkInterfaces( void ) +{ + pcap_if_t * pxAllNetworkInterfaces = NULL, * xInterface; + int32_t lInterfaceNumber = 1; + char cBuffer[ 512 ]; + static BaseType_t xInvalidInterfaceDetected = pdFALSE; + + if( xInvalidInterfaceDetected == pdFALSE ) + { + if( pcap_findalldevs_ex( PCAP_SRC_IF_STRING, NULL, &pxAllNetworkInterfaces, cErrorBuffer ) == -1 ) + { + printf( "Could not obtain a list of network interfaces\n%s\n", cErrorBuffer ); + pxAllNetworkInterfaces = NULL; + } + else + { + printf( "\r\n\r\nThe following network interfaces are available:\r\n\r\n" ); + } + + if( pxAllNetworkInterfaces != NULL ) + { + /* Print out the list of network interfaces. The first in the list + * is interface '1', not interface '0'. */ + for( xInterface = pxAllNetworkInterfaces; xInterface != NULL; xInterface = xInterface->next ) + { + /* The descriptions of the devices can be full of spaces, clean them + * a little. printf() can only be used here because the network is not + * up yet - so no other network tasks will be running. */ + printf( "Interface %d - %s\n", lInterfaceNumber, prvRemoveSpaces( cBuffer, sizeof( cBuffer ), xInterface->name ) ); + printf( " (%s)\n", prvRemoveSpaces( cBuffer, sizeof( cBuffer ), xInterface->description ? xInterface->description : "No description" ) ); + printf( "\n" ); + lInterfaceNumber++; + } + } + + if( lInterfaceNumber == 1 ) + { + /* The interface number was never incremented, so the above for() loop + * did not execute meaning no interfaces were found. */ + printf( " \nNo network interfaces were found.\n" ); + pxAllNetworkInterfaces = NULL; + } + + printf( "\r\nThe interface that will be opened is set by " ); + printf( "\"configNETWORK_INTERFACE_TO_USE\", which\r\nshould be defined in FreeRTOSConfig.h\r\n" ); + + if( ( xConfigNetworkInterfaceToUse < 1L ) || ( xConfigNetworkInterfaceToUse >= lInterfaceNumber ) ) + { + printf( "\r\nERROR: configNETWORK_INTERFACE_TO_USE is set to %d, which is an invalid value.\r\n", xConfigNetworkInterfaceToUse ); + printf( "Please set configNETWORK_INTERFACE_TO_USE to one of the interface numbers listed above,\r\n" ); + printf( "then re-compile and re-start the application. Only Ethernet (as opposed to WiFi)\r\n" ); + printf( "interfaces are supported.\r\n\r\nHALTING\r\n\r\n\r\n" ); + xInvalidInterfaceDetected = pdTRUE; + + if( pxAllNetworkInterfaces != NULL ) + { + /* Free the device list, as no devices are going to be opened. */ + pcap_freealldevs( pxAllNetworkInterfaces ); + pxAllNetworkInterfaces = NULL; + } + } + else + { + printf( "Attempting to open interface number %d.\n", xConfigNetworkInterfaceToUse ); + } + } + + return pxAllNetworkInterfaces; +} +/*-----------------------------------------------------------*/ + +static int prvOpenInterface( const char * pucName ) +{ + static char pucInterfaceName[ 256 ]; + + if( pucName != NULL ) + { + strncpy( pucInterfaceName, pucName, sizeof( pucInterfaceName ) ); + } + + pxOpenedInterfaceHandle = pcap_open( pucInterfaceName, /* The name of the selected interface. */ + ipTOTAL_ETHERNET_FRAME_SIZE, /* The size of the packet to capture. */ + PCAP_OPENFLAG_PROMISCUOUS, /* Open in promiscuous mode as the MAC and + * IP address is going to be "simulated", and + * not be the real MAC and IP address. This allows + * traffic to the simulated IP address to be routed + * to uIP, and traffic to the real IP address to be + * routed to the Windows TCP/IP stack. */ + 100, + NULL, /* No authentication is required as this is + * not a remote capture session. */ + cErrorBuffer + ); + + if( pxOpenedInterfaceHandle == NULL ) + { + printf( "\n%s is not supported by WinPcap and cannot be opened\n", pucInterfaceName ); + return 1; + } + else + { + /* Configure the capture filter to allow blocking reads, and to filter + * out packets that are not of interest to this demo. */ + prvConfigureCaptureBehaviour(); + } + + return 0; +} +/*-----------------------------------------------------------*/ + +static void prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces ) +{ + pcap_if_t * pxInterface; + int32_t x; + + /* Walk the list of devices until the selected device is located. */ + pxInterface = pxAllNetworkInterfaces; + + for( x = 0L; x < ( xConfigNetworkInterfaceToUse - 1L ); x++ ) + { + pxInterface = pxInterface->next; + } + + /* Open the selected interface. */ + if( prvOpenInterface( pxInterface->name ) == 0 ) + { + printf( "Successfully opened interface number %d.\n", x + 1 ); + } + else + { + printf( "Failed to open interface number %d.\n", x + 1 ); + } + + /* The device list is no longer required. */ + pcap_freealldevs( pxAllNetworkInterfaces ); +} +/*-----------------------------------------------------------*/ + +static void prvConfigureCaptureBehaviour( void ) +{ + struct bpf_program xFilterCode; + uint32_t ulNetMask; + + /* Set up a filter so only the packets of interest are passed to the IP + * stack. cErrorBuffer is used for convenience to create the string. Don't + * confuse this with an error message. */ + sprintf( cErrorBuffer, "broadcast or multicast or ether host %x:%x:%x:%x:%x:%x", + ipLOCAL_MAC_ADDRESS[ 0 ], ipLOCAL_MAC_ADDRESS[ 1 ], ipLOCAL_MAC_ADDRESS[ 2 ], ipLOCAL_MAC_ADDRESS[ 3 ], ipLOCAL_MAC_ADDRESS[ 4 ], ipLOCAL_MAC_ADDRESS[ 5 ] ); + + ulNetMask = ( configNET_MASK3 << 24UL ) | ( configNET_MASK2 << 16UL ) | ( configNET_MASK1 << 8L ) | configNET_MASK0; + + if( pcap_compile( pxOpenedInterfaceHandle, &xFilterCode, cErrorBuffer, 1, ulNetMask ) < 0 ) + { + printf( "\nThe packet filter string is invalid\n" ); + } + else + { + if( pcap_setfilter( pxOpenedInterfaceHandle, &xFilterCode ) < 0 ) + { + printf( "\nAn error occurred setting the packet filter.\n" ); + } + + /* When pcap_compile() succeeds, it allocates memory for the memory pointed to by the bpf_program struct + * parameter.pcap_freecode() will free that memory. */ + pcap_freecode( &xFilterCode ); + } + + /* Create the buffers used to pass packets between the FreeRTOS simulator + * and the Win32 threads that are handling WinPCAP. */ + prvCreateThreadSafeBuffers(); + + if( pvSendEvent == NULL ) + { + /* Create event used to signal the Win32 WinPCAP Tx thread. */ + pvSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL ); + + /* Create the Win32 thread that handles WinPCAP Rx. */ + vWinPcapRecvThreadHandle = CreateThread( + NULL, /* Pointer to thread security attributes. */ + 0, /* Initial thread stack size, in bytes. */ + prvWinPcapRecvThread, /* Pointer to thread function. */ + NULL, /* Argument for new thread. */ + 0, /* Creation flags. */ + NULL ); + + /* Use the cores that are not used by the FreeRTOS tasks. */ + SetThreadAffinityMask( vWinPcapRecvThreadHandle, ~0x01u ); + + /* Create the Win32 thread that handlers WinPCAP Tx. */ + vWinPcapSendThreadHandle = CreateThread( + NULL, /* Pointer to thread security attributes. */ + 0, /* initial thread stack size, in bytes. */ + prvWinPcapSendThread, /* Pointer to thread function. */ + NULL, /* Argument for new thread. */ + 0, /* Creation flags. */ + NULL ); + + /* Use the cores that are not used by the FreeRTOS tasks. */ + SetThreadAffinityMask( vWinPcapSendThreadHandle, ~0x01u ); + + /* Create a task that simulates an interrupt in a real system. This will + * block waiting for packets, then send a message to the IP task when data + * is available. */ + xTaskCreate( prvInterruptSimulatorTask, "MAC_ISR", configMINIMAL_STACK_SIZE, NULL, configMAC_ISR_SIMULATOR_PRIORITY, NULL ); + } +} +/*-----------------------------------------------------------*/ + +/* WinPCAP function. */ +void pcap_callback( u_char * user, + const struct pcap_pkthdr * pkt_header, + const u_char * pkt_data ) +{ + ( void ) user; + + /* THIS IS CALLED FROM A WINDOWS THREAD - DO NOT ATTEMPT ANY FREERTOS CALLS + * OR TO PRINT OUT MESSAGES HERE. */ + + /* Pass data to the FreeRTOS simulator on a thread safe circular buffer. */ + if( ( pkt_header->caplen <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) && + ( uxStreamBufferGetSpace( xRecvBuffer ) >= ( ( ( size_t ) pkt_header->caplen ) + sizeof( *pkt_header ) ) ) ) + { + /* The received packets will be written to a C source file, + * only if 'ipconfigUSE_DUMP_PACKETS' is defined. + * Otherwise, there is no action. */ + iptraceDUMP_PACKET( ( const uint8_t * ) pkt_data, ( size_t ) pkt_header->caplen, pdTRUE ); + + uxStreamBufferAdd( xRecvBuffer, 0, ( const uint8_t * ) pkt_header, sizeof( *pkt_header ) ); + uxStreamBufferAdd( xRecvBuffer, 0, ( const uint8_t * ) pkt_data, ( size_t ) pkt_header->caplen ); + } +} +/*-----------------------------------------------------------*/ + +DWORD WINAPI prvWinPcapRecvThread( void * pvParam ) +{ + ( void ) pvParam; + + /* THIS IS A WINDOWS THREAD - DO NOT ATTEMPT ANY FREERTOS CALLS OR TO PRINT + * OUT MESSAGES HERE. */ + + for( ; ; ) + { + pcap_dispatch( pxOpenedInterfaceHandle, 1, pcap_callback, ( u_char * ) "mydata" ); + } +} +/*-----------------------------------------------------------*/ + +DWORD WINAPI prvWinPcapSendThread( void * pvParam ) +{ + size_t xLength; + uint8_t ucBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ]; + static char cErrorMessage[ 1024 ]; + const DWORD xMaxMSToWait = 1000; + + /* THIS IS A WINDOWS THREAD - DO NOT ATTEMPT ANY FREERTOS CALLS OR TO PRINT + * OUT MESSAGES HERE. */ + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParam; + + for( ; ; ) + { + /* Wait until notified of something to send. */ + WaitForSingleObject( pvSendEvent, xMaxMSToWait ); + + /* Is there more than the length value stored in the circular buffer + * used to pass data from the FreeRTOS simulator into this Win32 thread? */ + while( uxStreamBufferGetSize( xSendBuffer ) > sizeof( xLength ) ) + { + uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) &xLength, sizeof( xLength ), pdFALSE ); + uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) ucBuffer, xLength, pdFALSE ); + + /* The packets sent will be written to a C source file, + * only if 'ipconfigUSE_DUMP_PACKETS' is defined. + * Otherwise, there is no action. */ + iptraceDUMP_PACKET( ucBuffer, xLength, pdFALSE ); + + if( pcap_sendpacket( pxOpenedInterfaceHandle, ucBuffer, xLength ) != 0 ) + { + ulWinPCAPSendFailures++; + } + } + } +} +/*-----------------------------------------------------------*/ + +static BaseType_t xPacketBouncedBack( const uint8_t * pucBuffer ) +{ + EthernetHeader_t * pxEtherHeader; + BaseType_t xResult; + + pxEtherHeader = ( EthernetHeader_t * ) pucBuffer; + + /* Sometimes, packets are bounced back by the driver and we need not process them. Check + * whether this packet is one such packet. */ + if( memcmp( ipLOCAL_MAC_ADDRESS, pxEtherHeader->xSourceAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ) == 0 ) + { + xResult = pdTRUE; + } + else + { + xResult = pdFALSE; + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +static void prvInterruptSimulatorTask( void * pvParameters ) +{ + struct pcap_pkthdr xHeader; + static struct pcap_pkthdr * pxHeader; + const uint8_t * pucPacketData; + uint8_t ucRecvBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ]; + NetworkBufferDescriptor_t * pxNetworkBuffer; + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + eFrameProcessingResult_t eResult; + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + for( ; ; ) + { + /* Does the circular buffer used to pass data from the Win32 thread that + * handles WinPCAP Rx into the FreeRTOS simulator contain another packet? */ + if( uxStreamBufferGetSize( xRecvBuffer ) > sizeof( xHeader ) ) + { + /* Get the next packet. */ + uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) &xHeader, sizeof( xHeader ), pdFALSE ); + uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) ucRecvBuffer, ( size_t ) xHeader.len, pdFALSE ); + pucPacketData = ucRecvBuffer; + pxHeader = &xHeader; + + iptraceNETWORK_INTERFACE_RECEIVE(); + + /* Check for minimal size. */ + if( pxHeader->len >= sizeof( EthernetHeader_t ) ) + { + eResult = ipCONSIDER_FRAME_FOR_PROCESSING( pucPacketData ); + } + else + { + eResult = eReleaseBuffer; + } + + if( eResult == eProcessBuffer ) + { + /* Will the data fit into the frame buffer? */ + if( pxHeader->len <= ipTOTAL_ETHERNET_FRAME_SIZE ) + { + /* Obtain a buffer into which the data can be placed. This + * is only an interrupt simulator, not a real interrupt, so it + * is ok to call the task level function here, but note that + * some buffer implementations cannot be called from a real + * interrupt. */ + if( xPacketBouncedBack( pucPacketData ) == pdFALSE ) + { + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( pxHeader->len, 0 ); + } + else + { + pxNetworkBuffer = NULL; + } + + if( pxNetworkBuffer != NULL ) + { + memcpy( pxNetworkBuffer->pucEthernetBuffer, pucPacketData, pxHeader->len ); + pxNetworkBuffer->xDataLength = ( size_t ) pxHeader->len; + + #if ( niDISRUPT_PACKETS == 1 ) + { + pxNetworkBuffer = vRxFaultInjection( pxNetworkBuffer, pucPacketData ); + } + #endif /* niDISRUPT_PACKETS */ + + if( pxNetworkBuffer != NULL ) + { + xRxEvent.pvData = ( void * ) pxNetworkBuffer; + + /* Data was received and stored. Send a message to + * the IP task to let it know. */ + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL ) + { + /* The buffer could not be sent to the stack so + * must be released again. This is only an + * interrupt simulator, not a real interrupt, so it + * is ok to use the task level function here, but + * note no all buffer implementations will allow + * this function to be executed from a real + * interrupt. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + iptraceETHERNET_RX_EVENT_LOST(); + } + } + else + { + /* The packet was already released or stored inside + * vRxFaultInjection(). Don't release it here. */ + } + } + else + { + iptraceETHERNET_RX_EVENT_LOST(); + } + } + else + { + /* Log that a packet was dropped because it would have + * overflowed the buffer, but there may be more buffers to + * process. */ + } + } + } + else + { + /* There is no real way of simulating an interrupt. Make sure + * other tasks can run. */ + vTaskDelay( configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY ); + } + } +} +/*-----------------------------------------------------------*/ + +static const char * prvRemoveSpaces( char * pcBuffer, + int aBuflen, + const char * pcMessage ) +{ + char * pcTarget = pcBuffer; + + /* Utility function used to format messages being printed only. */ + while( ( *pcMessage != 0 ) && ( pcTarget < ( pcBuffer + aBuflen - 1 ) ) ) + { + *( pcTarget++ ) = *pcMessage; + + if( isspace( *pcMessage ) != pdFALSE ) + { + while( isspace( *pcMessage ) != pdFALSE ) + { + pcMessage++; + } + } + else + { + pcMessage++; + } + } + + *pcTarget = '\0'; + + return pcBuffer; +} + +#define BUFFER_SIZE ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING ) +#define BUFFER_SIZE_ROUNDED_UP ( ( BUFFER_SIZE + 7 ) & ~0x07UL ) + +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + static uint8_t * pucNetworkPacketBuffers = NULL; + size_t uxIndex; + + if( pucNetworkPacketBuffers == NULL ) + { + pucNetworkPacketBuffers = ( uint8_t * ) malloc( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * BUFFER_SIZE_ROUNDED_UP ); + } + + if( pucNetworkPacketBuffers == NULL ) + { + FreeRTOS_printf( ( "Failed to allocate memory for pxNetworkBuffers" ) ); + configASSERT( 0 ); + } + else + { + for( uxIndex = 0; uxIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; uxIndex++ ) + { + size_t uxOffset = uxIndex * BUFFER_SIZE_ROUNDED_UP; + NetworkBufferDescriptor_t ** ppDescriptor; + + /* At the beginning of each pbuff is a pointer to the relevant descriptor */ + ppDescriptor = ( NetworkBufferDescriptor_t ** ) &( pucNetworkPacketBuffers[ uxOffset ] ); + + /* Set this pointer to the address of the correct descriptor */ + *ppDescriptor = &( pxNetworkBuffers[ uxIndex ] ); + + /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the + * beginning of the allocated buffer. */ + pxNetworkBuffers[ uxIndex ].pucEthernetBuffer = &( pucNetworkPacketBuffers[ uxOffset + ipBUFFER_PADDING ] ); + } + } +} diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/Zynq/NetworkInterface.c new file mode 100644 index 0000000..91cf3a3 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/NetworkInterface.c @@ -0,0 +1,423 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +/* Xilinx library files. */ +#include +#include "Zynq/x_topology.h" +#include "Zynq/x_emacpsif.h" +#include "Zynq/x_emacpsif_hw.h" + +/* Provided memory configured as uncached. */ +#include "uncached_memory.h" + +#ifndef niEMAC_HANDLER_TASK_PRIORITY + /* Define the priority of the task prvEMACHandlerTask(). */ + #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1 +#endif + +#define niBMSR_LINK_STATUS 0x0004uL + +#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 +#endif + +/* The size of each buffer when BufferAllocation_1 is used: + * http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html */ +#define niBUFFER_1_PACKET_SIZE 1536 + +/* Naming and numbering of PHY registers. */ +#define PHY_REG_01_BMSR 0x01 /* Basic mode status register */ + +#ifndef iptraceEMAC_TASK_STARTING + #define iptraceEMAC_TASK_STARTING() do {} while( ipFALSE_BOOL ) +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to 8 times + * the size of the stack used by the idle task - but allow this to be overridden in + * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 8 * configMINIMAL_STACK_SIZE ) +#endif + +#if ( ipconfigZERO_COPY_RX_DRIVER == 0 || ipconfigZERO_COPY_TX_DRIVER == 0 ) + #error Please define both 'ipconfigZERO_COPY_RX_DRIVER' and 'ipconfigZERO_COPY_TX_DRIVER' as 1 +#endif + +#if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 || ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) + #warning Please define both 'ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM' and 'ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM' as 1 +#endif +/*-----------------------------------------------------------*/ + +/* + * Look for the link to be up every few milliseconds until either xMaxTime time + * has passed or a link is found. + */ +static BaseType_t prvGMACWaitLS( TickType_t xMaxTime ); + +/* + * A deferred interrupt handler for all MAC/DMA interrupt sources. + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/*-----------------------------------------------------------*/ + +/* EMAC data/descriptions. */ +static xemacpsif_s xEMACpsif; +struct xtopology_t xXTopology = +{ + .emac_baseaddr = XPAR_PS7_ETHERNET_0_BASEADDR, + .emac_type = xemac_type_emacps, + .intc_baseaddr = 0x0, + .intc_emac_intr = 0x0, + .scugic_baseaddr = XPAR_PS7_SCUGIC_0_BASEADDR, + .scugic_emac_intr = 0x36, +}; + +XEmacPs_Config mac_config = +{ + .DeviceId = XPAR_PS7_ETHERNET_0_DEVICE_ID, /**< Unique ID of device */ + .BaseAddress = XPAR_PS7_ETHERNET_0_BASEADDR /**< Physical base address of IPIF registers */ +}; + +extern int phy_detected; + +/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ +static uint32_t ulPHYLinkStatus = 0uL; + +#if ( ipconfigUSE_LLMNR == 1 ) + static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; +#endif + +/* Holds the handle of the task used as a deferred interrupt processor. The + * handle is used so direct notifications can be sent to the task for all EMAC/DMA + * related interrupts. */ +TaskHandle_t xEMACTaskHandle = NULL; + +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + uint32_t ulLinkSpeed, ulDMAReg; + BaseType_t xStatus, xLinkStatus; + XEmacPs * pxEMAC_PS; + const TickType_t xWaitLinkDelay = pdMS_TO_TICKS( 7000UL ), xWaitRelinkDelay = pdMS_TO_TICKS( 1000UL ); + + /* Guard against the init function being called more than once. */ + if( xEMACTaskHandle == NULL ) + { + pxEMAC_PS = &( xEMACpsif.emacps ); + memset( &xEMACpsif, '\0', sizeof( xEMACpsif ) ); + + xStatus = XEmacPs_CfgInitialize( pxEMAC_PS, &mac_config, mac_config.BaseAddress ); + + if( xStatus != XST_SUCCESS ) + { + FreeRTOS_printf( ( "xEMACInit: EmacPs Configuration Failed....\n" ) ); + } + + /* Initialize the mac and set the MAC address. */ + XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) ipLOCAL_MAC_ADDRESS, 1 ); + + #if ( ipconfigUSE_LLMNR == 1 ) + { + /* Also add LLMNR multicast MAC address. */ + XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) xLLMNR_MACAddress, 2 ); + } + #endif /* ipconfigUSE_LLMNR == 1 */ + + XEmacPs_SetMdioDivisor( pxEMAC_PS, MDC_DIV_224 ); + ulLinkSpeed = Phy_Setup( pxEMAC_PS ); + XEmacPs_SetOperatingSpeed( pxEMAC_PS, ulLinkSpeed ); + + /* Setting the operating speed of the MAC needs a delay. */ + vTaskDelay( pdMS_TO_TICKS( 25UL ) ); + + ulDMAReg = XEmacPs_ReadReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET ); + + /* DISC_WHEN_NO_AHB: when set, the GEM DMA will automatically discard receive + * packets from the receiver packet buffer memory when no AHB resource is available. */ + XEmacPs_WriteReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET, + ulDMAReg | XEMACPS_DMACR_DISC_WHEN_NO_AHB_MASK ); + + setup_isr( &xEMACpsif ); + init_dma( &xEMACpsif ); + start_emacps( &xEMACpsif ); + + prvGMACWaitLS( xWaitLinkDelay ); + + /* The deferred interrupt handler task is created at the highest + * possible priority to ensure the interrupt handler can return directly + * to it. The task's handle is stored in xEMACTaskHandle so interrupts can + * notify the task when there is something to process. */ + xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle ); + } + else + { + /* Initialisation was already performed, just wait for the link. */ + prvGMACWaitLS( xWaitRelinkDelay ); + } + + /* Only return pdTRUE when the Link Status of the PHY is high, otherwise the + * DHCP process and all other communication will fail. */ + xLinkStatus = xGetPhyLinkStatus(); + + return( xLinkStatus != pdFALSE ); +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxBuffer, + BaseType_t bReleaseAfterSend ) +{ + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + { + ProtocolPacket_t * pxPacket; + + /* If the peripheral must calculate the checksum, it wants + * the protocol checksum to have a value of zero. */ + pxPacket = ( ProtocolPacket_t * ) ( pxBuffer->pucEthernetBuffer ); + + if( ( pxPacket->xICMPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) && + ( pxPacket->xICMPPacket.xIPHeader.ucProtocol != ipPROTOCOL_UDP ) && + ( pxPacket->xICMPPacket.xIPHeader.ucProtocol != ipPROTOCOL_TCP ) ) + { + /* The EMAC will calculate the checksum of the IP-header. + * It can only calculate protocol checksums of UDP and TCP, + * so for ICMP and other protocols it must be done manually. */ + usGenerateProtocolChecksum( ( uint8_t * ) &( pxPacket->xUDPPacket ), pxBuffer->xDataLength, pdTRUE ); + } + } + #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */ + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0UL ) + { + iptraceNETWORK_INTERFACE_TRANSMIT(); + emacps_send_message( &xEMACpsif, pxBuffer, bReleaseAfterSend ); + } + else if( bReleaseAfterSend != pdFALSE ) + { + /* No link. */ + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + } + + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +static inline unsigned long ulReadMDIO( unsigned ulRegister ) +{ + uint16_t usValue; + + XEmacPs_PhyRead( &( xEMACpsif.emacps ), phy_detected, ulRegister, &usValue ); + return usValue; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvGMACWaitLS( TickType_t xMaxTime ) +{ + TickType_t xStartTime, xEndTime; + const TickType_t xShortDelay = pdMS_TO_TICKS( 20UL ); + BaseType_t xReturn; + + xStartTime = xTaskGetTickCount(); + + for( ; ; ) + { + xEndTime = xTaskGetTickCount(); + + if( xEndTime - xStartTime > xMaxTime ) + { + xReturn = pdFALSE; + break; + } + + ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0uL ) + { + xReturn = pdTRUE; + break; + } + + vTaskDelay( xShortDelay ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__( ( aligned( 32 ) ) ); + 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; + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ + BaseType_t xReturn; + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) == 0uL ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void * pvParameters ) +{ + TimeOut_t xPhyTime; + TickType_t xPhyRemTime; + BaseType_t xResult = 0; + uint32_t xStatus; + const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + /* A possibility to set some additional task properties like calling + * portTASK_USES_FLOATING_POINT() */ + iptraceEMAC_TASK_STARTING(); + + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + + for( ; ; ) + { + #if ( ipconfigHAS_PRINTF != 0 ) + { + /* Call a function that monitors resources: the amount of free network + * buffers and the amount of free space on the heap. See FreeRTOS_IP.c + * for more detailed comments. */ + vPrintResourceStats(); + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + + if( ( xEMACpsif.isr_events & EMAC_IF_ALL_EVENT ) == 0 ) + { + /* No events to process now, wait for the next. */ + ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); + } + + if( ( xEMACpsif.isr_events & EMAC_IF_RX_EVENT ) != 0 ) + { + xEMACpsif.isr_events &= ~EMAC_IF_RX_EVENT; + xResult = emacps_check_rx( &xEMACpsif ); + } + + if( ( xEMACpsif.isr_events & EMAC_IF_TX_EVENT ) != 0 ) + { + xEMACpsif.isr_events &= ~EMAC_IF_TX_EVENT; + emacps_check_tx( &xEMACpsif ); + } + + if( ( xEMACpsif.isr_events & EMAC_IF_ERR_EVENT ) != 0 ) + { + xEMACpsif.isr_events &= ~EMAC_IF_ERR_EVENT; + emacps_check_errors( &xEMACpsif ); + } + + if( 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; + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) == 0uL ) + { + /* Indicate that the Link Status is high, so that + * xNetworkInterfaceOutput() can send packets. */ + ulPHYLinkStatus |= niBMSR_LINK_STATUS; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS assume 1\n" ) ); + } + } + else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) + { + xStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != ( xStatus & niBMSR_LINK_STATUS ) ) + { + ulPHYLinkStatus = xStatus; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0uL ) ); + } + + vTaskSetTimeOutState( &xPhyTime ); + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0uL ) + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); + } + else + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + } + } + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/README.txt b/FreeRTOS/source/portable/NetworkInterface/Zynq/README.txt new file mode 100644 index 0000000..a1185c1 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/README.txt @@ -0,0 +1,42 @@ + + +NetworkInterface for Xilinx' Zynq + +Please include the following source files: + + $(PLUS_TCP_PATH)/portable/NetworkInterface/Zynq/NetworkInterface.c + $(PLUS_TCP_PATH)/portable/NetworkInterface/Zynq/uncached_memory.c + $(PLUS_TCP_PATH)/portable/NetworkInterface/Zynq/x_emacpsif_dma.c + $(PLUS_TCP_PATH)/portable/NetworkInterface/Zynq/x_emacpsif_physpeed.c + $(PLUS_TCP_PATH)/portable/NetworkInterface/Zynq/x_emacpsif_hw.c + +The file uncached_memory.c can also be found in: + + vendors\xilinx\boards\microzed\aws_demos\application_code\xilinx_code + vendors\xilinx\boards\microzed\aws_tests\application_code\xilinx_code + +And include the following source files from the Xilinx library: + + $(CPU_PATH)/$(PROCESSOR)/libsrc/emacps_v2_0/src/xemacps.c + $(CPU_PATH)/$(PROCESSOR)/libsrc/emacps_v2_0/src/xemacps_control.c + $(CPU_PATH)/$(PROCESSOR)/libsrc/emacps_v2_0/src/xemacps_g.c + $(CPU_PATH)/$(PROCESSOR)/libsrc/emacps_v2_0/src/xemacps_intr.c + + E.g. ps7_cortexa9_0/libsrc/emacps_v2_0/src/xemacps_intr.c + +The following source files are NOT used for the FreeRTOS+TCP interface: + + $(CPU_PATH)/$(PROCESSOR)/libsrc/emacps_v2_0/src/xemacps_bdring.c + $(CPU_PATH)/$(PROCESSOR)/libsrc/emacps_v2_0/src/xemacps_hw.c + $(CPU_PATH)/$(PROCESSOR)/libsrc/emacps_v2_0/src/xemacps_sinit.c + +It is recommended to have these defined : + +#define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 +#define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 +#define ipconfigUSE_LINKED_RX_MESSAGES 1 + +It is obligatory to define: + +#define ipconfigZERO_COPY_RX_DRIVER 1 +#define ipconfigZERO_COPY_TX_DRIVER 1 diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/uncached_memory.c b/FreeRTOS/source/portable/NetworkInterface/Zynq/uncached_memory.c new file mode 100644 index 0000000..692a521 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/uncached_memory.c @@ -0,0 +1,166 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* + * 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. + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" + +#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 "uncached_memory.h" + +/* Reserve 1 MB of memory. */ +#define uncMEMORY_SIZE 0x100000uL + +/* Make sure that each pointer has an alignment of 4 KB. */ +#define uncALIGNMENT_SIZE 0x1000uL + +#define DDR_MEMORY_END ( XPAR_PS7_DDR_0_S_AXI_HIGHADDR + 1 ) + +#define uncMEMORY_ATTRIBUTE 0x1C02 + +static void vInitialiseUncachedMemory( void ); + +static uint8_t * pucHeadOfMemory; +static uint32_t ulMemorySize; +static uint8_t * pucStartOfMemory = NULL; + +/* The linker file defines some pseudo variables. '_end' is one of them. + * It is located at the first free byte in RAM. */ +extern u8 _end; + +/*-----------------------------------------------------------*/ + +uint8_t ucIsCachedMemory( const uint8_t * pucBuffer ) +{ + uint8_t ucReturn; + + if( ( pucStartOfMemory != NULL ) && + ( pucBuffer >= pucStartOfMemory ) && + ( pucBuffer < ( pucStartOfMemory + uncMEMORY_SIZE ) ) ) + { + ucReturn = pdFALSE; + } + else + { + ucReturn = pdTRUE; + } + + return ucReturn; +} +/*-----------------------------------------------------------*/ + +uint8_t * pucGetUncachedMemory( uint32_t ulSize ) +{ + uint8_t * pucReturn; + uint32_t ulSkipSize; + + if( pucStartOfMemory == NULL ) + { + vInitialiseUncachedMemory(); + } + + if( ( pucStartOfMemory == NULL ) || ( ulSize > ulMemorySize ) ) + { + pucReturn = NULL; + } + else + { + pucReturn = pucHeadOfMemory; + /* Make sure that the next pointer return will have a good alignment. */ + ulSkipSize = ( ulSize + uncALIGNMENT_SIZE ) & ~( uncALIGNMENT_SIZE - 1uL ); + pucHeadOfMemory += ulSkipSize; + ulMemorySize -= ulSkipSize; + } + + return pucReturn; +} +/*-----------------------------------------------------------*/ + +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 + uncMEMORY_SIZE ) & ( ~( uncMEMORY_SIZE - 1 ) ) ); + + if( ( ( u32 ) pucStartOfMemory ) + uncMEMORY_SIZE > DDR_MEMORY_END ) + { + FreeRTOS_printf( ( "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, uncMEMORY_ATTRIBUTE ); + + /* For experiments in the SDIO driver, make the remaining uncached memory + * public */ + pucHeadOfMemory = pucStartOfMemory; + ulMemorySize = uncMEMORY_SIZE; + memset( pucStartOfMemory, '\0', uncMEMORY_SIZE ); + } +} diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/uncached_memory.h b/FreeRTOS/source/portable/NetworkInterface/Zynq/uncached_memory.h new file mode 100644 index 0000000..fc96a72 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/uncached_memory.h @@ -0,0 +1,22 @@ +/* + * 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 */ diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif.h b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif.h new file mode 100644 index 0000000..029e4a3 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2010-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef __NETIF_XEMACPSIF_H__ + #define __NETIF_XEMACPSIF_H__ + + #ifdef __cplusplus + extern "C" { + #endif + + #include + + #include "xstatus.h" + #include "xparameters.h" + #include "xparameters_ps.h" /* defines XPAR values */ + #include "xil_types.h" + #include "xil_assert.h" + #include "xil_io.h" + #include "xil_exception.h" + #include "xpseudo_asm.h" + #include "xil_cache.h" + #include "xuartps.h" + #include "xscugic.h" + #include "xemacps.h" /* defines XEmacPs API */ + +/*#include "netif/xpqueue.h" */ +/*#include "xlwipconfig.h" */ + + void xemacpsif_setmac( uint32_t index, + uint8_t * addr ); + uint8_t * xemacpsif_getmac( uint32_t index ); +/*int xemacpsif_init(struct netif *netif); */ +/*int xemacpsif_input(struct netif *netif); */ + #ifdef NOTNOW_BHILL + unsigned get_IEEE_phy_speed( XLlTemac * xlltemacp ); + #endif + +/* xaxiemacif_hw.c */ + void xemacps_error_handler( XEmacPs * Temac ); + + struct xBD_TYPE + { + uint32_t address; + uint32_t flags; + }; + +/* + * Missing declaration in 'src/xemacps_hw.h' : + * When set, the GEM DMA will automatically + * discard receive packets from the receiver packet + * buffer memory when no AHB resource is + * available. + * When low, then received packets will remain to be + * stored in the SRAM based packet buffer until + * AHB buffer resource next becomes available. + */ + #define XEMACPS_DMACR_DISC_WHEN_NO_AHB_MASK 0x01000000 + + #define EMAC_IF_RX_EVENT 1 + #define EMAC_IF_TX_EVENT 2 + #define EMAC_IF_ERR_EVENT 4 + #define EMAC_IF_ALL_EVENT 7 + +/* structure within each netif, encapsulating all information required for + * using a particular temac instance + */ + typedef struct + { + XEmacPs emacps; + + /* pointers to memory holding buffer descriptors (used only with SDMA) */ + struct xBD_TYPE * rxSegments; + struct xBD_TYPE * txSegments; + + unsigned char * tx_space; + unsigned uTxUnitSize; + + char * remain_mem; + unsigned remain_siz; + + volatile int rxHead, rxTail; + volatile int txHead, txTail; + + volatile int txBusy; + + volatile uint32_t isr_events; + + unsigned int last_rx_frms_cntr; + } xemacpsif_s; + +/*extern xemacpsif_s xemacpsif; */ + + int is_tx_space_available( xemacpsif_s * emac ); + +/* xaxiemacif_dma.c */ + + struct xNETWORK_BUFFER; + + int emacps_check_rx( xemacpsif_s * xemacpsif ); + void emacps_check_tx( xemacpsif_s * xemacpsif ); + int emacps_check_errors( xemacpsif_s * xemacps ); + void emacps_set_rx_buffers( xemacpsif_s * xemacpsif, + u32 ulCount ); + + extern XStatus emacps_send_message( xemacpsif_s * xemacpsif, + struct xNETWORK_BUFFER * pxBuffer, + int iReleaseAfterSend ); + extern unsigned Phy_Setup( XEmacPs * xemacpsp ); + extern void setup_isr( xemacpsif_s * xemacpsif ); + extern XStatus init_dma( xemacpsif_s * xemacpsif ); + extern void start_emacps( xemacpsif_s * xemacpsif ); + + void EmacEnableIntr( void ); + void EmacDisableIntr( void ); + + XStatus init_axi_dma( xemacpsif_s * xemacpsif ); + void process_sent_bds( xemacpsif_s * xemacpsif ); + + void emacps_send_handler( void * arg ); + void emacps_recv_handler( void * arg ); + void emacps_error_handler( void * arg, + u8 Direction, + u32 ErrorWord ); + void HandleTxErrors( xemacpsif_s * xemacpsif ); + XEmacPs_Config * xemacps_lookup_config( unsigned mac_base ); + + void clean_dma_txdescs( xemacpsif_s * xemacpsif ); + void resetrx_on_no_rxdata( xemacpsif_s * xemacpsif ); + + #ifdef __cplusplus + } + #endif + +#endif /* __NETIF_XAXIEMACIF_H__ */ diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_dma.c b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_dma.c new file mode 100644 index 0000000..7237cb4 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_dma.c @@ -0,0 +1,667 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" + +#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 "uncached_memory.h" + +/* Two defines used to set or clear the EMAC interrupt */ +#define INTC_BASE_ADDR XPAR_SCUGIC_CPU_BASEADDR +#define INTC_DIST_BASE_ADDR XPAR_SCUGIC_DIST_BASEADDR + + + +#if ( ipconfigPACKET_FILLER_SIZE != 2 ) + #error Please define ipconfigPACKET_FILLER_SIZE as the value '2' +#endif +#define TX_OFFSET ipconfigPACKET_FILLER_SIZE + +#define dmaRX_TX_BUFFER_SIZE 1536 + +/* Defined in NetworkInterface.c */ +extern TaskHandle_t xEMACTaskHandle; + +/* + * pxDMA_tx_buffers: these are character arrays, each one is big enough to hold 1 MTU. + * The actual TX buffers are located in uncached RAM. + */ +static unsigned char * pxDMA_tx_buffers[ ipconfigNIC_N_TX_DESC ] = { NULL }; + +/* + * pxDMA_rx_buffers: these are pointers to 'NetworkBufferDescriptor_t'. + * Once a message has been received by the EMAC, the descriptor can be passed + * immediately to the IP-task. + */ +static NetworkBufferDescriptor_t * pxDMA_rx_buffers[ ipconfigNIC_N_RX_DESC ] = { NULL }; + +/* + * The FreeRTOS+TCP port is using a fixed 'topology', which is declared in + * ./portable/NetworkInterface/Zynq/NetworkInterface.c + */ +extern struct xtopology_t xXTopology; + +static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; + +/* + * The FreeRTOS+TCP port does not make use of "src/xemacps_bdring.c". + * In stead 'struct xemacpsif_s' has a "head" and a "tail" index. + * "head" is the next index to be written, used. + * "tail" is the next index to be read, freed. + */ + +int is_tx_space_available( xemacpsif_s * xemacpsif ) +{ + size_t uxCount; + + if( xTXDescriptorSemaphore != NULL ) + { + uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + } + else + { + uxCount = ( UBaseType_t ) 0u; + } + + return uxCount; +} + +void emacps_check_tx( xemacpsif_s * xemacpsif ) +{ + int tail = xemacpsif->txTail; + int head = xemacpsif->txHead; + size_t uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + /* uxCount is the number of TX descriptors that are in use by the DMA. */ + /* When done, "TXBUF_USED" will be set. */ + + while( ( uxCount > 0 ) && ( ( xemacpsif->txSegments[ tail ].flags & XEMACPS_TXBUF_USED_MASK ) != 0 ) ) + { + if( ( tail == head ) && ( uxCount != ipconfigNIC_N_TX_DESC ) ) + { + break; + } + + { + void * pvBuffer = pxDMA_tx_buffers[ tail ]; + NetworkBufferDescriptor_t * pxBuffer; + + if( pvBuffer != NULL ) + { + pxDMA_tx_buffers[ tail ] = NULL; + pxBuffer = pxPacketBuffer_to_NetworkBuffer( pvBuffer ); + + if( pxBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + } + else + { + FreeRTOS_printf( ( "emacps_check_tx: Can not find network buffer\n" ) ); + } + } + } + + /* Clear all but the "used" and "wrap" bits. */ + if( tail < ipconfigNIC_N_TX_DESC - 1 ) + { + xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK; + } + else + { + xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK; + } + + uxCount--; + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + + if( ++tail == ipconfigNIC_N_TX_DESC ) + { + tail = 0; + } + + xemacpsif->txTail = tail; + } +} + +void emacps_send_handler( void * arg ) +{ + xemacpsif_s * xemacpsif; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + xemacpsif = ( xemacpsif_s * ) ( arg ); + + /* This function is called from an ISR. The Xilinx ISR-handler has already + * cleared the TXCOMPL and TXSR_USEDREAD status bits in the XEMACPS_TXSR register. + * But it forgets to do a read-back. Do so now to avoid ever-returning ISR's. */ + ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_TXSR_OFFSET ); + + /* In this port for FreeRTOS+TCP, the EMAC interrupts will only set a bit in + * "isr_events". The task in NetworkInterface will wake-up and do the necessary work. + */ + xemacpsif->isr_events |= EMAC_IF_TX_EVENT; + xemacpsif->txBusy = pdFALSE; + + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} + +static BaseType_t xValidLength( BaseType_t xLength ) +{ + BaseType_t xReturn; + + if( ( xLength >= ( BaseType_t ) sizeof( struct xARP_PACKET ) ) && ( ( ( uint32_t ) xLength ) <= dmaRX_TX_BUFFER_SIZE ) ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} + +XStatus emacps_send_message( xemacpsif_s * xemacpsif, + NetworkBufferDescriptor_t * pxBuffer, + int iReleaseAfterSend ) +{ + int head = xemacpsif->txHead; + int iHasSent = 0; + uint32_t ulBaseAddress = xemacpsif->emacps.Config.BaseAddress; + TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 5000u ); + + /* This driver wants to own all network buffers which are to be transmitted. */ + configASSERT( iReleaseAfterSend != pdFALSE ); + + /* Open a do {} while ( 0 ) loop to be able to call break. */ + do + { + uint32_t ulFlags = 0; + + if( xValidLength( pxBuffer->xDataLength ) != pdTRUE ) + { + break; + } + + if( xTXDescriptorSemaphore == NULL ) + { + break; + } + + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + FreeRTOS_printf( ( "emacps_send_message: Time-out waiting for TX buffer\n" ) ); + break; + } + + /* Pass the pointer (and its ownership) directly to DMA. */ + pxDMA_tx_buffers[ head ] = pxBuffer->pucEthernetBuffer; + + if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheFlushRange( ( unsigned ) pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength ); + } + + /* Buffer has been transferred, do not release it. */ + iReleaseAfterSend = pdFALSE; + + /* Packets will be sent one-by-one, so for each packet + * the TXBUF_LAST bit will be set. */ + ulFlags |= XEMACPS_TXBUF_LAST_MASK; + ulFlags |= ( pxBuffer->xDataLength & XEMACPS_TXBUF_LEN_MASK ); + + if( head == ( ipconfigNIC_N_TX_DESC - 1 ) ) + { + ulFlags |= XEMACPS_TXBUF_WRAP_MASK; + } + + /* Copy the address of the buffer and set the flags. */ + xemacpsif->txSegments[ head ].address = ( uint32_t ) pxDMA_tx_buffers[ head ]; + xemacpsif->txSegments[ head ].flags = ulFlags; + + iHasSent = pdTRUE; + + if( ++head == ipconfigNIC_N_TX_DESC ) + { + head = 0; + } + + /* Update the TX-head index. These variable are declared volatile so they will be + * accessed as little as possible. */ + xemacpsif->txHead = head; + } while( pdFALSE ); + + if( iReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + pxBuffer = NULL; + } + + /* Data Synchronization Barrier */ + dsb(); + + if( iHasSent != pdFALSE ) + { + /* Make STARTTX high */ + uint32_t ulValue = XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET ); + /* Start transmit */ + xemacpsif->txBusy = pdTRUE; + XEmacPs_WriteReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET, ( ulValue | XEMACPS_NWCTRL_STARTTX_MASK ) ); + /* Read back the register to make sure the data is flushed. */ + ( void ) XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET ); + } + + dsb(); + + return 0; +} + +void emacps_recv_handler( void * arg ) +{ + xemacpsif_s * xemacpsif; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + xemacpsif = ( xemacpsif_s * ) ( arg ); + xemacpsif->isr_events |= EMAC_IF_RX_EVENT; + + /* The driver has already cleared the FRAMERX, BUFFNA and error bits + * in the XEMACPS_RXSR register, + * But it forgets to do a read-back. Do so now. */ + ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET ); + + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} + +static void prvPassEthMessages( NetworkBufferDescriptor_t * pxDescriptor ) +{ + IPStackEvent_t xRxEvent; + + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = ( void * ) pxDescriptor; + + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 1000 ) != pdPASS ) + { + /* The buffer could not be sent to the stack so must be released again. + * This is a deferred handler task, not a real interrupt, so it is ok to + * use the task level function here. */ + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + do + { + NetworkBufferDescriptor_t * pxNext = pxDescriptor->pxNextBuffer; + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + pxDescriptor = pxNext; + } while( pxDescriptor != NULL ); + } + #else + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "prvPassEthMessages: Can not queue return packet!\n" ) ); + } +} + +int emacps_check_rx( xemacpsif_s * xemacpsif ) +{ + NetworkBufferDescriptor_t * pxBuffer, * pxNewBuffer; + int rx_bytes; + volatile int msgCount = 0; + int head = xemacpsif->rxHead; + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + NetworkBufferDescriptor_t * pxFirstDescriptor = NULL; + NetworkBufferDescriptor_t * pxLastDescriptor = NULL; + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + + /* There seems to be an issue (SI# 692601), see comments below. */ + resetrx_on_no_rxdata( xemacpsif ); + + /* This FreeRTOS+TCP driver shall be compiled with the option + * "ipconfigUSE_LINKED_RX_MESSAGES" enabled. It allows the driver to send a + * chain of RX messages within one message to the IP-task. */ + for( ; ; ) + { + if( ( ( xemacpsif->rxSegments[ head ].address & XEMACPS_RXBUF_NEW_MASK ) == 0 ) || + ( pxDMA_rx_buffers[ head ] == NULL ) ) + { + break; + } + + pxNewBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 ); + + if( pxNewBuffer == NULL ) + { + /* A packet has been received, but there is no replacement for this Network Buffer. + * The packet will be dropped, and it Network Buffer will stay in place. */ + FreeRTOS_printf( ( "emacps_check_rx: unable to allocate a Network Buffer\n" ) ); + pxNewBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ head ]; + } + else + { + pxBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ head ]; + + /* Just avoiding to use or refer to the same buffer again */ + pxDMA_rx_buffers[ head ] = pxNewBuffer; + + /* + * Adjust the buffer size to the actual number of bytes received. + */ + rx_bytes = xemacpsif->rxSegments[ head ].flags & XEMACPS_RXBUF_LEN_MASK; + + pxBuffer->xDataLength = rx_bytes; + + if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheInvalidateRange( ( ( uint32_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( unsigned ) rx_bytes ); + } + + /* store it in the receive queue, where it'll be processed by a + * different handler. */ + iptraceNETWORK_INTERFACE_RECEIVE(); + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + pxBuffer->pxNextBuffer = NULL; + + if( pxFirstDescriptor == NULL ) + { + /* Becomes the first message */ + pxFirstDescriptor = pxBuffer; + } + else if( pxLastDescriptor != NULL ) + { + /* Add to the tail */ + pxLastDescriptor->pxNextBuffer = pxBuffer; + } + + pxLastDescriptor = pxBuffer; + } + #else /* if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) */ + { + prvPassEthMessages( pxBuffer ); + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + + msgCount++; + } + + { + if( ucIsCachedMemory( pxNewBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheInvalidateRange( ( ( uint32_t ) pxNewBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( uint32_t ) dmaRX_TX_BUFFER_SIZE ); + } + + { + uint32_t addr = ( ( uint32_t ) pxNewBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK; + + if( head == ( ipconfigNIC_N_RX_DESC - 1 ) ) + { + addr |= XEMACPS_RXBUF_WRAP_MASK; + } + + /* Clearing 'XEMACPS_RXBUF_NEW_MASK' 0x00000001 *< Used bit.. */ + xemacpsif->rxSegments[ head ].flags = 0; + xemacpsif->rxSegments[ head ].address = addr; + /* Make sure that the value has reached the peripheral by reading it back. */ + ( void ) xemacpsif->rxSegments[ head ].address; + } + } + + if( ++head == ipconfigNIC_N_RX_DESC ) + { + head = 0; + } + + xemacpsif->rxHead = head; + } + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + if( pxFirstDescriptor != NULL ) + { + prvPassEthMessages( pxFirstDescriptor ); + } + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + + return msgCount; +} + +void clean_dma_txdescs( xemacpsif_s * xemacpsif ) +{ + int index; + unsigned char * ucTxBuffer; + + /* Clear all TX descriptors and assign uncached memory to each descriptor. + * "tx_space" points to the first available TX buffer. */ + ucTxBuffer = xemacpsif->tx_space; + + for( index = 0; index < ipconfigNIC_N_TX_DESC; index++ ) + { + xemacpsif->txSegments[ index ].address = ( uint32_t ) ucTxBuffer; + xemacpsif->txSegments[ index ].flags = XEMACPS_TXBUF_USED_MASK; + pxDMA_tx_buffers[ index ] = ( unsigned char * ) NULL; + ucTxBuffer += xemacpsif->uTxUnitSize; + } + + xemacpsif->txSegments[ ipconfigNIC_N_TX_DESC - 1 ].flags = + XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK; +} + +XStatus init_dma( xemacpsif_s * xemacpsif ) +{ + NetworkBufferDescriptor_t * pxBuffer; + + int iIndex; + UBaseType_t xRxSize; + UBaseType_t xTxSize; + struct xtopology_t * xtopologyp = &xXTopology; + + xRxSize = ipconfigNIC_N_RX_DESC * sizeof( xemacpsif->rxSegments[ 0 ] ); + + xTxSize = ipconfigNIC_N_TX_DESC * sizeof( xemacpsif->txSegments[ 0 ] ); + + xemacpsif->uTxUnitSize = dmaRX_TX_BUFFER_SIZE; + + /* + * We allocate 65536 bytes for RX BDs which can accommodate a + * maximum of 8192 BDs which is much more than any application + * will ever need. + */ + xemacpsif->rxSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xRxSize ) ); + xemacpsif->txSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xTxSize ) ); + xemacpsif->tx_space = ( unsigned char * ) ( pucGetUncachedMemory( ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize ) ); + + /* These variables will be used in XEmacPs_Start (see src/xemacps.c). */ + xemacpsif->emacps.RxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->rxSegments; + xemacpsif->emacps.TxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->txSegments; + + if( xTXDescriptorSemaphore == NULL ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ipconfigNIC_N_TX_DESC, ( UBaseType_t ) ipconfigNIC_N_TX_DESC ); + configASSERT( xTXDescriptorSemaphore ); + } + + /* + * Allocate RX descriptors, 1 RxBD at a time. + */ + for( iIndex = 0; iIndex < ipconfigNIC_N_RX_DESC; iIndex++ ) + { + pxBuffer = pxDMA_rx_buffers[ iIndex ]; + + if( pxBuffer == NULL ) + { + pxBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 ); + + if( pxBuffer == NULL ) + { + FreeRTOS_printf( ( "Unable to allocate a network buffer in recv_handler\n" ) ); + return -1; + } + } + + xemacpsif->rxSegments[ iIndex ].flags = 0; + xemacpsif->rxSegments[ iIndex ].address = ( ( uint32_t ) pxBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK; + + pxDMA_rx_buffers[ iIndex ] = pxBuffer; + + /* Make sure this memory is not in cache for now. */ + if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheInvalidateRange( ( ( uint32_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, + ( unsigned ) dmaRX_TX_BUFFER_SIZE ); + } + } + + xemacpsif->rxSegments[ ipconfigNIC_N_RX_DESC - 1 ].address |= XEMACPS_RXBUF_WRAP_MASK; + + memset( xemacpsif->tx_space, '\0', ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize ); + + clean_dma_txdescs( xemacpsif ); + + { + uint32_t value; + value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET ); + + /* 1xxxx: Attempt to use INCR16 AHB bursts */ + value = ( value & ~( XEMACPS_DMACR_BLENGTH_MASK ) ) | XEMACPS_DMACR_INCR16_AHB_BURST; + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + value |= XEMACPS_DMACR_TCPCKSUM_MASK; + #else + #warning Are you sure the EMAC should not calculate outgoing checksums? + value &= ~XEMACPS_DMACR_TCPCKSUM_MASK; + #endif + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET, value ); + } + { + uint32_t value; + value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET ); + + /* Network buffers are 32-bit aligned + 2 bytes (because ipconfigPACKET_FILLER_SIZE = 2 ). + * Now tell the EMAC that received messages should be stored at "address + 2". */ + value = ( value & ~XEMACPS_NWCFG_RXOFFS_MASK ) | 0x8000; + + #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM != 0 ) + value |= XEMACPS_NWCFG_RXCHKSUMEN_MASK; + #else + #warning Are you sure the EMAC should not calculate incoming checksums? + value &= ~XEMACPS_NWCFG_RXCHKSUMEN_MASK; + #endif + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET, value ); + } + + /* + * Connect the device driver handler that will be called when an + * interrupt for the device occurs, the handler defined above performs + * the specific interrupt processing for the device. + */ + XScuGic_RegisterHandler( INTC_BASE_ADDR, xtopologyp->scugic_emac_intr, + ( Xil_ExceptionHandler ) XEmacPs_IntrHandler, + ( void * ) &xemacpsif->emacps ); + + /* + * Enable the interrupt for emacps. + */ + EmacEnableIntr(); + + return 0; +} + +/* + * resetrx_on_no_rxdata(): + * + * It is called at regular intervals through the API xemacpsif_resetrx_on_no_rxdata + * called by the user. + * The EmacPs has a HW bug (SI# 692601) on the Rx path for heavy Rx traffic. + * Under heavy Rx traffic because of the HW bug there are times when the Rx path + * becomes unresponsive. The workaround for it is to check for the Rx path for + * traffic (by reading the stats registers regularly). If the stats register + * does not increment for sometime (proving no Rx traffic), the function resets + * the Rx data path. + * + */ + +void resetrx_on_no_rxdata( xemacpsif_s * xemacpsif ) +{ + unsigned long regctrl; + unsigned long tempcntr; + + tempcntr = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXCNT_OFFSET ); + + if( ( tempcntr == 0 ) && ( xemacpsif->last_rx_frms_cntr == 0 ) ) + { + regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET ); + regctrl &= ( ~XEMACPS_NWCTRL_RXEN_MASK ); + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, regctrl ); + regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET ); + regctrl |= ( XEMACPS_NWCTRL_RXEN_MASK ); + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, regctrl ); + } + + xemacpsif->last_rx_frms_cntr = tempcntr; +} + +void EmacDisableIntr( void ) +{ + XScuGic_DisableIntr( INTC_DIST_BASE_ADDR, xXTopology.scugic_emac_intr ); +} + +void EmacEnableIntr( void ) +{ + XScuGic_EnableIntr( INTC_DIST_BASE_ADDR, xXTopology.scugic_emac_intr ); +} diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_hw.c b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_hw.c new file mode 100644 index 0000000..49e7043 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_hw.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2010-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "Zynq/x_emacpsif.h" + +extern TaskHandle_t xEMACTaskHandle; + +/*** IMPORTANT: Define PEEP in xemacpsif.h and sys_arch_raw.c + *** to run it on a PEEP board + ***/ + +void setup_isr( xemacpsif_s * xemacpsif ) +{ + /* + * Setup callbacks + */ + XEmacPs_SetHandler( &xemacpsif->emacps, XEMACPS_HANDLER_DMASEND, + ( void * ) emacps_send_handler, + ( void * ) xemacpsif ); + + XEmacPs_SetHandler( &xemacpsif->emacps, XEMACPS_HANDLER_DMARECV, + ( void * ) emacps_recv_handler, + ( void * ) xemacpsif ); + + XEmacPs_SetHandler( &xemacpsif->emacps, XEMACPS_HANDLER_ERROR, + ( void * ) emacps_error_handler, + ( void * ) xemacpsif ); +} + +void start_emacps( xemacpsif_s * xemacps ) +{ + /* start the temac */ + XEmacPs_Start( &xemacps->emacps ); +} + +extern struct xtopology_t xXTopology; + +volatile int error_msg_count = 0; +volatile const char * last_err_msg = ""; + +struct xERROR_MSG +{ + void * arg; + u8 Direction; + u32 ErrorWord; +}; + +static struct xERROR_MSG xErrorList[ 8 ]; +static BaseType_t xErrorHead, xErrorTail; + +void emacps_error_handler( void * arg, + u8 Direction, + u32 ErrorWord ) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xemacpsif_s * xemacpsif; + BaseType_t xNextHead = xErrorHead; + + xemacpsif = ( xemacpsif_s * ) ( arg ); + + if( ( Direction != XEMACPS_SEND ) || ( ErrorWord != XEMACPS_TXSR_USEDREAD_MASK ) ) + { + if( ++xNextHead == ( sizeof( xErrorList ) / sizeof( xErrorList[ 0 ] ) ) ) + { + xNextHead = 0; + } + + if( xNextHead != xErrorTail ) + { + xErrorList[ xErrorHead ].arg = arg; + xErrorList[ xErrorHead ].Direction = Direction; + xErrorList[ xErrorHead ].ErrorWord = ErrorWord; + + xErrorHead = xNextHead; + + xemacpsif = ( xemacpsif_s * ) ( arg ); + xemacpsif->isr_events |= EMAC_IF_ERR_EVENT; + } + + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + } + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} + +static void emacps_handle_error( void * arg, + u8 Direction, + u32 ErrorWord ); + +int emacps_check_errors( xemacpsif_s * xemacps ) +{ + int xResult; + + ( void ) xemacps; + + if( xErrorHead == xErrorTail ) + { + xResult = 0; + } + else + { + xResult = 1; + emacps_handle_error( + xErrorList[ xErrorTail ].arg, + xErrorList[ xErrorTail ].Direction, + xErrorList[ xErrorTail ].ErrorWord ); + } + + return xResult; +} + +static void emacps_handle_error( void * arg, + u8 Direction, + u32 ErrorWord ) +{ + xemacpsif_s * xemacpsif; + struct xtopology_t * xtopologyp; + XEmacPs * xemacps; + + xemacpsif = ( xemacpsif_s * ) ( arg ); + + xtopologyp = &xXTopology; + + xemacps = &xemacpsif->emacps; + + /* Do not appear to be used. */ + ( void ) xemacps; + ( void ) xtopologyp; + + last_err_msg = NULL; + + if( ErrorWord != 0 ) + { + switch( Direction ) + { + case XEMACPS_RECV: + + if( ( ErrorWord & XEMACPS_RXSR_HRESPNOK_MASK ) != 0 ) + { + last_err_msg = "Receive DMA error"; + xNetworkInterfaceInitialise(); + } + + if( ( ErrorWord & XEMACPS_RXSR_RXOVR_MASK ) != 0 ) + { + last_err_msg = "Receive over run"; + emacps_recv_handler( arg ); + } + + if( ( ErrorWord & XEMACPS_RXSR_BUFFNA_MASK ) != 0 ) + { + last_err_msg = "Receive buffer not available"; + emacps_recv_handler( arg ); + } + + break; + + case XEMACPS_SEND: + + if( ( ErrorWord & XEMACPS_TXSR_HRESPNOK_MASK ) != 0 ) + { + last_err_msg = "Transmit DMA error"; + xNetworkInterfaceInitialise(); + } + + if( ( ErrorWord & XEMACPS_TXSR_URUN_MASK ) != 0 ) + { + last_err_msg = "Transmit under run"; + HandleTxErrors( xemacpsif ); + } + + if( ( ErrorWord & XEMACPS_TXSR_BUFEXH_MASK ) != 0 ) + { + last_err_msg = "Transmit buffer exhausted"; + HandleTxErrors( xemacpsif ); + } + + if( ( ErrorWord & XEMACPS_TXSR_RXOVR_MASK ) != 0 ) + { + last_err_msg = "Transmit retry excessed limits"; + HandleTxErrors( xemacpsif ); + } + + if( ( ErrorWord & XEMACPS_TXSR_FRAMERX_MASK ) != 0 ) + { + last_err_msg = "Transmit collision"; + emacps_check_tx( xemacpsif ); + } + + break; + } + } + + /* Break on this statement and inspect error_msg if you like */ + if( last_err_msg != NULL ) + { + error_msg_count++; + FreeRTOS_printf( ( "emacps_handle_error: %s\n", last_err_msg ) ); + } +} + +void HandleTxErrors( xemacpsif_s * xemacpsif ) +{ + u32 netctrlreg; + + /*taskENTER_CRITICAL() */ + { + netctrlreg = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET ); + netctrlreg = netctrlreg & ( ~XEMACPS_NWCTRL_TXEN_MASK ); + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, netctrlreg ); + + clean_dma_txdescs( xemacpsif ); + netctrlreg = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET ); + netctrlreg = netctrlreg | ( XEMACPS_NWCTRL_TXEN_MASK ); + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, netctrlreg ); + } + /*taskEXIT_CRITICAL( ); */ +} diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_hw.h b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_hw.h new file mode 100644 index 0000000..9769fc1 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_hw.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef __XEMACPSIF_HW_H_ + #define __XEMACPSIF_HW_H_ + + #include "Zynq/x_emacpsif.h" +/*#include "lwip/netif.h" */ + + #ifdef __cplusplus + extern "C" { + #endif + + XEmacPs_Config * lookup_config( unsigned mac_base ); + +/*void init_emacps(xemacpsif_s *xemacpsif, struct netif *netif); */ + + int emacps_check_errors( xemacpsif_s * xemacps ); + + #ifdef __cplusplus + } + #endif + +#endif /* ifndef __XEMACPSIF_HW_H_ */ diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_physpeed.c b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_physpeed.c new file mode 100644 index 0000000..a023e6c --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_emacpsif_physpeed.c @@ -0,0 +1,665 @@ +/* + * Copyright (c) 2007-2008, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Advanced Micro Devices, Inc. nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/* + * Some portions copyright (c) 2010-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" + +#include "Zynq/x_emacpsif.h" +#include "xparameters_ps.h" +#include "xparameters.h" + + +int phy_detected = 0; + +/*** IMPORTANT: Define PEEP in xemacpsif.h and sys_arch_raw.c + *** to run it on a PEEP board + ***/ + +/* Advertisement control register. */ +#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_100_AND_10 \ + ( ADVERTISE_10FULL | ADVERTISE_100FULL | \ + ADVERTISE_10HALF | ADVERTISE_100HALF ) +#define ADVERTISE_100 ( ADVERTISE_100FULL | ADVERTISE_100HALF ) +#define ADVERTISE_10 ( ADVERTISE_10FULL | ADVERTISE_10HALF ) + +#define ADVERTISE_1000 0x0300 + + +/*#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 IEEE_CONTROL_REG_OFFSET 0 +#define IEEE_STATUS_REG_OFFSET 1 +#define IEEE_PHYSID1_OFFSET 2 +#define IEEE_PHYSID2_OFFSET 3 +#define IEEE_AUTONEGO_ADVERTISE_REG 4 +#define IEEE_PARTNER_ABILITIES_1_REG_OFFSET 5 +#define IEEE_1000_ADVERTISE_REG_OFFSET 9 +#define IEEE_PARTNER_ABILITIES_3_REG_OFFSET 10 +#define IEEE_COPPER_SPECIFIC_CONTROL_REG 16 +#define IEEE_SPECIFIC_STATUS_REG 17 +#define IEEE_COPPER_SPECIFIC_STATUS_REG_2 19 +#define IEEE_CONTROL_REG_MAC 21 +#define IEEE_PAGE_ADDRESS_REGISTER 22 + + +#define IEEE_CTRL_1GBPS_LINKSPEED_MASK 0x2040 +#define IEEE_CTRL_LINKSPEED_MASK 0x0040 +#define IEEE_CTRL_LINKSPEED_1000M 0x0040 +#define IEEE_CTRL_LINKSPEED_100M 0x2000 +#define IEEE_CTRL_LINKSPEED_10M 0x0000 +#define IEEE_CTRL_RESET_MASK 0x8000 +#define IEEE_CTRL_AUTONEGOTIATE_ENABLE 0x1000 +#if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 + #define IEEE_CTRL_RESET 0x9140 + #define IEEE_CTRL_ISOLATE_DISABLE 0xFBFF +#endif +#define IEEE_STAT_AUTONEGOTIATE_CAPABLE 0x0008 +#define IEEE_STAT_AUTONEGOTIATE_COMPLETE 0x0020 +#define IEEE_STAT_AUTONEGOTIATE_RESTART 0x0200 +#define IEEE_STAT_1GBPS_EXTENSIONS 0x0100 +#define IEEE_AN1_ABILITY_MASK 0x1FE0 +#define IEEE_AN3_ABILITY_MASK_1GBPS 0x0C00 +#define IEEE_AN1_ABILITY_MASK_100MBPS 0x0380 +#define IEEE_AN1_ABILITY_MASK_10MBPS 0x0060 +#define IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK 0x0030 + +#define IEEE_ASYMMETRIC_PAUSE_MASK 0x0800 +#define IEEE_PAUSE_MASK 0x0400 +#define IEEE_AUTONEG_ERROR_MASK 0x8000 + +#define XEMACPS_GMII2RGMII_SPEED1000_FD 0x140 +#define XEMACPS_GMII2RGMII_SPEED100_FD 0x2100 +#define XEMACPS_GMII2RGMII_SPEED10_FD 0x100 +#define XEMACPS_GMII2RGMII_REG_NUM 0x10 + +/* Frequency setting */ +#define SLCR_LOCK_ADDR ( XPS_SYS_CTRL_BASEADDR + 0x4 ) +#define SLCR_UNLOCK_ADDR ( XPS_SYS_CTRL_BASEADDR + 0x8 ) +#define SLCR_GEM0_CLK_CTRL_ADDR ( XPS_SYS_CTRL_BASEADDR + 0x140 ) +#define SLCR_GEM1_CLK_CTRL_ADDR ( XPS_SYS_CTRL_BASEADDR + 0x144 ) +#ifdef PEEP + #define SLCR_GEM_10M_CLK_CTRL_VALUE 0x00103031 + #define SLCR_GEM_100M_CLK_CTRL_VALUE 0x00103001 + #define SLCR_GEM_1G_CLK_CTRL_VALUE 0x00103011 +#endif +#define SLCR_LOCK_KEY_VALUE 0x767B +#define SLCR_UNLOCK_KEY_VALUE 0xDF0D +#define SLCR_ADDR_GEM_RST_CTRL ( XPS_SYS_CTRL_BASEADDR + 0x214 ) +#define EMACPS_SLCR_DIV_MASK 0xFC0FC0FF + +#define EMAC0_BASE_ADDRESS 0xE000B000 +#define EMAC1_BASE_ADDRESS 0xE000C000 + +#define PHY_ADDRESS_COUNT 32 + +#define MINIMUM_SLEEP_TIME 2 + + +static int detect_phy( XEmacPs * xemacpsp ) +{ + u16 id_lower, id_upper; + u32 phy_addr, id; + + for( phy_addr = 0; phy_addr < PHY_ADDRESS_COUNT; phy_addr++ ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_PHYSID1_OFFSET, &id_lower ); + + if( ( id_lower != ( u16 ) 0xFFFFu ) && ( id_lower != ( u16 ) 0x0u ) ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_PHYSID2_OFFSET, &id_upper ); + id = ( ( ( uint32_t ) id_upper ) << 16 ) | ( id_lower & 0xFFF0 ); + FreeRTOS_printf( ( "XEmacPs detect_phy: %04lX at address %d.\n", id, phy_addr ) ); + phy_detected = phy_addr; + return phy_addr; + } + } + + FreeRTOS_printf( ( "XEmacPs detect_phy: No PHY detected. Assuming a PHY at address 0\n" ) ); + + /* default to zero */ + return 0; +} + +#ifdef PEEP + unsigned get_IEEE_phy_speed( XEmacPs * xemacpsp ) + { + u16 control; + u16 status; + u16 partner_capabilities; + u16 partner_capabilities_1000; + u16 phylinkspeed; + u32 phy_addr = detect_phy( xemacpsp ); + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + ADVERTISE_1000 ); + /* Advertise PHY speed of 100 and 10 Mbps */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_100_AND_10 ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, + &control ); + control |= ( IEEE_CTRL_AUTONEGOTIATE_ENABLE | + IEEE_STAT_AUTONEGOTIATE_RESTART ); + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control ); + + /* Read PHY control and status registers is successful. */ + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status ); + + if( ( control & IEEE_CTRL_AUTONEGOTIATE_ENABLE ) && ( status & + IEEE_STAT_AUTONEGOTIATE_CAPABLE ) ) + { + while( !( status & IEEE_STAT_AUTONEGOTIATE_COMPLETE ) ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, + &status ); + } + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_PARTNER_ABILITIES_1_REG_OFFSET, + &partner_capabilities ); + + if( status & IEEE_STAT_1GBPS_EXTENSIONS ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_PARTNER_ABILITIES_3_REG_OFFSET, + &partner_capabilities_1000 ); + + if( partner_capabilities_1000 & IEEE_AN3_ABILITY_MASK_1GBPS ) + { + return 1000; + } + } + + if( partner_capabilities & IEEE_AN1_ABILITY_MASK_100MBPS ) + { + return 100; + } + + if( partner_capabilities & IEEE_AN1_ABILITY_MASK_10MBPS ) + { + return 10; + } + + FreeRTOS_printf( ( "%s: unknown PHY link speed, setting TEMAC speed to be 10 Mbps\n", + __FUNCTION__ ) ); + return 10; + } + else + { + /* Update TEMAC speed accordingly */ + if( status & IEEE_STAT_1GBPS_EXTENSIONS ) + { + /* Get commanded link speed */ + phylinkspeed = control & IEEE_CTRL_1GBPS_LINKSPEED_MASK; + + switch( phylinkspeed ) + { + case ( IEEE_CTRL_LINKSPEED_1000M ): + return 1000; + + case ( IEEE_CTRL_LINKSPEED_100M ): + return 100; + + case ( IEEE_CTRL_LINKSPEED_10M ): + return 10; + + default: + FreeRTOS_printf( ( "%s: unknown PHY link speed (%d), setting TEMAC speed to be 10 Mbps\n", + __FUNCTION__, phylinkspeed ) ); + return 10; + } + } + else + { + return ( control & IEEE_CTRL_LINKSPEED_MASK ) ? 100 : 10; + } + } + } + +#else /* Zynq */ + unsigned get_IEEE_phy_speed( XEmacPs * xemacpsp ) + { + u16 temp; + u16 control; + u16 status; + u16 partner_capabilities; + + #if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 + u32 phy_addr = XPAR_PCSPMA_SGMII_PHYADDR; + #else + u32 phy_addr = detect_phy( xemacpsp ); + #endif + FreeRTOS_printf( ( "Start PHY autonegotiation \n" ) ); + + #if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 + #else + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2 ); + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control ); + control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control ); + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0 ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control ); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + control |= ADVERTISE_100; + control |= ADVERTISE_10; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + &control ); + control |= ADVERTISE_1000; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + control ); + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0 ); + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, + &control ); + control |= ( 7 << 12 ); /* max number of gigabit attempts */ + control |= ( 1 << 11 ); /* enable downshift */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, + control ); + #endif /* if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 */ + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + #if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 + control &= IEEE_CTRL_ISOLATE_DISABLE; + #endif + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control ); + + + #if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 + #else + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + control |= IEEE_CTRL_RESET_MASK; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control ); + + while( 1 ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + + if( control & IEEE_CTRL_RESET_MASK ) + { + continue; + } + else + { + break; + } + } + #endif /* if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 */ + FreeRTOS_printf( ( "Waiting for PHY to complete autonegotiation.\n" ) ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status ); + + while( !( status & IEEE_STAT_AUTONEGOTIATE_COMPLETE ) ) + { + vTaskDelay( MINIMUM_SLEEP_TIME ); + #if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 + #else + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_STATUS_REG_2, + &temp ); + + if( temp & IEEE_AUTONEG_ERROR_MASK ) + { + FreeRTOS_printf( ( "Auto negotiation error \n" ) ); + } + #endif + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, + &status ); + } + + FreeRTOS_printf( ( "autonegotiation complete \n" ) ); + + #if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 + #else + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_SPECIFIC_STATUS_REG, &partner_capabilities ); + #endif + + #if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 + FreeRTOS_printf( ( "Waiting for Link to be up; Polling for SGMII core Reg \n" ) ); + XEmacPs_PhyRead( xemacpsp, phy_addr, 5, &temp ); + + while( !( temp & 0x8000 ) ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, 5, &temp ); + } + + if( ( temp & 0x0C00 ) == 0x0800 ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, 0, &temp ); + return 1000; + } + else if( ( temp & 0x0C00 ) == 0x0400 ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, 0, &temp ); + return 100; + } + else if( ( temp & 0x0C00 ) == 0x0000 ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, 0, &temp ); + return 10; + } + else + { + FreeRTOS_printf( ( "get_IEEE_phy_speed(): Invalid speed bit value, Deafulting to Speed = 10 Mbps\n" ) ); + XEmacPs_PhyRead( xemacpsp, phy_addr, 0, &temp ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, 0, 0x0100 ); + return 10; + } + #else /* if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 */ + if( ( ( partner_capabilities >> 14 ) & 3 ) == 2 ) /* 1000Mbps */ + { + return 1000; + } + else if( ( ( partner_capabilities >> 14 ) & 3 ) == 1 ) /* 100Mbps */ + { + return 100; + } + else /* 10Mbps */ + { + return 10; + } + #endif /* if XPAR_GIGE_PCS_PMA_CORE_PRESENT == 1 */ + } +#endif /* ifdef PEEP */ + +unsigned configure_IEEE_phy_speed( XEmacPs * xemacpsp, + unsigned speed ) +{ + u16 control; + u32 phy_addr = detect_phy( xemacpsp ); + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2 ); + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control ); + control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control ); + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0 ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control ); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + control &= ~IEEE_CTRL_LINKSPEED_1000M; + control &= ~IEEE_CTRL_LINKSPEED_100M; + control &= ~IEEE_CTRL_LINKSPEED_10M; + + if( speed == 1000 ) + { + control |= IEEE_CTRL_LINKSPEED_1000M; + } + + else if( speed == 100 ) + { + control |= IEEE_CTRL_LINKSPEED_100M; + /* Dont advertise PHY speed of 1000 Mbps */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, 0 ); + /* Dont advertise PHY speed of 10 Mbps */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_100 ); + } + + else if( speed == 10 ) + { + control |= IEEE_CTRL_LINKSPEED_10M; + /* Dont advertise PHY speed of 1000 Mbps */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + 0 ); + /* Dont advertise PHY speed of 100 Mbps */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_10 ); + } + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, + control | IEEE_CTRL_RESET_MASK ); + { + volatile int wait; + + for( wait = 0; wait < 100000; wait++ ) + { + } + } + return 0; +} + +static void SetUpSLCRDivisors( int mac_baseaddr, + int speed ) +{ + volatile u32 slcrBaseAddress; + + #ifndef PEEP + u32 SlcrDiv0; + u32 SlcrDiv1 = 0; + u32 SlcrTxClkCntrl; + #endif + + *( volatile unsigned int * ) ( SLCR_UNLOCK_ADDR ) = SLCR_UNLOCK_KEY_VALUE; + + if( ( unsigned long ) mac_baseaddr == EMAC0_BASE_ADDRESS ) + { + slcrBaseAddress = SLCR_GEM0_CLK_CTRL_ADDR; + } + else + { + slcrBaseAddress = SLCR_GEM1_CLK_CTRL_ADDR; + } + + #ifdef PEEP + if( speed == 1000 ) + { + *( volatile unsigned int * ) ( slcrBaseAddress ) = + SLCR_GEM_1G_CLK_CTRL_VALUE; + } + else if( speed == 100 ) + { + *( volatile unsigned int * ) ( slcrBaseAddress ) = + SLCR_GEM_100M_CLK_CTRL_VALUE; + } + else + { + *( volatile unsigned int * ) ( slcrBaseAddress ) = + SLCR_GEM_10M_CLK_CTRL_VALUE; + } + #else /* ifdef PEEP */ + if( speed == 1000 ) + { + if( ( unsigned long ) mac_baseaddr == EMAC0_BASE_ADDRESS ) + { + #ifdef XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV1; + #endif + } + else + { + #ifdef XPAR_PS7_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_1_ENET_SLCR_1000MBPS_DIV1; + #endif + } + } + else if( speed == 100 ) + { + if( ( unsigned long ) mac_baseaddr == EMAC0_BASE_ADDRESS ) + { + #ifdef XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV1; + #endif + } + else + { + #ifdef XPAR_PS7_ETHERNET_1_ENET_SLCR_100MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_1_ENET_SLCR_100MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_1_ENET_SLCR_100MBPS_DIV1; + #endif + } + } + else + { + if( ( unsigned long ) mac_baseaddr == EMAC0_BASE_ADDRESS ) + { + #ifdef XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV1; + #endif + } + else + { + #ifdef XPAR_PS7_ETHERNET_1_ENET_SLCR_10MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_1_ENET_SLCR_10MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_1_ENET_SLCR_10MBPS_DIV1; + #endif + } + } + + SlcrTxClkCntrl = *( volatile unsigned int * ) ( slcrBaseAddress ); + SlcrTxClkCntrl &= EMACPS_SLCR_DIV_MASK; + SlcrTxClkCntrl |= ( SlcrDiv1 << 20 ); + SlcrTxClkCntrl |= ( SlcrDiv0 << 8 ); + *( volatile unsigned int * ) ( slcrBaseAddress ) = SlcrTxClkCntrl; + #endif /* ifdef PEEP */ + *( volatile unsigned int * ) ( SLCR_LOCK_ADDR ) = SLCR_LOCK_KEY_VALUE; +} + + +unsigned link_speed; +unsigned Phy_Setup( XEmacPs * xemacpsp ) +{ + unsigned long conv_present = 0; + unsigned long convspeeddupsetting = 0; + unsigned long convphyaddr = 0; + + #ifdef XPAR_GMII2RGMIICON_0N_ETH0_ADDR + convphyaddr = XPAR_GMII2RGMIICON_0N_ETH0_ADDR; + conv_present = 1; + #else + #ifdef XPAR_GMII2RGMIICON_0N_ETH1_ADDR + convphyaddr = XPAR_GMII2RGMIICON_0N_ETH1_ADDR; + conv_present = 1; + #endif + #endif + + #ifdef ipconfigNIC_LINKSPEED_AUTODETECT + link_speed = get_IEEE_phy_speed( xemacpsp ); + + if( link_speed == 1000 ) + { + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 1000 ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED1000_FD; + } + else if( link_speed == 100 ) + { + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 100 ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED100_FD; + } + else + { + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 10 ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED10_FD; + } + #elif defined( ipconfigNIC_LINKSPEED1000 ) + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 1000 ); + link_speed = 1000; + configure_IEEE_phy_speed( xemacpsp, link_speed ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED1000_FD; + vTaskDelay( MINIMUM_SLEEP_TIME ); + #elif defined( ipconfigNIC_LINKSPEED100 ) + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 100 ); + link_speed = 100; + configure_IEEE_phy_speed( xemacpsp, link_speed ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED100_FD; + vTaskDelay( MINIMUM_SLEEP_TIME ); + #elif defined( ipconfigNIC_LINKSPEED10 ) + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 10 ); + link_speed = 10; + configure_IEEE_phy_speed( xemacpsp, link_speed ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED10_FD; + vTaskDelay( MINIMUM_SLEEP_TIME ); + #endif /* ifdef ipconfigNIC_LINKSPEED_AUTODETECT */ + + if( conv_present ) + { + XEmacPs_PhyWrite( xemacpsp, convphyaddr, + XEMACPS_GMII2RGMII_REG_NUM, convspeeddupsetting ); + } + + FreeRTOS_printf( ( "link speed: %d\n", link_speed ) ); + return link_speed; +} diff --git a/FreeRTOS/source/portable/NetworkInterface/Zynq/x_topology.h b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_topology.h new file mode 100644 index 0000000..d6fe110 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/Zynq/x_topology.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2007-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef __XTOPOLOGY_H_ + #define __XTOPOLOGY_H_ + + #ifdef __cplusplus + extern "C" { + #endif + + enum xemac_types + { + xemac_type_unknown = -1, xemac_type_xps_emaclite, xemac_type_xps_ll_temac, xemac_type_axi_ethernet, xemac_type_emacps + }; + + struct xtopology_t + { + unsigned emac_baseaddr; + enum xemac_types emac_type; + unsigned intc_baseaddr; + unsigned intc_emac_intr; /* valid only for xemac_type_xps_emaclite */ + unsigned scugic_baseaddr; /* valid only for Zynq */ + unsigned scugic_emac_intr; /* valid only for GEM */ + }; + + extern int x_topology_n_emacs; + extern struct xtopology_t x_topology[]; + + int x_topology_find_index( unsigned base ); + + #ifdef __cplusplus + } + #endif + +#endif /* ifndef __XTOPOLOGY_H_ */ diff --git a/FreeRTOS/source/portable/NetworkInterface/board_family/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/board_family/NetworkInterface.c new file mode 100644 index 0000000..fe44dcc --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/board_family/NetworkInterface.c @@ -0,0 +1,76 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/***************************************************************************** +* Note: This file is Not! to be used as is. The purpose of this file is to provide +* a template for writing a network interface. Each network interface will have to provide +* concrete implementations of the functions in this file. +* +* See the following URL for an explanation of this file and its functions: +* https://freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Porting.html +* +*****************************************************************************/ + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "list.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" + +/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet + * driver will filter incoming packets and only pass the stack those packets it + * considers need processing. */ +#if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + /* FIX ME. */ + return pdFALSE; +} + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t xReleaseAfterSend ) +{ + /* FIX ME. */ + return pdFALSE; +} + +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + /* FIX ME. */ +} + +BaseType_t xGetPhyLinkStatus( void ) +{ + /* FIX ME. */ + return pdFALSE; +} diff --git a/FreeRTOS/source/portable/NetworkInterface/board_family/ReadMe.txt b/FreeRTOS/source/portable/NetworkInterface/board_family/ReadMe.txt new file mode 100644 index 0000000..782e429 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/board_family/ReadMe.txt @@ -0,0 +1,5 @@ +Update NetworkInterface.c and include other files needed by FreeRTOS+TCP here. + +Note: The NetworkInterface.c file in this directory is Not! to be used as is. The purpose of the file is to provide a template for writing a network interface. +Each network interface will have to provide concrete implementations of the functions in NetworkInterface.c. +See the following URL for an explanation of the file and its functions: https://freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Porting.html \ No newline at end of file diff --git a/FreeRTOS/source/portable/NetworkInterface/esp32/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/esp32/NetworkInterface.c new file mode 100644 index 0000000..cd79234 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/esp32/NetworkInterface.c @@ -0,0 +1,175 @@ +/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_DNS.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_wifi_internal.h" +#include "tcpip_adapter.h" + +enum if_state_t +{ + INTERFACE_DOWN = 0, + INTERFACE_UP, +}; + +static const char * TAG = "NetInterface"; +volatile static uint32_t xInterfaceState = INTERFACE_DOWN; + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + static BaseType_t xMACAdrInitialized = pdFALSE; + uint8_t ucMACAddress[ ipMAC_ADDRESS_LENGTH_BYTES ]; + + if( xInterfaceState == INTERFACE_UP ) + { + if( xMACAdrInitialized == pdFALSE ) + { + esp_wifi_get_mac( ESP_IF_WIFI_STA, ucMACAddress ); + FreeRTOS_UpdateMACAddress( ucMACAddress ); + xMACAdrInitialized = pdTRUE; + } + + return pdTRUE; + } + + return pdFALSE; +} + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t xReleaseAfterSend ) +{ + if( ( pxNetworkBuffer == NULL ) || ( pxNetworkBuffer->pucEthernetBuffer == NULL ) || ( pxNetworkBuffer->xDataLength == 0 ) ) + { + ESP_LOGE( TAG, "Invalid params" ); + return pdFALSE; + } + + esp_err_t ret; + + if( xInterfaceState == INTERFACE_DOWN ) + { + ESP_LOGD( TAG, "Interface down" ); + ret = ESP_FAIL; + } + else + { + ret = esp_wifi_internal_tx( ESP_IF_WIFI_STA, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); + + if( ret != ESP_OK ) + { + ESP_LOGE( TAG, "Failed to tx buffer %p, len %d, err %d", pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength, ret ); + } + } + + #if ( ipconfigHAS_PRINTF != 0 ) + { + /* Call a function that monitors resources: the amount of free network + * buffers and the amount of free space on the heap. See FreeRTOS_IP.c + * for more detailed comments. */ + vPrintResourceStats(); + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + + if( xReleaseAfterSend == pdTRUE ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + return ret == ESP_OK ? pdTRUE : pdFALSE; +} + +void vNetworkNotifyIFDown() +{ + IPStackEvent_t xRxEvent = { eNetworkDownEvent, NULL }; + + if( xInterfaceState != INTERFACE_DOWN ) + { + xInterfaceState = INTERFACE_DOWN; + xSendEventStructToIPTask( &xRxEvent, 0 ); + } +} + +void vNetworkNotifyIFUp() +{ + xInterfaceState = INTERFACE_UP; +} + +esp_err_t wlanif_input( void * netif, + void * buffer, + uint16_t len, + void * eb ) +{ + NetworkBufferDescriptor_t * pxNetworkBuffer; + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 ); + + #if ( ipconfigHAS_PRINTF != 0 ) + { + vPrintResourceStats(); + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + + if( eConsiderFrameForProcessing( buffer ) != eProcessBuffer ) + { + ESP_LOGD( TAG, "Dropping packet" ); + esp_wifi_internal_free_rx_buffer( eb ); + return ESP_OK; + } + + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( len, xDescriptorWaitTime ); + + if( pxNetworkBuffer != NULL ) + { + /* Set the packet size, in case a larger buffer was returned. */ + pxNetworkBuffer->xDataLength = len; + + /* Copy the packet data. */ + memcpy( pxNetworkBuffer->pucEthernetBuffer, buffer, len ); + xRxEvent.pvData = ( void * ) pxNetworkBuffer; + + if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFAIL ) + { + ESP_LOGE( TAG, "Failed to enqueue packet to network stack %p, len %d", buffer, len ); + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + return ESP_FAIL; + } + + esp_wifi_internal_free_rx_buffer( eb ); + return ESP_OK; + } + else + { + ESP_LOGE( TAG, "Failed to get buffer descriptor" ); + return ESP_FAIL; + } +} diff --git a/FreeRTOS/source/portable/NetworkInterface/include/phyHandling.h b/FreeRTOS/source/portable/NetworkInterface/include/phyHandling.h new file mode 100644 index 0000000..ad9db4c --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/include/phyHandling.h @@ -0,0 +1,161 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/** + * @brief + * 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 + * '*pulValue'. 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 'ulValue' 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_KSZ8795 0x00221550 + #define PHY_ID_KSZ8081MNXIA 0x00221560 + + #define PHY_ID_DP83848I 0x20005C90 + #define PHY_ID_DP83TC811S 0x2000A250 + + #define PHY_ID_TM4C129X 0x2000A221 + + #define PHY_ID_MV88E6071 0xFF000710 + +/* 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 command to the connected PHY ports and send configuration. */ + BaseType_t xPhyConfigure( EthernetPhy_t * pxPhyObject, + const PhyProperties_t * pxPhyProperties ); + +/* Give a command 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 ); + +/* Get the bitmask of a given 'EthernetPhy_t'. */ + #define xPhyGetMask( pxPhyObject ) \ + ( ( ( ( uint32_t ) 1u ) << ( pxPhyObject )->xPortCount ) - 1u ) + + #ifdef __cplusplus + } /* extern "C" */ + #endif + +#endif /* ifndef PHYHANDLING_H */ diff --git a/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/NetworkInterface.c new file mode 100644 index 0000000..2b224cf --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/NetworkInterface.c @@ -0,0 +1,1332 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* Standard includes. */ +#include +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "sam4e_xplained_pro.h" +#include "hr_gettime.h" +#include "conf_eth.h" +#include "ksz8851snl.h" +#include "ksz8851snl_reg.h" + +/* Some files from the Atmel Software Framework */ +#include +#include +#include + +/* + * Sending a packet: + * + * 1) Called by UP-task, add buffer to the TX-list: + * xNetworkInterfaceOutput() + * tx_buffers[ us_tx_head ] = pxNetworkBuffer; + * tx_busy[ us_tx_head ] = pdTRUE; + * us_tx_head++; + * + * 2) Called by EMAC-Task: start SPI transfer + * ksz8851snl_update() + * if( ul_spi_pdc_status == SPI_PDC_IDLE ) + * { + * if( ( tx_busy[ us_tx_tail ] != pdFALSE ) && + * ( us_pending_frame == 0 ) && + * ( ul_had_intn_interrupt == 0 ) ) + * { + * // disable all interrupts. + * ksz8851_reg_write( REG_INT_MASK, 0 ); + * Bring KSZ8851SNL_CSN_GPIO low + * ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength ); + * ul_spi_pdc_status = SPI_PDC_TX_START; + * tx_cur_buffer = pxNetworkBuffer; + * } + * } + * 3) Wait for SPI RXBUFF interrupt + * SPI_Handler() + * if( ul_spi_pdc_status == SPI_PDC_TX_START ) + * { + * if( SPI_Status & SPI_SR_RXBUFF ) + * { + * ul_spi_pdc_status = SPI_PDC_TX_COMPLETE; + * } + * } + * + * 4) Called by EMAC-Task: finish SPI transfer + * ksz8851snl_update() + * if( ul_spi_pdc_status == SPI_PDC_TX_COMPLETE ) + * { + * ul_spi_pdc_status = SPI_PDC_IDLE; + * Bring KSZ8851SNL_CSN_GPIO high + * // TX step12: disable TXQ write access. + * ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); + * // TX step12.1: enqueue frame in TXQ. + * ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE ); + * + * // RX step13: enable INT_RX flag. + * ksz8851_reg_write( REG_INT_MASK, INT_RX ); + * + * // Buffer sent, free the corresponding buffer and mark descriptor as owned by software. + * vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + * + * tx_buffers[ us_tx_tail ] = NULL; + * tx_busy[ us_tx_tail ] = pdFALSE; + * us_tx_tail++ + * } + * + * Receiving a packet: + * + * 1) Wait for a INTN interrupt + * INTN_Handler() + * ul_had_intn_interrupt = 1 + * vTaskNotifyGiveFromISR(); // Wake up the EMAC task + * + * 2) Called by EMAC-Task: check for new fragments and start SPI transfer + * ksz8851snl_update() + * if( ul_spi_pdc_status == SPI_PDC_IDLE ) + * { + * if( ( ul_had_intn_interrupt != 0 ) || ( us_pending_frame > 0 ) ) + * { + * if( us_pending_frame == 0 ) + * { + * us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8; + * if( us_pending_frame == 0 ) + * { + * break; + * } + * } + * // RX step2: disable all interrupts. + * ksz8851_reg_write( REG_INT_MASK, 0 ); + * Check if there is a valid packet: REG_RX_FHR_STATUS + * Read the length of the next fragment: REG_RX_FHR_BYTE_CNT + * ul_spi_pdc_status = SPI_PDC_RX_START; + * gpio_set_pin_low(KSZ8851SNL_CSN_GPIO); + * // Start SPI data transfer + * ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer, xReadLength ); + * } + * } + * + * 3) Wait for SPI RXBUFF interrupt + * SPI_Handler() + * if( ul_spi_pdc_status == SPI_PDC_RX_START: + * { + * if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 ) + * { + * // Transfer complete, disable SPI RXBUFF interrupt. + * spi_disable_interrupt( KSZ8851SNL_SPI, SPI_IDR_RXBUFF ); + * + * ul_spi_pdc_status = SPI_PDC_RX_COMPLETE; + * } + * } + * } + * + * 4) Finish SPI transfer + * ksz8851snl_update() + * if( ul_spi_pdc_status == SPI_PDC_RX_COMPLETE ) + * { + * ul_spi_pdc_status = SPI_PDC_IDLE; + * Bring KSZ8851SNL_CSN_GPIO high + * // RX step21: end RXQ read access. + * ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START); + * // RX step22-23: update frame count to be read. + * us_pending_frame-- + * // RX step24: enable INT_RX flag if transfer complete. + * if( us_pending_frame == 0 ) + * { + * // Allow more RX interrupts. + * ksz8851_reg_write( REG_INT_MASK, INT_RX ); + * } + * + * // Mark descriptor ready to be read. + * rx_ready[ rxHead ] = pdTRUE; + * rxHead++ + * } + */ + +#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_REG_05_LPA 0x05 /* Link partner ability reg */ +#define PHY_REG_06_ANER 0x06 /* 6 RW Auto-Negotiation Expansion Register */ +#define PHY_REG_07_ANNPTR 0x07 /* 7 RW Auto-Negotiation Next Page TX */ +#define PHY_REG_08_RESERVED0 0x08 /* 0x08..0x0Fh 8-15 RW RESERVED */ + +#define BMSR_LINK_STATUS 0x0004 /*!< Link status */ + +#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 +#endif + +/* Interrupt events to process. Currently only the Rx event is processed + * although code for other events is included to allow for possible future + * expansion. */ +#define EMAC_IF_RX_EVENT 1UL +#define EMAC_IF_TX_EVENT 2UL +#define EMAC_IF_ERR_EVENT 4UL +#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) + +#define ETHERNET_CONF_PHY_ADDR BOARD_GMAC_PHY_ADDR + +#ifdef ipconfigHAS_TX_CRC_OFFLOADING + #undef ipconfigHAS_TX_CRC_OFFLOADING +#endif +/* Override this define because the KSZ8851 is programmed to set all outgoing CRC's */ +#define ipconfigHAS_TX_CRC_OFFLOADING 1 + +#ifndef EMAC_MAX_BLOCK_TIME_MS + #define EMAC_MAX_BLOCK_TIME_MS 100ul +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to 4x + * the size of the stack used by the idle task - but allow this to be overridden in + * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 6 * configMINIMAL_STACK_SIZE ) +#endif + +#define SPI_PDC_IDLE 0 +#define SPI_PDC_RX_START 1 +#define SPI_PDC_TX_ERROR 2 +#define SPI_PDC_RX_COMPLETE 3 +#define SPI_PDC_TX_START 4 +#define SPI_PDC_RX_ERROR 5 +#define SPI_PDC_TX_COMPLETE 6 + +/** + * ksz8851snl driver structure. + */ +typedef struct +{ + /** Set to 1 when owner is software (ready to read), 0 for Micrel. */ + uint32_t rx_ready[ MICREL_RX_BUFFERS ]; + /** Set to 1 when owner is Micrel, 0 for software. */ + uint32_t tx_busy[ MICREL_TX_BUFFERS ]; + /** RX NetworkBufferDescriptor_t pointer list */ + NetworkBufferDescriptor_t * rx_buffers[ MICREL_RX_BUFFERS ]; + /** TX NetworkBufferDescriptor_t pointer list */ + NetworkBufferDescriptor_t * tx_buffers[ MICREL_TX_BUFFERS ]; + NetworkBufferDescriptor_t * tx_cur_buffer; + + /** Circular buffer head pointer for packet received. */ + uint32_t us_rx_head; + /** Circular buffer tail pointer for packet to be read. */ + uint32_t us_rx_tail; + /** Circular buffer head pointer by upper layer (buffer to be sent). */ + uint32_t us_tx_head; + /** Circular buffer tail pointer incremented by handlers (buffer sent). */ + uint32_t us_tx_tail; + + uint32_t ul_total_tx; + uint32_t ul_total_rx; + uint32_t tx_space; + + /** Still experimental: hash table to allow certain multicast addresses. */ + uint16_t pusHashTable[ 4 ]; + + /* ul_spi_pdc_status has "SPI_PDC_xxx" values. */ + volatile uint32_t ul_spi_pdc_status; + + /* ul_had_intn_interrupt becomes true within the INTN interrupt. */ + volatile uint32_t ul_had_intn_interrupt; + + uint16_t us_pending_frame; +} xKSZ8851_Device_t; + +/* SPI PDC register base. + * Declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */ +extern Pdc * g_p_spi_pdc; + +/* Temporary buffer for PDC reception. + * declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */ +extern uint8_t tmpbuf[ 1536 ]; + +COMPILER_ALIGNED( 8 ) +static xKSZ8851_Device_t xMicrelDevice; + +static TaskHandle_t xTransmitHandle; + +/*-----------------------------------------------------------*/ + +/* + * Wait a fixed time for the link status to indicate the network is up. + */ +static BaseType_t xGMACWaitLS( TickType_t xMaxTime ); + +/* + * A deferred interrupt handler task that processes GMAC interrupts. + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/* + * Try to obtain an Rx packet from the hardware. + */ +static uint32_t prvEMACRxPoll( void ); + +static inline unsigned long ulReadMDIO( unsigned uAddress ); + +static void ksz8851snl_low_level_init( void ); + +static NetworkBufferDescriptor_t * ksz8851snl_low_level_input( void ); + +/*-----------------------------------------------------------*/ + +/* Bit map of outstanding ETH interrupt events for processing. Currently only + * the Rx interrupt is handled, although code is included for other events to + * enable future expansion. */ +static volatile uint32_t ulISREvents; + +/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ +static uint32_t ulPHYLinkStatus = 0; +static volatile BaseType_t xGMACSwitchRequired; + +static void ksz8851snl_update( void ); + +static void ksz8851snl_rx_init( void ); + +static void ksz8851snl_tx_init( void ); + +/* Holds the handle of the task used as a deferred interrupt processor. The + * handle is used so direct notifications can be sent to the task for all EMAC/DMA + * related interrupts. */ +TaskHandle_t xEMACTaskHandle = NULL; + + +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + const TickType_t x5_Seconds = 5000UL; + + if( xEMACTaskHandle == NULL ) + { + ksz8851snl_low_level_init(); + + /* Wait at most 5 seconds for a Link Status in the PHY. */ + xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) ); + + /* The handler task is created at the highest possible priority to + * ensure the interrupt handler can return directly to it. */ + xTaskCreate( prvEMACHandlerTask, "KSZ8851", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle ); + configASSERT( xEMACTaskHandle != NULL ); + } + + /* When returning non-zero, the stack will become active and + * start DHCP (in configured) */ + ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0; +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ + BaseType_t xResult; + + /* This function returns true if the Link Status in the PHY is high. */ + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + xResult = pdTRUE; + } + else + { + xResult = pdFALSE; + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t bReleaseAfterSend ) +{ + BaseType_t xResult = pdFALSE; + int txHead = xMicrelDevice.us_tx_head; + + /* Make sure the next descriptor is free. */ + if( xMicrelDevice.tx_busy[ txHead ] != pdFALSE ) + { + /* All TX buffers busy. */ + } + else if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 ) + { + /* Output: LS low. */ + } + else + { + /* Pass the packet. */ + xMicrelDevice.tx_buffers[ txHead ] = pxNetworkBuffer; + /* The descriptor is now owned by Micrel. */ + xMicrelDevice.tx_busy[ txHead ] = pdTRUE; + + /* Move the head pointer. */ + if( ++txHead == MICREL_TX_BUFFERS ) + { + txHead = 0; + } + + xMicrelDevice.us_tx_head = txHead; + + if( xEMACTaskHandle != NULL ) + { + xTaskNotifyGive( xEMACTaskHandle ); + } + + #if ( ipconfigZERO_COPY_TX_DRIVER != 1 ) + #warning Please ipconfigZERO_COPY_TX_DRIVER as 1 + #endif + configASSERT( bReleaseAfterSend != pdFALSE ); + xResult = pdTRUE; + } + + if( ( xResult == pdFALSE ) && ( bReleaseAfterSend != pdFALSE ) ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +/* This Micrel has numbered it's PHY registers in a different way. + * Translate the register index. */ +static int ks8851_phy_reg( int reg ) +{ + switch( reg ) + { + case PHY_REG_00_BMCR: + return REG_PHY_CNTL; /* P1MBCR; */ + + case PHY_REG_01_BMSR: + return REG_PHY_STATUS; + + case PHY_REG_02_PHYSID1: + return REG_PHY_ID_LOW; + + case PHY_REG_03_PHYSID2: + return REG_PHY_ID_HIGH; + + case PHY_REG_04_ADVERTISE: + return REG_PHY_AUTO_NEGOTIATION; + + case PHY_REG_05_LPA: + return REG_PHY_REMOTE_CAPABILITY; + } + + return 0x0; +} +/*-----------------------------------------------------------*/ + +static inline unsigned long ulReadMDIO( unsigned uAddress ) +{ + uint16_t usPHYStatus; + int ks8851_reg = ks8851_phy_reg( uAddress ); + + if( ks8851_reg != 0 ) + { + usPHYStatus = ksz8851_reg_read( ks8851_reg ); + } + else + { + /* Other addresses not yet implemented. */ + usPHYStatus = 0; + } + + return usPHYStatus; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xGMACWaitLS( TickType_t xMaxTime ) +{ + TickType_t xStartTime = xTaskGetTickCount(); + TickType_t xEndTime; + BaseType_t xReturn; + const TickType_t xShortTime = pdMS_TO_TICKS( 100UL ); + const uint32_t ulHz_Per_MHz = 1000000UL; + + for( ; ; ) + { + xEndTime = xTaskGetTickCount(); + + if( ( xEndTime - xStartTime ) > xMaxTime ) + { + /* Waited more than xMaxTime, return. */ + xReturn = pdFALSE; + break; + } + + /* Check the link status again. */ + ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + /* Link is up - return. */ + xReturn = pdTRUE; + break; + } + + /* Link is down - wait in the Blocked state for a short while (to allow + * other tasks to execute) before checking again. */ + vTaskDelay( xShortTime ); + } + + FreeRTOS_printf( ( "xGMACWaitLS: %ld freq %lu Mz\n", + xReturn, + sysclk_get_cpu_hz() / ulHz_Per_MHz ) ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void vPioSetPinHigh( uint32_t ul_pin ) +{ + Pio * p_pio = ( Pio * ) ( ( uint32_t ) PIOA + ( PIO_DELTA * ( ul_pin >> 5 ) ) ); + + /* Value to be driven on the I/O line: 1. */ + p_pio->PIO_SODR = 1 << ( ul_pin & 0x1F ); +} + +/** + * \brief Handler for SPI interrupt. + */ +void SPI_Handler( void ) +{ + BaseType_t xDoWakeup = pdFALSE; + BaseType_t xKSZTaskWoken = pdFALSE; + uint32_t ulCurrentSPIStatus; + uint32_t ulEnabledSPIStatus; + + ulCurrentSPIStatus = spi_read_status( KSZ8851SNL_SPI ); + ulEnabledSPIStatus = spi_read_interrupt_mask( KSZ8851SNL_SPI ); + ulCurrentSPIStatus &= ulEnabledSPIStatus; + spi_disable_interrupt( KSZ8851SNL_SPI, ulCurrentSPIStatus ); + + switch( xMicrelDevice.ul_spi_pdc_status ) + { + case SPI_PDC_RX_START: + + if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 ) + { + pdc_disable_transfer( g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS ); + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_ERROR; + xDoWakeup = pdTRUE; + } + else + { + if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 ) + { + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_COMPLETE; + xDoWakeup = pdTRUE; + } + } + + break; + + case SPI_PDC_TX_START: + + /* Middle of TX. */ + if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 ) + { + pdc_disable_transfer( g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS ); + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_ERROR; + xDoWakeup = pdTRUE; + } + else + { + if( ( ulCurrentSPIStatus & SPI_SR_ENDRX ) != 0 ) + { + /* Enable RX complete interrupt. */ + spi_enable_interrupt( KSZ8851SNL_SPI, SPI_IER_RXBUFF ); + } + + /* End of TX. */ + if( ( ulCurrentSPIStatus & SPI_END_OF_TX ) != 0 ) + { + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_COMPLETE; + xDoWakeup = pdTRUE; + } + } + + break; + } /* switch( xMicrelDevice.ul_spi_pdc_status ) */ + + if( xDoWakeup != pdFALSE ) + { + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xKSZTaskWoken ); + } + } + else + { + } + + portEND_SWITCHING_ISR( xKSZTaskWoken ); +} +/*-----------------------------------------------------------*/ + +static void INTN_Handler( uint32_t id, + uint32_t mask ) +{ + BaseType_t xKSZTaskWoken = pdFALSE; + + if( ( id == INTN_ID ) && + ( mask == INTN_PIN_MSK ) ) + { + /* Clear the PIO interrupt flags. */ + pio_get_interrupt_status( INTN_PIO ); + + /* Set the INTN flag. */ + xMicrelDevice.ul_had_intn_interrupt++; + + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &( xKSZTaskWoken ) ); + } + } + + portEND_SWITCHING_ISR( xKSZTaskWoken ); +} +/*-----------------------------------------------------------*/ + +/** + * \brief Populate the RX descriptor ring buffers with pbufs. + * + * \param p_ksz8851snl_dev Pointer to driver data structure. + */ +static void ksz8851snl_rx_populate_queue( void ) +{ + uint32_t ul_index = 0; + NetworkBufferDescriptor_t * pxNetworkBuffer; + + /* Set up the RX descriptors */ + for( ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++ ) + { + if( xMicrelDevice.rx_buffers[ ul_index ] == NULL ) + { + /* Allocate a new NetworkBufferDescriptor_t with the maximum size. */ + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipconfigNETWORK_MTU + 36, 100 ); + + if( pxNetworkBuffer == NULL ) + { + FreeRTOS_printf( ( "ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t allocation failure\n" ) ); + configASSERT( 1 == 2 ); + } + + /* Make sure lwIP is well configured so one NetworkBufferDescriptor_t can contain the maximum packet size. */ + /*LWIP_ASSERT("ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t size too small!", pbuf_clen(pxNetworkBuffer) <= 1); */ + + /* Save NetworkBufferDescriptor_t pointer to be sent to lwIP upper layer. */ + xMicrelDevice.rx_buffers[ ul_index ] = pxNetworkBuffer; + /* Pass it to Micrel for reception. */ + xMicrelDevice.rx_ready[ ul_index ] = pdFALSE; + } + } +} + +unsigned tx_space, wait_tx_space, tx_status, fhr_status; +unsigned rx_debug = 0; + +/** + * \brief Update Micrel state machine and perform required actions. + * + * \param netif the lwIP network interface structure for this ethernetif. + */ +static void ksz8851snl_update() +{ + uint16_t txmir = 0; + +/* Check for free PDC. */ + switch( xMicrelDevice.ul_spi_pdc_status ) + { + case SPI_PDC_TX_ERROR: + { + uint32_t ulValue; + /* / * TX step11: end TX transfer. * / */ + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + + vTaskDelay( 2 ); + gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); + vTaskDelay( 1 ); + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + vTaskDelay( 1 ); + + /* Disable asynchronous transfer mode. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; + + /* TX step12: disable TXQ write access. */ + ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); + + ulValue = ksz8851snl_reset_tx(); + + xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK; + + FreeRTOS_printf( ( "SPI_PDC_TX_ERROR %02X\n", ulValue ) ); + } + break; + + case SPI_PDC_RX_ERROR: + { + uint32_t ulValue; + /* TX step11: end TX transfer. */ + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + + vTaskDelay( 2 ); + gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); + vTaskDelay( 1 ); + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + vTaskDelay( 1 ); + + /* Disable asynchronous transfer mode. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; + + /* TX step12: disable TXQ write access. */ + ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); + + /*ulValue = ksz8851snl_reset_rx(); */ + ulValue = ksz8851snl_reinit(); + + xGMACWaitLS( pdMS_TO_TICKS( 5000UL ) ); + + FreeRTOS_printf( ( "SPI_PDC_RX_ERROR %02X\n", ulValue ) ); + } + break; + } + + switch( xMicrelDevice.ul_spi_pdc_status ) + { + case SPI_PDC_IDLE: + { + int txTail = xMicrelDevice.us_tx_tail; + + /* + * ========================== Handle RX ========================== + */ + if( ( xMicrelDevice.ul_had_intn_interrupt != 0 ) || ( xMicrelDevice.us_pending_frame > 0 ) ) + { + int rxHead = xMicrelDevice.us_rx_head; + NetworkBufferDescriptor_t * pxNetworkBuffer; + #warning try + xMicrelDevice.ul_had_intn_interrupt = 0; + + if( xMicrelDevice.us_pending_frame == 0 ) + { + uint16_t int_status; + /* RX step1: read interrupt status for INT_RX flag. */ + int_status = ksz8851_reg_read( REG_INT_STATUS ); + + + /* RX step2: disable all interrupts. */ + ksz8851_reg_write( REG_INT_MASK, 0 ); + + /* RX step3: clear INT_RX flag. */ + ksz8851_reg_setbits( REG_INT_STATUS, INT_RX ); + + /* RX step4-5: check for received frames. */ + xMicrelDevice.us_pending_frame = ksz8851_reg_read( REG_RX_FRAME_CNT_THRES ) >> 8; + + if( xMicrelDevice.us_pending_frame == 0 ) + { + /* RX step24: enable INT_RX flag. */ + ksz8851_reg_write( REG_INT_MASK, INT_RX ); + return; + } + } + + #warning try + xMicrelDevice.ul_had_intn_interrupt = 0; + + /* Now xMicrelDevice.us_pending_frame != 0 */ + + /* Don't break Micrel state machine, wait for a free descriptor first! */ + if( xMicrelDevice.rx_ready[ rxHead ] != pdFALSE ) + { + FreeRTOS_printf( ( "ksz8851snl_update: out of free descriptor! [tail=%u head=%u]\n", + xMicrelDevice.us_rx_tail, rxHead ) ); + return; + } + + pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxHead ]; + + if( pxNetworkBuffer == NULL ) + { + ksz8851snl_rx_populate_queue(); + FreeRTOS_printf( ( "ksz8851snl_update: no buffer set [head=%u]\n", rxHead ) ); + return; + } + + /* RX step6: get RX packet status. */ + fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS ); + + if( ( ( fhr_status & RX_VALID ) == 0 ) || ( ( fhr_status & RX_ERRORS ) != 0 ) ) + { + ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_CMD_FREE_PACKET ); + FreeRTOS_printf( ( "ksz8851snl_update: RX packet error!\n" ) ); + + /* RX step4-5: check for received frames. */ + xMicrelDevice.us_pending_frame = ksz8851_reg_read( REG_RX_FRAME_CNT_THRES ) >> 8; + + if( xMicrelDevice.us_pending_frame == 0 ) + { + /* RX step24: enable INT_RX flag. */ + ksz8851_reg_write( REG_INT_MASK, INT_RX ); + } + + ulISREvents |= EMAC_IF_ERR_EVENT; + } + else + { + size_t xLength; + /* RX step7: read frame length. */ + xLength = ksz8851_reg_read( REG_RX_FHR_BYTE_CNT ) & RX_BYTE_CNT_MASK; + + /* RX step8: Drop packet if len is invalid or no descriptor available. */ + if( xLength == 0 ) + { + ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_CMD_FREE_PACKET ); + FreeRTOS_printf( ( "ksz8851snl_update: RX bad len!\n" ) ); + ulISREvents |= EMAC_IF_ERR_EVENT; + } + else + { + size_t xReadLength = xLength; + + xMicrelDevice.ul_total_rx++; + /* RX step9: reset RX frame pointer. */ + ksz8851_reg_clrbits( REG_RX_ADDR_PTR, ADDR_PTR_MASK ); + + /* RX step10: start RXQ read access. */ + ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_START ); + /* RX step11-17: start asynchronous FIFO read operation. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_START; + gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); + + if( ( xReadLength & ( sizeof( size_t ) - 1 ) ) != 0 ) + { + xReadLength = ( xReadLength | ( sizeof( size_t ) - 1 ) ) + 1; + } + + /* Pass the buffer minus 2 bytes, see ksz8851snl.c: RXQ_TWOBYTE_OFFSET. */ + ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer - 2, xReadLength ); + /* Remove CRC and update buffer length. */ + xLength -= 4; + pxNetworkBuffer->xDataLength = xLength; + /* Wait for SPI interrupt to set status 'SPI_PDC_RX_COMPLETE'. */ + } + } + + break; + } /* ul_had_intn_interrupt || us_pending_frame */ + + /* + * ========================== Handle TX ========================== + */ + + /* Fetch next packet to be sent. */ + if( ( xMicrelDevice.tx_busy[ txTail ] != pdFALSE ) && + ( xMicrelDevice.us_pending_frame == 0 ) && + ( xMicrelDevice.ul_had_intn_interrupt == 0 ) ) + { + NetworkBufferDescriptor_t * pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ]; + size_t xLength = pxNetworkBuffer->xDataLength; + int iIndex = xLength; + + xLength = 4 * ( ( xLength + 3 ) / 4 ); + + while( iIndex < ( int ) xLength ) + { + pxNetworkBuffer->pucEthernetBuffer[ iIndex ] = '\0'; + iIndex++; + } + + pxNetworkBuffer->xDataLength = xLength; + + /* TX step1: check if TXQ memory size is available for transmit. */ + txmir = ksz8851_reg_read( REG_TX_MEM_INFO ); + txmir = txmir & TX_MEM_AVAILABLE_MASK; + + if( txmir < ( xLength + 8 ) ) + { + if( wait_tx_space == pdFALSE ) + { + tx_status = ksz8851_reg_read( REG_TX_STATUS ); + fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS ); + wait_tx_space = pdTRUE; + } + + /*return; */ + rx_debug = 1; + tx_space = txmir; + } + else + { + tx_space = txmir; + + /* TX step2: disable all interrupts. */ + ksz8851_reg_write( REG_INT_MASK, 0 ); + + xMicrelDevice.tx_space -= xLength; + + /* TX step3: enable TXQ write access. */ + ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_START ); + /* TX step4-8: perform FIFO write operation. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_START; + xMicrelDevice.tx_cur_buffer = pxNetworkBuffer; + /* Bring SPI SS low. */ + gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); + xMicrelDevice.ul_total_tx++; + + ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength ); + } + } + } + break; /* SPI_PDC_IDLE */ + + case SPI_PDC_RX_COMPLETE: + { + int rxHead = xMicrelDevice.us_rx_head; + /* RX step18-19: pad with dummy data to keep dword alignment. */ + /* Packet lengths will be rounded up to a multiple of "sizeof size_t". */ +/* xLength = xMicrelDevice.rx_buffers[ rxHead ]->xDataLength & 3; */ +/* if( xLength != 0 ) */ +/* { */ +/* ksz8851_fifo_dummy( 4 - xLength ); */ +/* } */ + + /* RX step20: end RX transfer. */ + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + + /* Disable asynchronous transfer mode. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; + + /* RX step21: end RXQ read access. */ + ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); + + /* RX step22-23: update frame count to be read. */ + xMicrelDevice.us_pending_frame -= 1; + + /* RX step24: enable INT_RX flag if transfer complete. */ + if( xMicrelDevice.us_pending_frame == 0 ) + { + ksz8851_reg_write( REG_INT_MASK, INT_RX ); + } + + /* Mark descriptor ready to be read. */ + xMicrelDevice.rx_ready[ rxHead ] = pdTRUE; + + if( ++rxHead == MICREL_RX_BUFFERS ) + { + rxHead = 0; + } + + xMicrelDevice.us_rx_head = rxHead; + + if( rx_debug != 0 ) + { + uint32_t txmir; + rx_debug = 0; + txmir = ksz8851_reg_read( REG_TX_MEM_INFO ); + txmir = txmir & TX_MEM_AVAILABLE_MASK; + } + + /* Tell prvEMACHandlerTask that RX packets are available. */ + ulISREvents |= EMAC_IF_RX_EVENT; + } /* case SPI_PDC_RX_COMPLETE */ + break; + + case SPI_PDC_TX_COMPLETE: + { + int txTail = xMicrelDevice.us_tx_tail; + NetworkBufferDescriptor_t * pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ]; + + size_t xLength; + /* TX step9-10: pad with dummy data to keep dword alignment. */ + /* Not necessary: length is already a multiple of 4. */ + xLength = pxNetworkBuffer->xDataLength & 3; + + if( xLength != 0 ) + { +/* ksz8851_fifo_dummy( 4 - xLength ); */ + } + +/* / * TX step11: end TX transfer. * / */ + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + + /* Disable asynchronous transfer mode. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; + + /* TX step12: disable TXQ write access. */ + ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); + + xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK; + + /* TX step12.1: enqueue frame in TXQ. */ + ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE ); + + /* RX step13: enable INT_RX flag. */ +/* ksz8851_reg_write( REG_INT_MASK, INT_RX ); */ + /* Buffer sent, free the corresponding buffer and mark descriptor as owned by software. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + + xMicrelDevice.tx_buffers[ txTail ] = NULL; + xMicrelDevice.tx_busy[ txTail ] = pdFALSE; + + if( ++txTail == MICREL_TX_BUFFERS ) + { + txTail = 0; + } + + xMicrelDevice.us_tx_tail = txTail; + + /* Experiment. */ + /*xMicrelDevice.ul_had_intn_interrupt = 1; */ + if( xTransmitHandle != NULL ) + { + xTaskNotifyGive( xTransmitHandle ); + } + + #warning moved downward + /* RX step13: enable INT_RX flag. */ + ksz8851_reg_write( REG_INT_MASK, INT_RX ); + /* Prevent the EMAC task from sleeping a single time. */ + ulISREvents |= EMAC_IF_TX_EVENT; + } /* case SPI_PDC_TX_COMPLETE */ + break; + } /* switch( xMicrelDevice.ul_spi_pdc_status ) */ +} + +/** + * \brief Set up the RX descriptor ring buffers. + * + * This function sets up the descriptor list used for RX packets. + * + */ +static void ksz8851snl_rx_init() +{ + uint32_t ul_index = 0; + + /* Init pointer index. */ + xMicrelDevice.us_rx_head = 0; + xMicrelDevice.us_rx_tail = 0; + + /* Set up the RX descriptors. */ + for( ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++ ) + { + xMicrelDevice.rx_buffers[ ul_index ] = NULL; + xMicrelDevice.rx_ready[ ul_index ] = pdFALSE; + } + + /* Build RX buffer and descriptors. */ + ksz8851snl_rx_populate_queue(); +} + +/** + * \brief Set up the TX descriptor ring buffers. + * + * This function sets up the descriptor list used for TX packets. + * + */ +static void ksz8851snl_tx_init() +{ + uint32_t ul_index = 0; + + /* Init TX index pointer. */ + xMicrelDevice.us_tx_head = 0; + xMicrelDevice.us_tx_tail = 0; + + /* Set up the TX descriptors */ + for( ul_index = 0; ul_index < MICREL_TX_BUFFERS; ul_index++ ) + { + xMicrelDevice.tx_busy[ ul_index ] = pdFALSE; + } + + xMicrelDevice.tx_space = 6144; +} + +/** + * \brief Initialize ksz8851snl ethernet controller. + * + * \note Called from ethernetif_init(). + * + * \param netif the lwIP network interface structure for this ethernetif. + */ +static void ksz8851snl_low_level_init( void ) +{ + ksz8851snl_rx_init(); + ksz8851snl_tx_init(); + + /* Enable NVIC interrupts. */ + NVIC_SetPriority( SPI_IRQn, INT_PRIORITY_SPI ); + NVIC_EnableIRQ( SPI_IRQn ); + + /* Initialize SPI link. */ + if( ksz8851snl_init() < 0 ) + { + FreeRTOS_printf( ( "ksz8851snl_low_level_init: failed to initialize the Micrel driver!\n" ) ); + configASSERT( ipFALSE_BOOL ); + } + + memset( xMicrelDevice.pusHashTable, 255, sizeof( xMicrelDevice.pusHashTable ) ); + ksz8851_reg_write( REG_MAC_HASH_0, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 0 ] ) ); + ksz8851_reg_write( REG_MAC_HASH_2, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 1 ] ) ); + ksz8851_reg_write( REG_MAC_HASH_4, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 2 ] ) ); + ksz8851_reg_write( REG_MAC_HASH_6, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 3 ] ) ); + + /* Initialize interrupt line INTN. */ + configure_intn( INTN_Handler ); +} + +/** + * \brief Use pre-allocated pbuf as DMA source and return the incoming packet. + * + * \param netif the lwIP network interface structure for this ethernetif. + * + * \return a pbuf filled with the received packet (including MAC header). + * 0 on memory error. + */ +static NetworkBufferDescriptor_t * ksz8851snl_low_level_input( void ) +{ + NetworkBufferDescriptor_t * pxNetworkBuffer = NULL; + int rxTail = xMicrelDevice.us_rx_tail; + + /* Check that descriptor is owned by software (ie packet received). */ + if( xMicrelDevice.rx_ready[ rxTail ] != pdFALSE ) + { + /* Fetch pre-allocated buffer */ + pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxTail ]; + + /* Remove this pbuf from its descriptor. */ + xMicrelDevice.rx_buffers[ rxTail ] = NULL; + + /* Clears rx_ready and sets rx_buffers. */ + ksz8851snl_rx_populate_queue(); + + if( ++rxTail == MICREL_RX_BUFFERS ) + { + rxTail = 0; + } + + xMicrelDevice.us_rx_tail = rxTail; + } + + return pxNetworkBuffer; +} +/*-----------------------------------------------------------*/ + +static uint32_t prvEMACRxPoll( void ) +{ + NetworkBufferDescriptor_t * pxNetworkBuffer; + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + uint32_t ulReturnValue = 0; + + for( ; ; ) + { + /* Only for logging. */ + int rxTail = xMicrelDevice.us_rx_tail; + EthernetHeader_t * pxEthernetHeader; + + pxNetworkBuffer = ksz8851snl_low_level_input(); + + if( pxNetworkBuffer == NULL ) + { + break; + } + + pxEthernetHeader = ( EthernetHeader_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); + + if( ( pxEthernetHeader->usFrameType != ipIPv4_FRAME_TYPE ) && + ( pxEthernetHeader->usFrameType != ipARP_FRAME_TYPE ) ) + { + FreeRTOS_printf( ( "Frame type %02X received\n", pxEthernetHeader->usFrameType ) ); + } + + ulReturnValue++; + + xRxEvent.pvData = ( void * ) pxNetworkBuffer; + + /* Send the descriptor to the IP task for processing. */ + if( xSendEventStructToIPTask( &xRxEvent, 100UL ) != pdTRUE ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) ); + } + } + + return ulReturnValue; +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void * pvParameters ) +{ + TimeOut_t xPhyTime; + TickType_t xPhyRemTime; + TickType_t xLoggingTime; + UBaseType_t uxLastMinBufferCount = 0; + UBaseType_t uxCurrentCount; + BaseType_t xResult = 0; + uint32_t xStatus; + const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS ); + + #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + UBaseType_t uxLastMinQueueSpace = 0; + #endif + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + configASSERT( xEMACTaskHandle != NULL ); + + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + xLoggingTime = xTaskGetTickCount(); + + for( ; ; ) + { + uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); + + if( uxLastMinBufferCount != uxCurrentCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinBufferCount = uxCurrentCount; + FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", + uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); + } + + #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + { + uxCurrentCount = uxGetMinimumIPQueueSpace(); + + if( uxLastMinQueueSpace != uxCurrentCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinQueueSpace = uxCurrentCount; + FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); + } + } + #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ + + /* Run the state-machine of the ksz8851 driver. */ + ksz8851snl_update(); + + if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 ) + { + /* No events to process now, wait for the next. */ + ulTaskNotifyTake( pdTRUE, ulMaxBlockTime ); + } + + if( ( xTaskGetTickCount() - xLoggingTime ) > 10000 ) + { + xLoggingTime += 10000; + FreeRTOS_printf( ( "Now Tx/Rx %7d /%7d\n", + xMicrelDevice.ul_total_tx, xMicrelDevice.ul_total_rx ) ); + } + + if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 ) + { + ulISREvents &= ~EMAC_IF_RX_EVENT; + + /* Wait for the EMAC interrupt to indicate that another packet has been + * received. */ + xResult = prvEMACRxPoll(); + } + + if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) + { + /* Future extension: code to release TX buffers if zero-copy is used. */ + ulISREvents &= ~EMAC_IF_TX_EVENT; + } + + if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 ) + { + /* Future extension: logging about errors that occurred. */ + ulISREvents &= ~EMAC_IF_ERR_EVENT; + } + + if( xResult > 0 ) + { + /* As long as packets are being received, assume that + * the Link Status is high. */ + ulPHYLinkStatus |= BMSR_LINK_STATUS; + + /* 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 ) && + ( xMicrelDevice.ul_spi_pdc_status == SPI_PDC_IDLE ) ) + { + /* Check the link status again. */ + xStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) ) + { + ulPHYLinkStatus = xStatus; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) ); + } + + 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 ); + } + } + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/ksz8851snl.c b/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/ksz8851snl.c new file mode 100644 index 0000000..1ebdad2 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/ksz8851snl.c @@ -0,0 +1,653 @@ +/** + * + * \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 Atmel Support + */ + +/* 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; +} diff --git a/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/ksz8851snl.h b/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/ksz8851snl.h new file mode 100644 index 0000000..65ec332 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/ksz8851snl.h @@ -0,0 +1,74 @@ +/** + * + * \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 Atmel Support + */ + +#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 */ diff --git a/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/ksz8851snl_reg.h b/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/ksz8851snl_reg.h new file mode 100644 index 0000000..9fb5cc5 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/ksz8851snl/ksz8851snl_reg.h @@ -0,0 +1,475 @@ +/** + * + * \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 Atmel Support + */ + +#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 (default is 125MHz) */ +#define BUS_CLOCK_DIVIDEDBY_5 ( 0x0003 ) /* Bus clock divided by 5 */ +#define BUS_CLOCK_DIVIDEDBY_3 ( 0x0002 ) /* Bus clock divided by 3 */ +#define BUS_CLOCK_DIVIDEDBY_2 ( 0x0001 ) /* Bus clock divided by 2 */ +#define BUS_CLOCK_DIVIDEDBY_1 ( 0x0000 ) /* Bus clock divided by 1 */ +#define BUS_CLOCK_DIVIDED_MASK ( 0x0003 ) /* Bus clock divider 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 ) /* Enable 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 transmit */ + +#define REG_TX_STATUS ( 0x72 ) /* TXSR */ +#define TX_STAT_LATE_COL ( 0x2000 ) /* Transmit late collision occurs */ +#define TX_STAT_MAX_COL ( 0x1000 ) /* Transmit 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 ) /* Enable 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 damaged 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 occurred by timer duration */ +#define RXQ_STAT_BYTE_CNT_INT ( 0x0800 ) /* RX interrupt is occurred by byte count threshold */ +#define RXQ_STAT_FRAME_CNT_INT ( 0x0400 ) /* RX interrupt is occurred 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-up 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 ) /* Receive 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 */ diff --git a/FreeRTOS/source/portable/NetworkInterface/libslirp/MBuffNetifBackendLibslirp.c b/FreeRTOS/source/portable/NetworkInterface/libslirp/MBuffNetifBackendLibslirp.c new file mode 100644 index 0000000..cb6c0e7 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/libslirp/MBuffNetifBackendLibslirp.c @@ -0,0 +1,904 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* libc */ +#include +#include + +/* QEMU Slirp Library */ +#include + +#if defined( _WIN32 ) + #include + #include +#else + #include + #include + #include + #include + #include "wait_for_event.h" +#endif + +#include "errno.h" + +#include "FreeRTOS.h" +#include "message_buffer.h" +#include "FreeRTOSIPConfig.h" +#include "FreeRTOS_IP.h" + +#ifndef IF_MTU_DEFAULT + #define IF_MTU_DEFAULT 1500U +#endif + +#ifndef IF_MRU_DEFAULT + #define IF_MRU_DEFAULT 1500U +#endif + +#define NETWORK_BUFFER_LEN ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) + +#define xSEND_BUFFER_SIZE ( 32U * NETWORK_BUFFER_LEN ) +#define xRECV_BUFFER_SIZE ( 32U * NETWORK_BUFFER_LEN ) +#define xNUM_TIMERS ( 10U ) + +#if defined( _WIN32 ) + typedef uintptr_t Thread_t; + typedef HANDLE Mutex_t; + #define THREAD_RETURN unsigned + #define THREAD_FUNC_DEF __stdcall + static LARGE_INTEGER xClockFrequency; + typedef size_t nfds_t; +#else + typedef pthread_t Thread_t; + typedef pthread_mutex_t Mutex_t; + #define THREAD_RETURN void * + #define THREAD_FUNC_DEF +#endif /* if defined( _WIN32 ) */ + +#if !defined( slirp_ssize_t ) && defined( SSIZE_MAX ) + typedef ssize_t slirp_ssize_t; +#endif + +typedef struct +{ + /* "Hardware" buffers */ + MessageBufferHandle_t xSendMsgBuffer; + MessageBufferHandle_t xRecvMsgBuffer; + StaticMessageBuffer_t xSendMsgBufferStatic; + StaticMessageBuffer_t xRecvMsgBufferStatic; + uint8_t pucTxBuffer[ xSEND_BUFFER_SIZE ]; + uint8_t pucRxBuffer[ xRECV_BUFFER_SIZE ]; + + BaseType_t xExitFlag; + + /* libslirp context */ + struct Slirp * pxSlirp; + + /* File handle storage */ + nfds_t nfds; + size_t xPollFdArraySize; + struct pollfd * pxPollFdArray; + + /* Event used to signal when data is ready in xSendMsgBuffer */ + void * pvSendEvent; + + /* + * Mutex to arbitrate access to libslirp api between + * vTransmitThread and vReceiveThread + */ + Mutex_t xMutex; + Thread_t xTxThread; + Thread_t xRxThread; +} SlirpBackendContext_t; + +static int64_t llSlirp_ClockGetNanoSeconds( void * pvOpaque ); +static slirp_ssize_t xSlirp_WriteCallback( const void * pvBuffer, + size_t uxLen, + void * pvOpaque ); +static void vSlirpGuestError( const char * msg, + void * pvOpaque ); + +/* + * Stub functions for unimplemented timer feature + * Should not be called. + */ +static void * pvSlirp_TimerNew( SlirpTimerCb cb, + void * pvCallbackContext, + void * pvOpaque ); +static void vSlirp_TimerFree( void * pvTimer, + void * pvOpaque ); +static void vSlirp_TimerModify( void * pvTimer, + int64_t expire_time, + void * pvOpaque ); + +#if SLIRP_CHECK_VERSION( 4U, 7U, 0U ) + static void * pvSlirpTimerNewOpaque( SlirpTimerId xTimerId, + void * cb_opaque, + void * pvOpaque ); +#endif /* SLIRP_CHECK_VERSION( 4U, 7U, 0U ) */ + +/* + * Other empty callbacks. Not used for linux port. + */ +static void vSlirp_RegisterPollFd( int lFd, + void * pvCallbackContext ); +static void vSlirp_UnRegisterPollFd( int lFd, + void * pvCallbackContext ); +static void vSlirp_Notify( void * pvCallbackContext ); + +#if SLIRP_CHECK_VERSION( 4U, 7U, 0U ) + static void vSlirp_InitCompleted( Slirp * pxSlirp, + void * pvCallbackContext ); +#endif /* SLIRP_CHECK_VERSION( 4U, 7U, 0U ) */ + +/* Receive and Transmit threads */ +static THREAD_RETURN THREAD_FUNC_DEF vReceiveThread( void * pvParameters ); +static THREAD_RETURN THREAD_FUNC_DEF vTransmitThread( void * pvParameters ); + +/** + * @brief Initialize the slirp posix backend. + * + * @param[out] pxSendMsgBuffer Location to store the handle of the send message buffer. + * @param[out] pxRecvMsgBuffer Location to store the handle of the receive message buffer. + * @param[in] pvSendEvent Pointer of the event struct which the implemenbtation should pend on to receive frames. + * @param[out] ppvBackendContext Location to store an implementation specific void pointer. + */ +void vMBuffNetifBackendInit( MessageBufferHandle_t * pxSendMsgBuffer, + MessageBufferHandle_t * pxRecvMsgBuffer, + void * pvSendEvent, + void ** ppvBackendContext ) +{ + BaseType_t xErrorFlag = pdFALSE; + void * pvContextBuffer = NULL; + SlirpBackendContext_t * pxCtx = NULL; + + static const struct SlirpCb xSlirpCallbacks = + { + .send_packet = xSlirp_WriteCallback, + .guest_error = vSlirpGuestError, + .clock_get_ns = llSlirp_ClockGetNanoSeconds, + .timer_new = pvSlirp_TimerNew, + .timer_free = vSlirp_TimerFree, + .timer_mod = vSlirp_TimerModify, + .register_poll_fd = vSlirp_RegisterPollFd, + .unregister_poll_fd = vSlirp_UnRegisterPollFd, + .notify = vSlirp_Notify, + + #if SLIRP_CHECK_VERSION( 4U, 7U, 0U ) + .init_completed = vSlirp_InitCompleted, + .timer_new_opaque = pvSlirpTimerNewOpaque, + #endif + }; + + static struct SlirpConfig xSlirpConfig = { 0U }; + + #if SLIRP_CHECK_VERSION( 4U, 7U, 0U ) + xSlirpConfig.version = 4U; + #else + xSlirpConfig.version = 3U; + #endif + + xSlirpConfig.restricted = false; + + /* IPv4 Enabled */ + xSlirpConfig.in_enabled = true; + xSlirpConfig.vnetwork.s_addr = FreeRTOS_inet_addr_quick( 10U, 0U, 2U, 0U ); + xSlirpConfig.vnetmask.s_addr = FreeRTOS_inet_addr_quick( 255U, 255U, 255U, 0U ); + xSlirpConfig.vhost.s_addr = FreeRTOS_inet_addr_quick( 10, 0U, 2U, 2U ); + + /* IPv6 disabled */ + xSlirpConfig.in6_enabled = false; + + xSlirpConfig.vhostname = NULL; + xSlirpConfig.tftp_server_name = NULL; + xSlirpConfig.tftp_path = NULL; + xSlirpConfig.bootfile = NULL; + + xSlirpConfig.vdhcp_start.s_addr = FreeRTOS_inet_addr_quick( 10U, 0U, 2U, 15U ); + xSlirpConfig.vnameserver.s_addr = FreeRTOS_inet_addr_quick( 10U, 0U, 2U, 3U ); + xSlirpConfig.vdnssearch = NULL; + xSlirpConfig.vdomainname = NULL; + + xSlirpConfig.if_mtu = IF_MTU_DEFAULT; + xSlirpConfig.if_mru = IF_MRU_DEFAULT; + + xSlirpConfig.disable_host_loopback = false; + xSlirpConfig.enable_emu = false; + xSlirpConfig.disable_dns = false; + + #if SLIRP_CHECK_VERSION( 4U, 7U, 0U ) + xSlirpConfig.disable_dhcp = false; + #endif /* SLIRP_CHECK_VERSION( 4U, 7U, 0U ) */ + + if( ( pxSendMsgBuffer == NULL ) || + ( pxRecvMsgBuffer == NULL ) || + ( pvSendEvent == NULL ) || + ( ppvBackendContext == NULL ) ) + { + fprintf( stderr, "NULL parameter passed to vMBuffNetifBackendInit.\n" ); + } + else + { + pvContextBuffer = pvPortMalloc( sizeof( SlirpBackendContext_t ) ); + } + + if( pvContextBuffer != NULL ) + { + pxCtx = ( SlirpBackendContext_t * ) pvContextBuffer; + + pxCtx->xSendMsgBuffer = xMessageBufferCreateStatic( xSEND_BUFFER_SIZE, + pxCtx->pucTxBuffer, + &( pxCtx->xSendMsgBufferStatic ) ); + + if( pxCtx->xSendMsgBuffer == NULL ) + { + xErrorFlag = pdTRUE; + } + + pxCtx->xRecvMsgBuffer = xMessageBufferCreateStatic( xSEND_BUFFER_SIZE, + pxCtx->pucRxBuffer, + &( pxCtx->xRecvMsgBufferStatic ) ); + + if( pxCtx->xRecvMsgBuffer == NULL ) + { + xErrorFlag = pdTRUE; + } + + /* Copy pointer to event struct */ + pxCtx->pvSendEvent = pvSendEvent; + + /* Initialize libslirp */ + pxCtx->pxSlirp = slirp_new( &xSlirpConfig, &xSlirpCallbacks, pvContextBuffer ); + + if( pxCtx->pxSlirp ) + { + #if defined( _WIN32 ) + pxCtx->xMutex = CreateMutex( NULL, FALSE, NULL ); + configASSERT( pxCtx->xMutex != ( Mutex_t ) NULL ); + + pxCtx->xTxThread = _beginthreadex( NULL, 0, vTransmitThread, pvContextBuffer, 0, NULL ); + configASSERT( pxCtx->xTxThread != ( Thread_t ) NULL ); + + pxCtx->xRxThread = _beginthreadex( NULL, 0, vReceiveThread, pvContextBuffer, 0, NULL ); + configASSERT( pxCtx->xRxThread != ( Thread_t ) NULL ); + + ( void ) QueryPerformanceFrequency( &xClockFrequency ); + #else /* if defined( _WIN32 ) */ + int lRslt; + lRslt = pthread_mutex_init( &( pxCtx->xMutex ), NULL ); + configASSERT( lRslt == 0U ); + + lRslt = pthread_create( &( pxCtx->xTxThread ), NULL, vTransmitThread, pvContextBuffer ); + configASSERT( lRslt == 0U ); + + lRslt = pthread_create( &( pxCtx->xRxThread ), NULL, vReceiveThread, pvContextBuffer ); + configASSERT( lRslt == 0U ); + #endif /* if defined( _WIN32 ) */ + } + } + + /* vTaskDelay(2 * 1000); */ + + if( pvContextBuffer != NULL ) + { + *pxSendMsgBuffer = pxCtx->xSendMsgBuffer; + *pxRecvMsgBuffer = pxCtx->xRecvMsgBuffer; + *ppvBackendContext = pvContextBuffer; + } +} + +/** + * @brief Lock the given SlirpBackendContext_t object. + * @param [in] pxCtx Pointer to the relevant SlirpBackendContext_t. + */ +static inline void vLockSlirpContext( SlirpBackendContext_t * pxCtx ) +{ + int lRslt; + + configASSERT( pxCtx != NULL ); + + #if defined( _WIN32 ) + lRslt = WaitForSingleObject( pxCtx->xMutex, INFINITE ); + configASSERT( lRslt == 0 ); + #else /* _WIN32 */ + lRslt = pthread_mutex_lock( &( pxCtx->xMutex ) ); + configASSERT( lRslt == 0 ); + #endif /* _WIN32 */ +} + +/** + * @brief Unlock the given SlirpBackendContext_t object. + * @param [in] pxCtx Pointer to the relevant SlirpBackendContext_t. + */ +static inline void vUnlockSlirpContext( SlirpBackendContext_t * pxCtx ) +{ + int lRslt; + + #if defined( _WIN32 ) + lRslt = ( int ) ReleaseMutex( pxCtx->xMutex ); + configASSERT( lRslt != 0 ); + #else /* _WIN32 */ + lRslt = pthread_mutex_unlock( &( pxCtx->xMutex ) ); + configASSERT( lRslt == 0 ); + #endif /* _WIN32 */ +} + +/** + * @brief Deinitialize function for the slirp backend driver. + * + * @param [in,out] pvBackendContext Context to deinitialize / free. + */ +void vMBuffNetifBackendDeInit( void * pvBackendContext ) +{ + SlirpBackendContext_t * pxCtx = NULL; + + if( pvBackendContext != NULL ) + { + pxCtx = ( SlirpBackendContext_t * ) pvBackendContext; + + pxCtx->xExitFlag = pdTRUE; + + #if defined( _WIN32 ) + ( void ) WaitForSingleObject( ( HANDLE ) pxCtx->xTxThread, INFINITE ); + ( void ) WaitForSingleObject( ( HANDLE ) pxCtx->xRxThread, INFINITE ); + #else + pthread_join( pxCtx->xTxThread, NULL ); + pthread_join( pxCtx->xRxThread, NULL ); + #endif + + vLockSlirpContext( pxCtx ); + + #if defined( _WIN32 ) + ( void ) CloseHandle( pxCtx->xMutex ); + #else + ( void ) pthread_mutex_destroy( &( pxCtx->xMutex ) ); + #endif + + slirp_cleanup( pxCtx->pxSlirp ); + + vMessageBufferDelete( pxCtx->xSendMsgBuffer ); + vMessageBufferDelete( pxCtx->xRecvMsgBuffer ); + + free( ( void * ) ( pxCtx->pxPollFdArray ) ); + free( ( void * ) pxCtx ); + pxCtx = NULL; + } +} + +/** + * @brief Callback called by libslirp when an incomming frame is available. + * + * @param [in] pvBuffer Pointer to a buffer containing the incoming frame. + * @param [in] uxLen Length of the incoming frame. + * @param [in] pvOpaque Opaque context pointer ( points to a SlirpBackendContext_t ). + * @return slirp_ssize_t 0U Always. + */ +static slirp_ssize_t xSlirp_WriteCallback( const void * pvBuffer, + size_t uxLen, + void * pvOpaque ) +{ + SlirpBackendContext_t * pxCtx = ( SlirpBackendContext_t * ) pvOpaque; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if( uxLen > ( NETWORK_BUFFER_LEN ) ) + { + fprintf( stderr, "Dropping RX frame of length: %zu > %zu. Frame received from libslirp is too large.\n", uxLen, ( size_t ) NETWORK_BUFFER_LEN ); + } + else if( uxLen < sizeof( EthernetHeader_t ) ) + { + fprintf( stderr, "Dropping RX frame of length: %zu < %zu. Frame received from libslirp is too small.\n", uxLen, sizeof( EthernetHeader_t ) ); + } + else if( xMessageBufferSpacesAvailable( pxCtx->xRecvMsgBuffer ) < ( uxLen + 4U ) ) + { + fprintf( stderr, "Dropping RX frame of length: %zu. xRecvMsgBuffer is full\n", uxLen ); + } + else + { + size_t uxBytesSent; + + uxBytesSent = xMessageBufferSendFromISR( pxCtx->xRecvMsgBuffer, + pvBuffer, + uxLen, + &xHigherPriorityTaskWoken ); + + configASSERT( uxBytesSent == uxLen ); + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + } + + return 0U; +} + + +/** + * @brief Checks that pxPollFdArray is large enough to accomodate the specified number of file descriptors. + * + * @param [in] pxCtx Pointer to the relevant SlirpBackendContext_t. + * @param [in] xDesiredSize Desired number of file descriptors to store. + */ +static void vEnsurePollfdSize( SlirpBackendContext_t * pxCtx, + size_t xDesiredSize ) +{ + configASSERT( pxCtx != NULL ); + + if( pxCtx->xPollFdArraySize < xDesiredSize ) + { + size_t xNewSize; + + if( pxCtx->xPollFdArraySize > 0 ) + { + xNewSize = 2U * pxCtx->xPollFdArraySize; + } + else + { + xNewSize = 10U; + } + + if( xDesiredSize > xNewSize ) + { + xNewSize = xDesiredSize; + } + + if( pxCtx->pxPollFdArray == NULL ) + { + pxCtx->pxPollFdArray = ( struct pollfd * ) malloc( xNewSize * sizeof( struct pollfd ) ); + } + else + { + pxCtx->pxPollFdArray = ( struct pollfd * ) realloc( pxCtx->pxPollFdArray, xNewSize * sizeof( struct pollfd ) ); + } + + configASSERT( pxCtx->pxPollFdArray != NULL ); + + pxCtx->xPollFdArraySize = xNewSize; + } +} + +/** + * @brief Convert from a libslirp poll event flag to a posix poll flag. + * @param [in] lSlirpPollFlags libslirp poll event flags to be converted. + * @return The equivalent posix poll events. + */ +static inline int lSlirpEventsToNativePollEvents( int lSlirpPollFlags ) +{ + int lPosixPollFlags = 0U; + + if( lSlirpPollFlags & SLIRP_POLL_IN ) + { + lPosixPollFlags |= POLLIN; + } + + if( lSlirpPollFlags & SLIRP_POLL_OUT ) + { + lPosixPollFlags |= POLLOUT; + } + + if( lSlirpPollFlags & SLIRP_POLL_PRI ) + { + lPosixPollFlags |= POLLPRI; + } + + if( lSlirpPollFlags & SLIRP_POLL_ERR ) + { + lPosixPollFlags |= POLLERR; + } + + if( lSlirpPollFlags & SLIRP_POLL_HUP ) + { + lPosixPollFlags |= POLLHUP; + } + + return lPosixPollFlags; +} + +/** + * @brief Convert from posix poll event flags to libslirp poll event flags. + * @param [in] lPosixPollFlags Posix poll event flags to be converted. + * @return The equivalent libslirp poll events. + */ +static inline int lNativePollEventsToSlirpEvents( int lPosixPollFlags ) +{ + int lSlirpPollFlags = 0U; + + if( lPosixPollFlags & POLLIN ) + { + lSlirpPollFlags |= SLIRP_POLL_IN; + } + + if( lPosixPollFlags & POLLOUT ) + { + lSlirpPollFlags |= SLIRP_POLL_OUT; + } + + if( lPosixPollFlags & POLLPRI ) + { + lSlirpPollFlags |= SLIRP_POLL_PRI; + } + + if( lPosixPollFlags & POLLERR ) + { + lSlirpPollFlags |= SLIRP_POLL_ERR; + } + + if( lPosixPollFlags & POLLHUP ) + { + lSlirpPollFlags |= SLIRP_POLL_HUP; + } + + return lSlirpPollFlags; +} + +/** + * @brief SlirpAddPollCb implementation passed to libslirp during initialization. + * @param [in] fd File descriptor to add to the polling list. + * @param [in] lSlirpFlags Flags to be placed in the relevant events field. + * @param [in] pvOpaque Opaque pointer to the relevant SlirpBackendContext_t. + */ +static int lSlirpAddPollCallback( int lFd, + int lSlirpFlags, + void * pvOpaque ) +{ + SlirpBackendContext_t * pxCtx = ( SlirpBackendContext_t * ) pvOpaque; + + configASSERT( pxCtx != NULL ); + configASSERT( pxCtx->nfds < INT_MAX ); + + vEnsurePollfdSize( pxCtx, pxCtx->nfds + 1U ); + + pxCtx->pxPollFdArray[ pxCtx->nfds ].fd = lFd; + pxCtx->pxPollFdArray[ pxCtx->nfds ].events = lSlirpEventsToNativePollEvents( lSlirpFlags ); + pxCtx->pxPollFdArray[ pxCtx->nfds ].revents = 0U; + + pxCtx->nfds++; + + return ( int ) ( pxCtx->nfds - 1U ); +} + +/** + * @brief SlirpGetREventsCb implementation passed to libslirp during initialization. + * @param [in] lIdx Index returned by lSlirpAddPollCallback for the given file descriptor. + * @param [in] pvOpaque Opaque pointer to the relevant SlirpBackendContext_t. + * @return An integer with the relevant libslirp polling flags set. + */ +static int lSlirpGetREventsCallback( int lIdx, + void * pvOpaque ) +{ + SlirpBackendContext_t * pxCtx = ( SlirpBackendContext_t * ) pvOpaque; + int rEvents = 0U; + nfds_t xIndex; + + configASSERT( pxCtx ); + + configASSERT( lIdx >= 0 ); + + xIndex = ( nfds_t ) lIdx; + configASSERT( xIndex < pxCtx->nfds ); + configASSERT( xIndex < pxCtx->xPollFdArraySize ); + + rEvents = ( pxCtx->pxPollFdArray[ xIndex ].revents ); + + return lNativePollEventsToSlirpEvents( rEvents ); +} + +/** + * @brief Posix thread implementation which reads from xSendMsgBuffer and passes outgoing frames to libslirp. + * @param [in] pvParameters Opaque pointer to the relevant SlirpBackendContext_t. + * @return NULL + */ +static THREAD_RETURN THREAD_FUNC_DEF vTransmitThread( void * pvParameters ) +{ + SlirpBackendContext_t * pxCtx = ( SlirpBackendContext_t * ) pvParameters; + const time_t xMaxMSToWait = 1000; + uint8_t ucFrameSendBuffer[ NETWORK_BUFFER_LEN ]; + + #if !defined( _WIN32 ) + sigset_t set; + + /* + * disable signals to avoid treating this thread as a FreeRTOS task and putting + * it to sleep by the scheduler + */ + sigfillset( &set ); + pthread_sigmask( SIG_SETMASK, &set, NULL ); + #endif /* !defined(_WIN32) */ + + configASSERT( pxCtx != NULL ); + + while( pxCtx->xExitFlag == pdFALSE ) + { + /* Wait until notified of something to send. */ + #if defined( _WIN32 ) + ( void ) WaitForSingleObject( pxCtx->pvSendEvent, ( DWORD ) xMaxMSToWait ); + #else + event_wait_timed( pxCtx->pvSendEvent, xMaxMSToWait ); + #endif + + while( xMessageBufferIsEmpty( pxCtx->xSendMsgBuffer ) == pdFALSE ) + { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + size_t uxFrameLen = xMessageBufferReceiveFromISR( pxCtx->xSendMsgBuffer, ucFrameSendBuffer, sizeof( ucFrameSendBuffer ), &xHigherPriorityTaskWoken ); + + vLockSlirpContext( pxCtx ); + { + slirp_input( pxCtx->pxSlirp, ucFrameSendBuffer, uxFrameLen ); + } + vUnlockSlirpContext( pxCtx ); + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + } + } + + return ( THREAD_RETURN ) NULL; +} + +/** + * @brief Posix thread implementation which polls file descriptors used by libslirp and forwards + * incoming frames to xRecvMsgBuffer indirectly by calling xSlirp_WriteCallback. + * @param [in] pvParameters Opaque pointer to the relevant SlirpBackendContext_t. + * @return NULL + */ +static THREAD_RETURN THREAD_FUNC_DEF vReceiveThread( void * pvParameters ) +{ + SlirpBackendContext_t * pxCtx = ( SlirpBackendContext_t * ) pvParameters; + + #if !defined( _WIN32 ) + sigset_t set; + + /* + * disable signals to avoid treating this thread as a FreeRTOS task and putting + * it to sleep by the scheduler + */ + sigfillset( &set ); + pthread_sigmask( SIG_SETMASK, &set, NULL ); + #endif /* !defined(_WIN32) */ + + configASSERT( pxCtx != NULL ); + + while( pxCtx->xExitFlag == pdFALSE ) + { + int lPollRslt; + + uint32_t ulPollerTimeoutMs = 100 * 1000U; + + vLockSlirpContext( pxCtx ); + { + pxCtx->nfds = 0; + slirp_pollfds_fill( pxCtx->pxSlirp, &ulPollerTimeoutMs, lSlirpAddPollCallback, pxCtx ); + } + vUnlockSlirpContext( pxCtx ); + + errno = 0; + #if defined( _WIN32 ) + lPollRslt = WSAPoll( pxCtx->pxPollFdArray, pxCtx->nfds, ( int ) ulPollerTimeoutMs ); + #else /* _WIN32 */ + lPollRslt = poll( pxCtx->pxPollFdArray, pxCtx->nfds, ulPollerTimeoutMs ); + #endif /* _WIN32 */ + + if( lPollRslt > 0 ) + { + lPollRslt = 0; + } + + vLockSlirpContext( pxCtx ); + { + slirp_pollfds_poll( pxCtx->pxSlirp, lPollRslt, lSlirpGetREventsCallback, ( void * ) pxCtx ); + } + vUnlockSlirpContext( pxCtx ); + } + + return ( THREAD_RETURN ) NULL; +} + +#if defined( _WIN32 ) + +/** + * @brief Callback function passed to libslirp to get the current time in nanoseconds. + * + * @param [in] pvOpaque Opaque context pointer (unused). + * @return int64_t Current time in nanoseconds. + */ + static int64_t llSlirp_ClockGetNanoSeconds( void * pvOpaque ) + { + LARGE_INTEGER xTime; + int64_t lTime; + + ( void ) pvOpaque; + + QueryPerformanceCounter( &xTime ); + + lTime = ( xTime.QuadPart * 1000000000 / xClockFrequency.QuadPart ); + + return lTime; + } +#else /* if defined( _WIN32 ) */ + +/** + * @brief Callback function passed to libslirp to get the current time in nanoseconds. + * + * @param [in] pvOpaque Opaque context pointer (unused). + * @return int64_t Current time in nanoseconds. + */ + static int64_t llSlirp_ClockGetNanoSeconds( void * pvOpaque ) + { + struct timespec ts; + int64_t llTimeNs = 0; + + clock_gettime( CLOCK_MONOTONIC, &ts ); + + ( void ) pvOpaque; + + llTimeNs = ( ts.tv_sec * 1000000000LL ) + ts.tv_nsec; + return llTimeNs; + } +#endif /* if defined( _WIN32 ) */ + +/** + * @brief Callback funciton passed to libslirp to report a runtime error. + * + * @param [in] msg Error message + * @param pvOpaque Opaque context pointer (unused). + */ +static void vSlirpGuestError( const char * msg, + void * pvOpaque ) +{ + fprintf( stderr, "libslirp guest error: %s\n", msg ); + exit( 1 ); +} + +/** + * @brief Stub callback function for libslirp timer API. + * + * @param cb Unused. + * @param pvCallbackContext Unused. + * @param pvOpaque Unused. + * @return void* NULL + */ +static void * pvSlirp_TimerNew( SlirpTimerCb cb, + void * pvCallbackContext, + void * pvOpaque ) +{ + /* Stub */ + ( void ) cb; + ( void ) pvCallbackContext; + ( void ) pvOpaque; + configASSERT( 0 ); + return NULL; +} + +/** + * @brief Stub callback function for libslirp timer API. + * + * @param pvTimer Unused. + * @param pvOpaque Unused. + */ +static void vSlirp_TimerFree( void * pvTimer, + void * pvOpaque ) +{ + /* Stub */ + ( void ) pvTimer; + ( void ) pvOpaque; + configASSERT( 0 ); +} + +/** + * @brief Stub callback function for libslirp timer API. + * + * @param pvTimer Unused. + * @param expire_time Unused. + * @param pvOpaque Unused. + */ +static void vSlirp_TimerModify( void * pvTimer, + int64_t expire_time, + void * pvOpaque ) +{ + /* Stub */ + ( void ) pvTimer; + ( void ) expire_time; + ( void ) pvOpaque; + configASSERT( 0 ); +} + +#if SLIRP_CHECK_VERSION( 4U, 7U, 0U ) + +/** + * @brief Stub callback function for libslirp timer API. + * + * @param xTimerId Unused. + * @param cb_opaque Unused. + * @param pvOpaque Unused. + * @return void* NULL + */ + static void * pvSlirpTimerNewOpaque( SlirpTimerId xTimerId, + void * cb_opaque, + void * pvOpaque ) + { + /* Stub */ + ( void ) xTimerId; + ( void ) cb_opaque; + ( void ) pvOpaque; + configASSERT( 0 ); + return NULL; + } +#endif /* SLIRP_CHECK_VERSION( 4U, 7U, 0U ) */ + +/** + * @brief Called by libslipr when a new file descriptor / socket is opened. + * + * @param lFd File descriptor to watch. + * @param pvOpaque Pointer to driver context. + */ +static void vSlirp_RegisterPollFd( int lFd, + void * pvOpaque ) +{ + SlirpBackendContext_t * pxCtx = ( SlirpBackendContext_t * ) pvOpaque; + + configASSERT( pxCtx != NULL ); + + ( void ) lFd; + + vEnsurePollfdSize( pxCtx, pxCtx->nfds + 1 ); +} + +/** + * @brief Stub callback function. + * + * @param lFd Unused. + * @param pvOpaque Unused. + */ +static void vSlirp_UnRegisterPollFd( int lFd, + void * pvOpaque ) +{ + ( void ) lFd; + ( void ) pvOpaque; +} + +/** + * @brief Stub callback function. + * + * @param pvOpaque Unused. + */ +static void vSlirp_Notify( void * pvOpaque ) +{ + /* Stub */ + ( void ) pvOpaque; +} + +#if SLIRP_CHECK_VERSION( 4U, 7U, 0U ) + +/** + * @brief Stub callback function. + * + * @param pxSlirp Unused. + * @param pvOpaque Unused. + */ + static void vSlirp_InitCompleted( Slirp * pxSlirp, + void * pvOpaque ) + { + /* Stub */ + ( void ) pxSlirp; + ( void ) pvOpaque; + } +#endif /* SLIRP_CHECK_VERSION( 4U, 7U, 0U ) */ diff --git a/FreeRTOS/source/portable/NetworkInterface/libslirp/MBuffNetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/libslirp/MBuffNetworkInterface.c new file mode 100644 index 0000000..00cabc9 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/libslirp/MBuffNetworkInterface.c @@ -0,0 +1,337 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "list.h" +#include "task.h" +#include "message_buffer.h" + +#if !defined( _WIN32 ) + #include "wait_for_event.h" +#endif + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" + +/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet + * driver will filter incoming packets and only pass the stack those packets it + * considers need processing. */ +#if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +#if ipconfigNETWORK_MTU < 1500U + #error ipconfigNETWORK_MTU must be at least 1500 +#endif + +#define NETWORK_BUFFER_LEN ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) + +#define xSEND_BUFFER_SIZE ( 32U * NETWORK_BUFFER_LEN ) +#define xRECV_BUFFER_SIZE ( 32U * NETWORK_BUFFER_LEN ) + +typedef struct +{ + BaseType_t xInterfaceState; + MessageBufferHandle_t xSendMsgBuffer; + MessageBufferHandle_t xRecvMsgBuffer; + TaskHandle_t xRecvTask; + void * pvSendEvent; + void * pvBackendContext; +} MBuffNetDriverContext_t; + +extern void vMBuffNetifBackendInit( MessageBufferHandle_t * pxSendMsgBuffer, + MessageBufferHandle_t * pxRecvMsgBuffer, + void * pvSendEvent, + void ** ppvBackendContext ); + +extern void vMBuffNetifBackendDeInit( void * pvBackendContext ); + +static void vNetifReceiveTask( void * pvParameters ); + +MBuffNetDriverContext_t xDriverCtx = { 0 }; + +/** + * @brief Initialize the MessageBuffer backed network interface. + * + * @return BaseType_t pdTRUE on success + */ +BaseType_t xNetworkInterfaceInitialise( void ) +{ + BaseType_t xResult = pdTRUE; + + if( xDriverCtx.xInterfaceState == pdFALSE ) + { + xDriverCtx.xInterfaceState = pdFALSE; + + #if defined( _WIN32 ) + xDriverCtx.pvSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL ); + #else + xDriverCtx.pvSendEvent = ( void * ) event_create(); + #endif + + if( xDriverCtx.pvSendEvent == NULL ) + { + xResult = pdFALSE; + } + else + { + vMBuffNetifBackendInit( &( xDriverCtx.xSendMsgBuffer ), + &( xDriverCtx.xRecvMsgBuffer ), + xDriverCtx.pvSendEvent, + &( xDriverCtx.pvBackendContext ) ); + + if( xDriverCtx.pvBackendContext == NULL ) + { + xResult = pdFALSE; + #if defined( _WIN32 ) + ( void ) CloseHandle( xDriverCtx.pvSendEvent ); + #else + ( void ) event_delete( xDriverCtx.pvSendEvent ); + #endif + } + } + + if( xResult == pdTRUE ) + { + xResult = xTaskCreate( vNetifReceiveTask, "NetRX", + configMINIMAL_STACK_SIZE, NULL, + tskIDLE_PRIORITY, + &( xDriverCtx.xRecvTask ) ); + } + + /* Cleanup on failure */ + if( xResult != pdTRUE ) + { + if( xDriverCtx.pvSendEvent != NULL ) + { + #if defined( _WIN32 ) + ( void ) CloseHandle( xDriverCtx.pvSendEvent ); + #else + event_delete( xDriverCtx.pvSendEvent ); + #endif + } + + if( xDriverCtx.pvBackendContext != NULL ) + { + vMBuffNetifBackendDeInit( xDriverCtx.pvBackendContext ); + } + } + + xDriverCtx.xInterfaceState = xResult; + } + + return xResult; +} + +/** + * @brief Deinitialize the message buffer backed network interface. + * + * @return BaseType_t pdTRUE + */ +BaseType_t xNetworkInterfaceDeInitialise( void ) +{ + #if defined( _WIN32 ) + ( void ) CloseHandle( xDriverCtx.pvSendEvent ); + #else + event_delete( xDriverCtx.pvSendEvent ); + #endif + + vTaskDelete( xDriverCtx.xRecvTask ); + + return pdTRUE; +} + +/*! + * @brief FreeRTOS task which reads from xRecvMsgBuffer and passes new frames to FreeRTOS+TCP. + * @param [in] pvParameters not used + */ +static void vNetifReceiveTask( void * pvParameters ) +{ + NetworkBufferDescriptor_t * pxDescriptor = NULL; + + ( void ) pvParameters; + + for( ; ; ) + { + size_t uxMessageLen; + + while( pxDescriptor == NULL ) + { + /* Wait for an MTU + header sized buffer */ + pxDescriptor = pxGetNetworkBufferWithDescriptor( NETWORK_BUFFER_LEN, portMAX_DELAY ); + configASSERT( pxDescriptor->xDataLength >= NETWORK_BUFFER_LEN ); + } + + /* Read an incoming frame */ + uxMessageLen = xMessageBufferReceive( xDriverCtx.xRecvMsgBuffer, + pxDescriptor->pucEthernetBuffer, + pxDescriptor->xDataLength, + portMAX_DELAY ); + + if( uxMessageLen > 0 ) + { + IPStackEvent_t xRxEvent; + eFrameProcessingResult_t xFrameProcess; + + pxDescriptor->xDataLength = uxMessageLen; + + /* eConsiderFrameForProcessing is interrupt safe */ + xFrameProcess = ipCONSIDER_FRAME_FOR_PROCESSING( pxDescriptor->pucEthernetBuffer ); + + if( xFrameProcess != eProcessBuffer ) + { + FreeRTOS_debug_printf( ( "Dropping RX frame of length: %lu. eConsiderFrameForProcessing returned %lu.\n", + uxMessageLen, xFrameProcess ) ); + } + + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = ( void * ) pxDescriptor; + + if( xSendEventStructToIPTask( &xRxEvent, 0U ) == pdTRUE ) + { + iptraceNETWORK_INTERFACE_RECEIVE(); + + /* Clear pxDescriptor so that the task requests a new buffer */ + pxDescriptor = NULL; + } + else + { + FreeRTOS_debug_printf( ( "Dropping TX frame of length: %lu. FreeRTOS+TCP event queue is full.\n", + pxDescriptor->xDataLength ) ); + /* Drop the frame and reuse the descriptor for the next incomming frame */ + iptraceETHERNET_RX_EVENT_LOST(); + } + } + else + { + /* + * xMessageBufferReceive returned zero. + */ + } + } +} + +/*! + * @brief API call, called from reeRTOS_IP.c to send a network packet over the + * selected interface + * @return pdTRUE if successful else pdFALSE + */ +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t xReleaseAfterSend ) +{ + BaseType_t xResult = pdFALSE; + + configASSERT( pxNetworkBuffer != NULL ); + configASSERT( pxNetworkBuffer->pucEthernetBuffer != NULL ); + configASSERT( pxNetworkBuffer->xDataLength >= sizeof( EthernetHeader_t ) ); + + if( xDriverCtx.xInterfaceState == pdTRUE ) + { + if( xMessageBufferSpacesAvailable( xDriverCtx.xSendMsgBuffer ) > pxNetworkBuffer->xDataLength + 4U ) + { + size_t uxBytesSent; + uxBytesSent = xMessageBufferSend( xDriverCtx.xSendMsgBuffer, + pxNetworkBuffer->pucEthernetBuffer, + pxNetworkBuffer->xDataLength, + 0U ); + ( void ) uxBytesSent; + configASSERT( uxBytesSent == pxNetworkBuffer->xDataLength ); + xResult = pdTRUE; + } + else + { + FreeRTOS_debug_printf( ( "Dropping TX frame of length: %lu. xSendMsgBuffer is full.\n", + pxNetworkBuffer->xDataLength ) ); + } + + iptraceNETWORK_INTERFACE_TRANSMIT(); + + if( xReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + if( xResult == pdTRUE ) + { + #if defined( _WIN32 ) + SetEvent( xDriverCtx.pvSendEvent ); + #else + event_signal( xDriverCtx.pvSendEvent ); + #endif + } + } + + return xResult; +} + +#define BUFFER_SIZE ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING ) +#define BUFFER_SIZE_ROUNDED_UP ( ( BUFFER_SIZE + 7 ) & ~0x07UL ) + +/*! + * @brief Allocate RAM for packet buffers and set the pucEthernetBuffer field for each descriptor. + * Called when the BufferAllocation1 scheme is used. + * @param [in,out] pxNetworkBuffers Pointer to an array of NetworkBufferDescriptor_t to populate. + */ +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + static uint8_t * pucNetworkPacketBuffers = NULL; + size_t uxIndex; + + if( pucNetworkPacketBuffers == NULL ) + { + pucNetworkPacketBuffers = ( uint8_t * ) malloc( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * BUFFER_SIZE_ROUNDED_UP ); + } + + if( pucNetworkPacketBuffers == NULL ) + { + FreeRTOS_printf( ( "Failed to allocate memory for pxNetworkBuffers" ) ); + configASSERT( 0 ); + } + else + { + for( uxIndex = 0; uxIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; uxIndex++ ) + { + size_t uxOffset = uxIndex * BUFFER_SIZE_ROUNDED_UP; + NetworkBufferDescriptor_t ** ppDescriptor; + + /* At the beginning of each pbuff is a pointer to the relevant descriptor */ + ppDescriptor = ( NetworkBufferDescriptor_t ** ) &( pucNetworkPacketBuffers[ uxOffset ] ); + + /* Set this pointer to the address of the correct descriptor */ + *ppDescriptor = &( pxNetworkBuffers[ uxIndex ] ); + + /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the + * beginning of the allocated buffer. */ + pxNetworkBuffers[ uxIndex ].pucEthernetBuffer = &( pucNetworkPacketBuffers[ uxOffset + ipBUFFER_PADDING ] ); + } + } +} diff --git a/FreeRTOS/source/portable/NetworkInterface/linux/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/linux/NetworkInterface.c new file mode 100644 index 0000000..f04ca13 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/linux/NetworkInterface.c @@ -0,0 +1,934 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* ========================= FreeRTOS includes ============================== */ +#include "FreeRTOS.h" +#include "event_groups.h" +#include "task.h" +#include "semphr.h" + +/* ========================= FreeRTOS+TCP includes ========================== */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_Stream_Buffer.h" + +/* ======================== Standard Library includes ======================== */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ========================== Local includes =================================*/ +#include + +/* ======================== Macro Definitions =============================== */ +#if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) \ + eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +/* ============================== Definitions =============================== */ +#define xSEND_BUFFER_SIZE 32768 +#define xRECV_BUFFER_SIZE 32768 +#define MAX_CAPTURE_LEN 65535 +#define IP_SIZE 100 + +/* ================== Static Function Prototypes ============================ */ +static int prvConfigureCaptureBehaviour( void ); +static int prvCreateThreadSafeBuffers( void ); +static void * prvLinuxPcapSendThread( void * pvParam ); +static void * prvLinuxPcapRecvThread( void * pvParam ); +static void prvInterruptSimulatorTask( void * pvParameters ); +static void prvPrintAvailableNetworkInterfaces( pcap_if_t * pxAllNetworkInterfaces ); +static pcap_if_t * prvGetAvailableNetworkInterfaces( void ); +static const char * prvRemoveSpaces( char * pcBuffer, + int aBuflen, + const char * pcMessage ); +static int prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces ); +static int prvCreateWorkerThreads( void ); +static int prvSetDeviceModes( void ); +static void print_hex( unsigned const char * const bin_data, + size_t len ); + +/* ======================== Static Global Variables ========================= */ +static StreamBuffer_t * xSendBuffer = NULL; +static StreamBuffer_t * xRecvBuffer = NULL; +static char errbuf[ PCAP_ERRBUF_SIZE ]; +static pcap_t * pxOpenedInterfaceHandle = NULL; +static struct event * pvSendEvent = NULL; +static uint32_t ulPCAPSendFailures = 0; +static BaseType_t xConfigNetworkInterfaceToUse = configNETWORK_INTERFACE_TO_USE; +static BaseType_t xInvalidInterfaceDetected = pdFALSE; + +/* ======================= API Function definitions ========================= */ + +/*! + * @brief API call, called from reeRTOS_IP.c to initialize the capture device + * to be able to send and receive packets + * @return pdPASS if successful else pdFAIL + */ +BaseType_t xNetworkInterfaceInitialise( void ) +{ + BaseType_t ret = pdFAIL; + pcap_if_t * pxAllNetworkInterfaces; + + /* Query the computer the simulation is being executed on to find the + * network interfaces it has installed. */ + pxAllNetworkInterfaces = prvGetAvailableNetworkInterfaces(); + + if( pxAllNetworkInterfaces != NULL ) + { + prvPrintAvailableNetworkInterfaces( pxAllNetworkInterfaces ); + ret = prvOpenSelectedNetworkInterface( pxAllNetworkInterfaces ); + + if( ret == pdPASS ) + { + ret = prvCreateThreadSafeBuffers(); + + if( ret == pdPASS ) + { + ret = prvCreateWorkerThreads(); + } + } + + /* The device list is no longer required. */ + pcap_freealldevs( pxAllNetworkInterfaces ); + } + + if( ( pxOpenedInterfaceHandle != NULL ) && ( ret == pdPASS ) ) + { + ret = pdPASS; + } + + return ret; +} + +/*! + * @brief API call, called from reeRTOS_IP.c to send a network packet over the + * selected interface + * @return pdTRUE if successful else pdFALSE + */ +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t bReleaseAfterSend ) +{ + size_t xSpace; + + iptraceNETWORK_INTERFACE_TRANSMIT(); + configASSERT( xIsCallingFromIPTask() == pdTRUE ); + + /* Both the length of the data being sent and the actual data being sent + * are placed in the thread safe buffer used to pass data between the FreeRTOS + * tasks and the pthread that sends data via the pcap library. Drop + * the packet if there is insufficient space in the buffer to hold both. */ + xSpace = uxStreamBufferGetSpace( xSendBuffer ); + + if( ( pxNetworkBuffer->xDataLength <= + ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) && + ( xSpace >= ( pxNetworkBuffer->xDataLength + + sizeof( pxNetworkBuffer->xDataLength ) ) ) ) + { + /* First write in the length of the data, then write in the data + * itself. */ + uxStreamBufferAdd( xSendBuffer, + 0, + ( const uint8_t * ) &( pxNetworkBuffer->xDataLength ), + sizeof( pxNetworkBuffer->xDataLength ) ); + uxStreamBufferAdd( xSendBuffer, + 0, + ( const uint8_t * ) pxNetworkBuffer->pucEthernetBuffer, + pxNetworkBuffer->xDataLength ); + } + else + { + FreeRTOS_printf( ( "xNetworkInterfaceOutput: send buffers full to store %lu\n", + pxNetworkBuffer->xDataLength ) ); + } + + /* Kick the Tx task in either case in case it doesn't know the buffer is + * full. */ + event_signal( pvSendEvent ); + + /* The buffer has been sent so can be released. */ + if( bReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + return pdPASS; +} + +/* ====================== Static Function definitions ======================= */ + +/*! + * @brief create thread safe buffers to send/receive packets between threads + * @returns + */ +static int prvCreateThreadSafeBuffers( void ) +{ + int ret = pdFAIL; + + /* The buffer used to pass data to be transmitted from a FreeRTOS task to + * the linux thread that sends via the pcap library. */ + do + { + if( xSendBuffer == NULL ) + { + xSendBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) + xSEND_BUFFER_SIZE + 1 ); + + if( xSendBuffer == NULL ) + { + break; + } + + configASSERT( xSendBuffer ); + memset( xSendBuffer, '\0', sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) ); + xSendBuffer->LENGTH = xSEND_BUFFER_SIZE + 1; + } + + /* The buffer used to pass received data from the pthread that receives + * via the pcap library to the FreeRTOS task. */ + if( xRecvBuffer == NULL ) + { + xRecvBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) + xRECV_BUFFER_SIZE + 1 ); + + if( xRecvBuffer == NULL ) + { + break; + } + + configASSERT( xRecvBuffer ); + memset( xRecvBuffer, '\0', sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) ); + xRecvBuffer->LENGTH = xRECV_BUFFER_SIZE + 1; + } + + ret = pdPASS; + } while( 0 ); + + return ret; +} + +/*! + * @brief print network interfaces available on the system + * @param[in] pxAllNetworkInterfaces interface structure list to print + */ +static void prvPrintAvailableNetworkInterfaces( pcap_if_t * pxAllNetworkInterfaces ) +{ + pcap_if_t * xInterface; + int32_t lInterfaceNumber = 1; + char cBuffer[ 512 ]; + + if( pxAllNetworkInterfaces != NULL ) + { + /* Print out the list of network interfaces. The first in the list + * is interface '1', not interface '0'. */ + for( xInterface = pxAllNetworkInterfaces; + xInterface != NULL; xInterface = xInterface->next ) + { + /* The descriptions of the devices can be full of spaces, clean them + * a little. printf() can only be used here because the network is not + * up yet - so no other network tasks will be running. */ + printf( "Interface %d - %s\n", + lInterfaceNumber, + prvRemoveSpaces( cBuffer, sizeof( cBuffer ), xInterface->name ) ); + printf( " (%s)\n", + prvRemoveSpaces( cBuffer, + sizeof( cBuffer ), + xInterface->description ? xInterface->description : + "No description" ) ); + printf( "\n" ); + lInterfaceNumber++; + } + } + + if( lInterfaceNumber == 1 ) + { + /* The interface number was never incremented, so the above for() loop + * did not execute meaning no interfaces were found. */ + printf( " \nNo network interfaces were found.\n" ); + pxAllNetworkInterfaces = NULL; + } + + printf( "\r\nThe interface that will be opened is set by " ); + printf( "\"configNETWORK_INTERFACE_TO_USE\", which\r\nshould be defined in FreeRTOSConfig.h\r\n" ); + + if( ( xConfigNetworkInterfaceToUse < 1L ) || ( xConfigNetworkInterfaceToUse >= lInterfaceNumber ) ) + { + printf( "\r\nERROR: configNETWORK_INTERFACE_TO_USE is set to %ld, which is an invalid value.\r\n", xConfigNetworkInterfaceToUse ); + printf( "Please set configNETWORK_INTERFACE_TO_USE to one of the interface numbers listed above,\r\n" ); + printf( "then re-compile and re-start the application. Only Ethernet (as opposed to WiFi)\r\n" ); + printf( "interfaces are supported.\r\n\r\nHALTING\r\n\r\n\r\n" ); + xInvalidInterfaceDetected = pdTRUE; + + if( pxAllNetworkInterfaces != NULL ) + { + /* Free the device list, as no devices are going to be opened. */ + pcap_freealldevs( pxAllNetworkInterfaces ); + pxAllNetworkInterfaces = NULL; + } + } + else + { + printf( "Attempting to open interface number %ld.\n", xConfigNetworkInterfaceToUse ); + } +} + +/*! + * @brief get network interfaces from the system + * @returns the structure list containing all found devices + */ +static pcap_if_t * prvGetAvailableNetworkInterfaces( void ) +{ + pcap_if_t * pxAllNetworkInterfaces = NULL; + + if( xInvalidInterfaceDetected == pdFALSE ) + { + int ret; + ret = pcap_findalldevs( &pxAllNetworkInterfaces, errbuf ); + + if( ret == PCAP_ERROR ) + { + FreeRTOS_printf( ( "Could not obtain a list of network interfaces\n%s\n", + errbuf ) ); + pxAllNetworkInterfaces = NULL; + } + else + { + printf( "\r\n\r\nThe following network interfaces are available:\r\n\r\n" ); + } + } + + return pxAllNetworkInterfaces; +} + +/*! + * @brief set device operation modes + * @returns pdPASS on success pdFAIL on failure + */ +static int prvSetDeviceModes() +{ + int ret = pdFAIL; + + /* + * Open in promiscuous mode as the MAC and + * IP address is going to be "simulated", and + * not be the real MAC and IP address. This allows + * traffic to the simulated IP address to be routed + * to uIP, and traffic to the real IP address to be + * routed to the Linux TCP/IP stack. + */ + FreeRTOS_debug_printf( ( "setting device modes of operation...\n" ) ); + + do + { + ret = pcap_set_promisc( pxOpenedInterfaceHandle, 1 ); + + if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) ) + { + FreeRTOS_printf( ( "coult not activate promisuous mode\n" ) ); + break; + } + + ret = pcap_set_snaplen( pxOpenedInterfaceHandle, + ipTOTAL_ETHERNET_FRAME_SIZE ); + + if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) ) + { + FreeRTOS_printf( ( "coult not set snaplen\n" ) ); + break; + } + + ret = pcap_set_timeout( pxOpenedInterfaceHandle, 200 ); + + if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) ) + { + FreeRTOS_printf( ( "coult not set timeout\n" ) ); + break; + } + + ret = pcap_set_buffer_size( pxOpenedInterfaceHandle, + ipTOTAL_ETHERNET_FRAME_SIZE * 1100 ); + + if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) ) + { + FreeRTOS_printf( ( "coult not set buffer size\n" ) ); + break; + } + + ret = pdPASS; + } while( 0 ); + + return ret; +} + +/*! + * @brief open selected interface given its name + * @param [in] pucName interface name to pen + * @returns pdPASS on success pdFAIL on failure + */ +static int prvOpenInterface( const char * pucName ) +{ + static char pucInterfaceName[ 256 ]; + int ret = pdFAIL; + + if( pucName != NULL ) + { + ( void ) strncpy( pucInterfaceName, pucName, sizeof( pucInterfaceName ) ); + pucInterfaceName[ sizeof( pucInterfaceName ) - ( size_t ) 1 ] = '\0'; + + FreeRTOS_debug_printf( ( "opening interface %s\n", pucInterfaceName ) ); + + pxOpenedInterfaceHandle = pcap_create( pucInterfaceName, errbuf ); + + if( pxOpenedInterfaceHandle != NULL ) + { + ret = prvSetDeviceModes(); + + if( ret == pdPASS ) + { + if( pcap_activate( pxOpenedInterfaceHandle ) == 0 ) + { + /* Configure the capture filter to allow blocking reads, and to filter + * out packets that are not of interest to this demo. */ + ret = prvConfigureCaptureBehaviour(); + } + else + { + FreeRTOS_debug_printf( ( "pcap activate error %s\n", + pcap_geterr( pxOpenedInterfaceHandle ) ) ); + ret = pdFAIL; + } + } + } + else + { + FreeRTOS_printf( ( "\n%s is not supported by pcap and cannot be opened %s\n", + pucInterfaceName, errbuf ) ); + } + } + else + { + FreeRTOS_printf( ( "could not open interface: name is null\n" ) ); + } + + return ret; +} + +/*! + * @brief Open the network interface. The number of the interface to be opened is + * set by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h + * Calling this function will set the pxOpenedInterfaceHandle variable + * If, after calling this function, pxOpenedInterfaceHandle + * is equal to NULL, then the interface could not be opened. + * @param [in] pxAllNetworkInterfaces network interface list to choose from + * @returns pdPASS on success or pdFAIL when something goes wrong + */ +static int prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces ) +{ + pcap_if_t * pxInterface; + int32_t x; + int ret = pdFAIL; + + /* Walk the list of devices until the selected device is located. */ + pxInterface = pxAllNetworkInterfaces; + + for( x = 0L; x < ( xConfigNetworkInterfaceToUse - 1L ); x++ ) + { + pxInterface = pxInterface->next; + } + + /* Open the selected interface. */ + if( prvOpenInterface( pxInterface->name ) == pdPASS ) + { + FreeRTOS_debug_printf( ( "Successfully opened interface number %d.\n", x + 1 ) ); + ret = pdPASS; + } + else + { + FreeRTOS_printf( ( "Failed to open interface number %d.\n", x + 1 ) ); + } + + return ret; +} + +/*! + * @brief launch 2 linux threads, one for Tx and one for Rx + * and one FreeRTOS thread that will simulate an interrupt + * and notify the tcp/ip stack of new data + * @return pdPASS on success otherwise pdFAIL + */ +static int prvCreateWorkerThreads( void ) +{ + pthread_t vPcapRecvThreadHandle; + pthread_t vPcapSendThreadHandle; + int ret = pdPASS; + + if( pvSendEvent == NULL ) + { + FreeRTOS_debug_printf( ( "Creating Threads ..\n" ) ); + ret = pdFAIL; + /* Create event used to signal the pcap Tx thread. */ + pvSendEvent = event_create(); + + do + { + /* Create the thread that handles pcap Rx. */ + ret = pthread_create( &vPcapRecvThreadHandle, + NULL, + prvLinuxPcapRecvThread, + NULL ); + + if( ret != 0 ) + { + FreeRTOS_printf( ( "pthread error %d", ret ) ); + break; + } + + /* Create the thread that handles pcap Tx. */ + ret = pthread_create( &vPcapSendThreadHandle, + NULL, + prvLinuxPcapSendThread, + NULL ); + + if( ret != 0 ) + { + FreeRTOS_printf( ( "pthread error %d", ret ) ); + break; + } + + ret = pdPASS; + } while( 0 ); + + /* Create a task that simulates an interrupt in a real system. This will + * block waiting for packets, then send a message to the IP task when data + * is available. */ + if( xTaskCreate( prvInterruptSimulatorTask, + "MAC_ISR", + configMINIMAL_STACK_SIZE, + NULL, + configMAC_ISR_SIMULATOR_PRIORITY, + NULL ) != pdPASS ) + { + ret = pdFAIL; + FreeRTOS_printf( ( "xTaskCreate could not create a new task\n" ) ); + } + } + + return ret; +} + +/*! + * @brief Create the buffers used to pass packets between the FreeRTOS simulator + * and the pthreads that are handling pcap as well as the FreeRTOS task + * responsible of simulating an interrupt. + * @returns pdPASS when successful and pdFAIL when there is a failure + */ +static int prvConfigureCaptureBehaviour( void ) +{ + struct bpf_program xFilterCode; + uint32_t ulNetMask; + char pcap_filter[ 500 ]; + int ret = pdFAIL; + + FreeRTOS_debug_printf( ( "Configuring Capture behaviour\n" ) ); + + /* Set up a filter so only the packets of interest are passed to the IP + * stack. errbuf is used for convenience to create the string. Don't + * confuse this with an error message. */ + sprintf( pcap_filter, "broadcast or multicast or ether host %x:%x:%x:%x:%x:%x", + ipLOCAL_MAC_ADDRESS[ 0 ], + ipLOCAL_MAC_ADDRESS[ 1 ], + ipLOCAL_MAC_ADDRESS[ 2 ], + ipLOCAL_MAC_ADDRESS[ 3 ], + ipLOCAL_MAC_ADDRESS[ 4 ], + ipLOCAL_MAC_ADDRESS[ 5 ] ); + FreeRTOS_debug_printf( ( "pcap filter to compile: %s\n", pcap_filter ) ); + + ulNetMask = ( configNET_MASK3 << 24UL ) | ( configNET_MASK2 << 16UL ) | ( configNET_MASK1 << 8L ) | configNET_MASK0; + + ret = pcap_compile( pxOpenedInterfaceHandle, + &xFilterCode, + pcap_filter, + 1, + ulNetMask ); + + if( ret < 0 ) + { + ( void ) printf( "\nThe packet filter string is invalid %s\n", + pcap_geterr( pxOpenedInterfaceHandle ) ); + } + else + { + ret = pcap_setfilter( pxOpenedInterfaceHandle, &xFilterCode ); + + if( ret < 0 ) + { + ( void ) printf( "\nAn error occurred setting the packet filter. %s\n", + pcap_geterr( pxOpenedInterfaceHandle ) ); + } + else + { + ret = pdPASS; + } + + /* When pcap_compile() succeeds, it allocates memory for the memory pointed to by the bpf_program struct + * parameter.pcap_freecode() will free that memory. */ + pcap_freecode( &xFilterCode ); + } + + return ret; +} + +/*! + * @brief callback function called from pcap_dispatch function when new + * data arrives on the interface + * @param [in] user data sent to pcap_dispatch + * @param [in] pkt_header received packet header + * @param [in] pkt_data received packet data + * @warning this is called from a Linux thread, do not attempt any FreeRTOS calls + */ +static void pcap_callback( unsigned char * user, + const struct pcap_pkthdr * pkt_header, + const u_char * pkt_data ) +{ + FreeRTOS_debug_printf( ( "Receiving < =========== network callback user: %s len: %d caplen: %d\n", + user, + pkt_header->len, + pkt_header->caplen ) ); + print_hex( pkt_data, pkt_header->len ); + + /* Pass data to the FreeRTOS simulator on a thread safe circular buffer. */ + if( ( pkt_header->caplen <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) && + ( uxStreamBufferGetSpace( xRecvBuffer ) >= ( ( ( size_t ) pkt_header->caplen ) + sizeof( *pkt_header ) ) ) ) + { + uxStreamBufferAdd( xRecvBuffer, 0, ( const uint8_t * ) pkt_header, sizeof( *pkt_header ) ); + uxStreamBufferAdd( xRecvBuffer, 0, ( const uint8_t * ) pkt_data, ( size_t ) pkt_header->caplen ); + } +} + +/*! + * @brief infinite loop pthread to read from pcap + * @param [in] pvParam not used + * @returns NULL + * @warning this is called from a Linux thread, do not attempt any FreeRTOS calls + * @remarks This function disables signal, to prevent it from being put into + * sleep byt the posix port + */ +static void * prvLinuxPcapRecvThread( void * pvParam ) +{ + int ret; + + ( void ) pvParam; + + /* Disable signals to this thread since this is a Linux pthread to be able to + * printf and other blocking operations without being interrupted and put in + * suspension mode by the linux port signals + */ + sigset_t set; + + sigfillset( &set ); + pthread_sigmask( SIG_SETMASK, &set, NULL ); + + for( ; ; ) + { + ret = pcap_dispatch( pxOpenedInterfaceHandle, 1, + pcap_callback, ( u_char * ) "mydata" ); + + if( ret == -1 ) + { + FreeRTOS_printf( ( "pcap_dispatch error received: %s\n", + pcap_geterr( pxOpenedInterfaceHandle ) ) ); + } + } + + return NULL; +} + +/*! + * @brief Infinite loop thread that waits for events when there is data + * available then sends the data on the interface + * @param [in] pvParam not used + * @returns NULL + * @warning this is called from a Linux thread, do not attempt any FreeRTOS calls + */ +static void * prvLinuxPcapSendThread( void * pvParam ) +{ + size_t xLength; + uint8_t ucBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ]; + const time_t xMaxMSToWait = 1000; + + ( void ) pvParam; + + /* disable signals to avoid treating this thread as a FreeRTOS task and putting + * it to sleep by the scheduler */ + sigset_t set; + + sigfillset( &set ); + pthread_sigmask( SIG_SETMASK, &set, NULL ); + + for( ; ; ) + { + /* Wait until notified of something to send. */ + event_wait_timed( pvSendEvent, xMaxMSToWait ); + + /* Is there more than the length value stored in the circular buffer + * used to pass data from the FreeRTOS simulator into this pthread?*/ + while( uxStreamBufferGetSize( xSendBuffer ) > sizeof( xLength ) ) + { + uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) &xLength, sizeof( xLength ), pdFALSE ); + uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) ucBuffer, xLength, pdFALSE ); + FreeRTOS_debug_printf( ( "Sending ========== > data pcap_sendpadcket %lu\n", xLength ) ); + print_hex( ucBuffer, xLength ); + + if( pcap_sendpacket( pxOpenedInterfaceHandle, ucBuffer, xLength ) != 0 ) + { + FreeRTOS_printf( ( "pcap_sendpackeet: send failed %d\n", ulPCAPSendFailures ) ); + ulPCAPSendFailures++; + } + } + } + + return NULL; +} + +/*! + * @brief FreeRTOS infinite loop thread that simulates a network interrupt to notify the + * network stack of the presence of new data + * @param [in] pvParameters not used + */ +static void prvInterruptSimulatorTask( void * pvParameters ) +{ + struct pcap_pkthdr xHeader; + static struct pcap_pkthdr * pxHeader; + const uint8_t * pucPacketData; + uint8_t ucRecvBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ]; + NetworkBufferDescriptor_t * pxNetworkBuffer; + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + eFrameProcessingResult_t eResult; + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + for( ; ; ) + { + /* Does the circular buffer used to pass data from the pthread thread that + * handles pacap Rx into the FreeRTOS simulator contain another packet? */ + if( uxStreamBufferGetSize( xRecvBuffer ) > sizeof( xHeader ) ) + { + /* Get the next packet. */ + uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) &xHeader, sizeof( xHeader ), pdFALSE ); + uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) ucRecvBuffer, ( size_t ) xHeader.len, pdFALSE ); + pucPacketData = ucRecvBuffer; + pxHeader = &xHeader; + + iptraceNETWORK_INTERFACE_RECEIVE(); + + /* Check for minimal size. */ + if( pxHeader->len >= sizeof( EthernetHeader_t ) ) + { + eResult = ipCONSIDER_FRAME_FOR_PROCESSING( pucPacketData ); + } + else + { + eResult = eReleaseBuffer; + } + + if( eResult == eProcessBuffer ) + { + /* Will the data fit into the frame buffer? */ + if( pxHeader->len <= ipTOTAL_ETHERNET_FRAME_SIZE ) + { + /* Obtain a buffer into which the data can be placed. This + * is only an interrupt simulator, not a real interrupt, so it + * is ok to call the task level function here, but note that + * some buffer implementations cannot be called from a real + * interrupt. */ + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( pxHeader->len, 0 ); + + if( pxNetworkBuffer != NULL ) + { + memcpy( pxNetworkBuffer->pucEthernetBuffer, pucPacketData, pxHeader->len ); + pxNetworkBuffer->xDataLength = ( size_t ) pxHeader->len; + + #if ( niDISRUPT_PACKETS == 1 ) + { + pxNetworkBuffer = vRxFaultInjection( pxNetworkBuffer, pucPacketData ); + } + #endif /* niDISRUPT_PACKETS */ + + if( pxNetworkBuffer != NULL ) + { + xRxEvent.pvData = ( void * ) pxNetworkBuffer; + + /* Data was received and stored. Send a message to + * the IP task to let it know. */ + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL ) + { + /* The buffer could not be sent to the stack so + * must be released again. This is only an + * interrupt simulator, not a real interrupt, so it + * is ok to use the task level function here, but + * note no all buffer implementations will allow + * this function to be executed from a real + * interrupt. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + iptraceETHERNET_RX_EVENT_LOST(); + } + } + else + { + /* The packet was already released or stored inside + * vRxFaultInjection(). Don't release it here. */ + } + } + else + { + iptraceETHERNET_RX_EVENT_LOST(); + } + } + else + { + /* Log that a packet was dropped because it would have + * overflowed the buffer, but there may be more buffers to + * process. */ + } + } + } + else + { + /* There is no real way of simulating an interrupt. Make sure + * other tasks can run. */ + vTaskDelay( configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY ); + } + } +} + +/*! + * @brief remove spaces from pcMessage into pcBuffer + * @param [out] pcBuffer buffer to fill up + * @param [in] aBuflen length of pcBuffer + * @param [in] pcMessage original message + * @returns + */ +static const char * prvRemoveSpaces( char * pcBuffer, + int aBuflen, + const char * pcMessage ) +{ + char * pcTarget = pcBuffer; + + /* Utility function used to format messages being printed only. */ + while( ( *pcMessage != 0 ) && ( pcTarget < ( &pcBuffer[ aBuflen - 1 ] ) ) ) + { + *( pcTarget++ ) = *pcMessage; + + if( isspace( *pcMessage ) != pdFALSE ) + { + while( isspace( *pcMessage ) != pdFALSE ) + { + pcMessage++; + } + } + else + { + pcMessage++; + } + } + + *pcTarget = '\0'; + + return pcBuffer; +} + +/*! + * @brief print binary packet in hex + * @param [in] bin_daa data to print + * @param [in] len length of the data + */ +static void print_hex( unsigned const char * const bin_data, + size_t len ) +{ + size_t i; + + for( i = 0; i < len; ++i ) + { + FreeRTOS_debug_printf( ( "%.2X ", bin_data[ i ] ) ); + } + + FreeRTOS_debug_printf( ( "\n" ) ); +} + + +#define BUFFER_SIZE ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING ) +#define BUFFER_SIZE_ROUNDED_UP ( ( BUFFER_SIZE + 7 ) & ~0x07UL ) + +/*! + * @brief Allocate RAM for packet buffers and set the pucEthernetBuffer field for each descriptor. + * Called when the BufferAllocation1 scheme is used. + * @param [in,out] pxNetworkBuffers Pointer to an array of NetworkBufferDescriptor_t to populate. + */ +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + static uint8_t * pucNetworkPacketBuffers = NULL; + size_t uxIndex; + + if( pucNetworkPacketBuffers == NULL ) + { + pucNetworkPacketBuffers = ( uint8_t * ) malloc( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * BUFFER_SIZE_ROUNDED_UP ); + } + + if( pucNetworkPacketBuffers == NULL ) + { + FreeRTOS_printf( ( "Failed to allocate memory for pxNetworkBuffers" ) ); + configASSERT( 0 ); + } + else + { + for( uxIndex = 0; uxIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; uxIndex++ ) + { + size_t uxOffset = uxIndex * BUFFER_SIZE_ROUNDED_UP; + NetworkBufferDescriptor_t ** ppDescriptor; + + /* At the beginning of each pbuff is a pointer to the relevant descriptor */ + ppDescriptor = ( NetworkBufferDescriptor_t ** ) &( pucNetworkPacketBuffers[ uxOffset ] ); + + /* Set this pointer to the address of the correct descriptor */ + *ppDescriptor = &( pxNetworkBuffers[ uxIndex ] ); + + /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the + * beginning of the allocated buffer. */ + pxNetworkBuffers[ uxIndex ].pucEthernetBuffer = &( pucNetworkPacketBuffers[ uxOffset + ipBUFFER_PADDING ] ); + } + } +} diff --git a/FreeRTOS/source/portable/NetworkInterface/mw300_rd/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/mw300_rd/NetworkInterface.c new file mode 100644 index 0000000..e8fa5bf --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/mw300_rd/NetworkInterface.c @@ -0,0 +1,247 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "list.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_DNS.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "wifi-decl.h" +#include "wmerrno.h" +#include "wifi.h" + +#include + +#define net_e( ... ) \ + wmlog_e( "freertos_tcp", ## __VA_ARGS__ ) +#define net_w( ... ) \ + wmlog_w( "freertos_tcp", ## __VA_ARGS__ ) +#define net_d( ... ) \ + wmlog( "freertos_tcp", ## __VA_ARGS__ ) + +#if 0 /*this is lwip structure. */ + #define MAX_INTERFACES_SUPPORTED 3 + static struct netif * netif_arr[ MAX_INTERFACES_SUPPORTED ]; +#endif + +/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet + * driver will filter incoming packets and only pass the stack those packets it + * considers need processing. */ +#if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer +#else + #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) +#endif + +#define IP_ADDR_ANY ( ( ip_addr_t * ) &ip_addr_any ) +#define IP_ADDR_BROADCAST ( ( ip_addr_t * ) &ip_addr_broadcast ) + +/** 255.255.255.255 */ +#define IPADDR_NONE ( ( u32_t ) 0xffffffffUL ) +/** 127.0.0.1 */ +#define IPADDR_LOOPBACK ( ( u32_t ) 0x7f000001UL ) +/** 0.0.0.0 */ +#define IPADDR_ANY ( ( u32_t ) 0x00000000UL ) +/** 255.255.255.255 */ +#define IPADDR_BROADCAST ( ( u32_t ) 0xffffffffUL ) + +/** 255.255.255.255 */ +#define INADDR_NONE IPADDR_NONE +/** 127.0.0.1 */ +#define INADDR_LOOPBACK IPADDR_LOOPBACK +/** 0.0.0.0 */ +#define INADDR_ANY IPADDR_ANY +/** 255.255.255.255 */ +#define INADDR_BROADCAST IPADDR_BROADCAST + +enum if_state_t +{ + INTERFACE_DOWN = 0, + INTERFACE_UP, +}; +struct ip_addr +{ + u32_t addr; +}; + +#define MLAN_BSS_TYPE_STA 0 + +extern uint8_t outbuf[ 2048 ]; +extern bool mlan_is_amsdu( const t_u8 * rcvdata ); +extern t_u8 * mlan_get_payload( const t_u8 * rcvdata, + t_u16 * payload_len, + int * interface ); +extern int wrapper_wlan_handle_amsdu_rx_packet( const t_u8 * rcvdata, + const t_u16 datalen ); +extern int wrapper_wlan_handle_rx_packet( const t_u16 datalen, + const t_u8 * rcvdata, + NetworkBufferDescriptor_t * pxNetworkBuffer ); +static volatile uint32_t xInterfaceState = INTERFACE_DOWN; + +static int process_data_packet( const t_u8 * databuf, + const t_u16 datalen ) +{ + int interface = BSS_TYPE_STA; + t_u8 * payload = NULL; + t_u16 payload_len = 0; + const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 ); + + NetworkBufferDescriptor_t * pxNetworkBuffer; + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + + payload = ( t_u8 * ) mlan_get_payload( databuf, &payload_len, &interface ); + + if( eConsiderFrameForProcessing( payload ) != eProcessBuffer ) + { + net_d( "Dropping packet\r\n" ); + return WM_SUCCESS; + } + + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( /*payload_len*/ datalen, xDescriptorWaitTime ); + + if( pxNetworkBuffer != NULL ) + { + /* Set the packet size, in case a larger buffer was returned. */ + pxNetworkBuffer->xDataLength = payload_len; + + /* Copy the packet data. */ + memcpy( pxNetworkBuffer->pucEthernetBuffer, payload, payload_len ); + + xRxEvent.pvData = ( void * ) pxNetworkBuffer; + + if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFAIL ) + { + wmprintf( "Failed to enqueue packet to network stack %p, len %d", payload, payload_len ); + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + return WM_FAIL; + } + } + + return WM_SUCCESS; +} + +/* Callback function called from the wifi module */ +void handle_data_packet( const t_u8 interface, + const t_u8 * rcvdata, + const t_u16 datalen ) +{ + if( interface == BSS_TYPE_STA ) + { + process_data_packet( rcvdata, datalen ); + } +} + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + uint8_t ret; + mac_addr_t mac_addr; + + ret = wifi_get_device_mac_addr( &mac_addr ); + + if( ret != WM_SUCCESS ) + { + net_d( "Failed to get mac address" ); + } + + FreeRTOS_UpdateMACAddress( mac_addr.mac ); + + return ( xInterfaceState == INTERFACE_UP && ret == WM_SUCCESS ) ? pdTRUE : pdFALSE; +} + +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + /* FIX ME. */ +} + +BaseType_t xGetPhyLinkStatus( void ) +{ + /* FIX ME. */ + return pdFALSE; +} +void vNetworkNotifyIFDown() +{ + IPStackEvent_t xRxEvent = { eNetworkDownEvent, NULL }; + + xInterfaceState = INTERFACE_DOWN; + + if( xSendEventStructToIPTask( &xRxEvent, 0 ) != pdPASS ) + { + /* Could not send the message, so it is still pending. */ + net_e( "Could not send network down event" ); + } + else + { + /* Message was sent so it is not pending. */ + net_d( "Sent network down event" ); + } +} + +void vNetworkNotifyIFUp() +{ + xInterfaceState = INTERFACE_UP; +} + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, + BaseType_t xReleaseAfterSend ) +{ + uint8_t pkt_len; + + if( ( pxNetworkBuffer == NULL ) || + ( pxNetworkBuffer->pucEthernetBuffer == NULL ) || + ( pxNetworkBuffer->xDataLength == 0 ) ) + { + net_d( "Incorrect params" ); + return pdFALSE; + } + + memset( outbuf, 0x00, sizeof( outbuf ) ); + pkt_len = 22 + 4; /* sizeof(TxPD) + INTF_HEADER_LEN */ + memcpy( ( u8_t * ) outbuf + pkt_len, ( u8_t * ) pxNetworkBuffer->pucEthernetBuffer, + pxNetworkBuffer->xDataLength ); + int ret = wifi_low_level_output( BSS_TYPE_STA, outbuf + pkt_len, pxNetworkBuffer->xDataLength ); + + if( ret != WM_SUCCESS ) + { + net_e( "Failed output %p, length %d, error %d \r\n", pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength, ret ); + } + + if( xReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + return ret == WM_SUCCESS ? pdTRUE : pdFALSE; +} diff --git a/FreeRTOS/source/portable/NetworkInterface/pic32mzef/BufferAllocation_2.c b/FreeRTOS/source/portable/NetworkInterface/pic32mzef/BufferAllocation_2.c new file mode 100644 index 0000000..27e0472 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/pic32mzef/BufferAllocation_2.c @@ -0,0 +1,594 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/****************************************************************************** +* +* See the following web page for essential buffer allocation scheme usage and +* configuration details: +* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html +* +******************************************************************************/ + +/* THIS FILE SHOULD NOT BE USED IF THE PROJECT INCLUDES A MEMORY ALLOCATOR + * THAT WILL FRAGMENT THE HEAP MEMORY. For example, heap_2 must not be used, + * heap_4 can be used. */ + +/* Standard includes. */ +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_UDP_IP.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" + +#include "tcpip/tcpip.h" +#include "tcpip/src/tcpip_private.h" + +#include "NetworkConfig.h" + +/* The obtained network buffer must be large enough to hold a packet that might + * replace the packet that was requested to be sent. */ +#if ipconfigUSE_TCP == 1 + #define baMINIMAL_BUFFER_SIZE sizeof( TCPPacket_t ) +#else + #define baMINIMAL_BUFFER_SIZE sizeof( ARPPacket_t ) +#endif /* ipconfigUSE_TCP == 1 */ + +/*_RB_ This is too complex not to have an explanation. */ +#if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES ) + #define ASSERT_CONCAT_( a, b ) a ## b + #define ASSERT_CONCAT( a, b ) ASSERT_CONCAT_( a, b ) + #define STATIC_ASSERT( e ) \ + ; enum { ASSERT_CONCAT( assert_line_, __LINE__ ) = 1 / ( !!( e ) ) } + + STATIC_ASSERT( ipconfigETHERNET_MINIMUM_PACKET_BYTES <= baMINIMAL_BUFFER_SIZE ); +#endif + +/* A list of free (available) NetworkBufferDescriptor_t structures. */ +static List_t xFreeBuffersList; + +/* Some statistics about the use of buffers. */ +static size_t uxMinimumFreeNetworkBuffers; + +/* Declares the pool of NetworkBufferDescriptor_t structures that are available + * to the system. All the network buffers referenced from xFreeBuffersList exist + * in this array. The array is not accessed directly except during initialisation, + * when the xFreeBuffersList is filled (as all the buffers are free when the system + * is booted). */ +static NetworkBufferDescriptor_t xNetworkBufferDescriptors[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ]; + +/* This constant is defined as false to let FreeRTOS_TCP_IP.c know that the + * network buffers have a variable size: resizing may be necessary */ +const BaseType_t xBufferAllocFixedSize = pdFALSE; + +/* The semaphore used to obtain network buffers. */ +static SemaphoreHandle_t xNetworkBufferSemaphore = NULL; + +/*-----------------------------------------------------------*/ + +#ifdef PIC32_USE_ETHERNET + +/* PIC32 specific stuff */ +/* */ + +/* MAC packet acknowledgment, once MAC is done with it */ + static bool PIC32_MacPacketAcknowledge( TCPIP_MAC_PACKET * pPkt, + const void * param ); + +/* allocates a MAC packet that holds a data buffer that can be used by both: */ +/* - the FreeRTOSIP (NetworkBufferDescriptor_t->pucEthernetBuffer) */ +/* - the Harmony MAC driver: TCPIP_MAC_PACKET->pDSeg->segLoad */ +/* from the beginning of the buffer: */ +/* - 4 bytes pointer to the network descriptor (FreeRTOS) */ +/* - 4 bytes pointer to the MAC packet (pic32_NetworkInterface.c) */ +/* - 2 bytes offset from the MAC packet (Harmony MAC driver: segLoadOffset) */ +/* */ +/* NOTE: segLoadLen should NOT include: */ +/* - the TCPIP_MAC_FRAME_OFFSET (== ipBUFFER_PADDING which should be == 10!) */ +/* - the sizeof(TCPIP_MAC_ETHERNET_HEADER) */ +/* These are added by the MAC packet allocation! */ +/* */ + static uint8_t * PIC32_PktAlloc( uint16_t pktLen, + uint16_t segLoadLen, + TCPIP_MAC_PACKET_ACK_FUNC ackF, + TCPIP_MAC_PACKET ** pPtrPkt ) + { + uint8_t * pBuff = 0; + + /* allocate standard packet */ + TCPIP_MAC_PACKET * pPkt = TCPIP_PKT_PacketAlloc( pktLen, segLoadLen, 0 ); + + /* set the MAC packet pointer in the packet */ + if( pPkt != 0 ) + { + pBuff = pPkt->pDSeg->segLoad; + TCPIP_MAC_PACKET ** ppkt = ( TCPIP_MAC_PACKET ** ) ( pBuff - PIC32_BUFFER_PKT_PTR_OSSET ); + configASSERT( ( ( uint32_t ) ppkt & ( sizeof( uint32_t ) - 1 ) ) == 0 ); + *ppkt = pPkt; /* store the packet it comes from */ + pPkt->ackFunc = ackF; + pPkt->ackParam = 0; + } + + if( pPtrPkt != 0 ) + { + *pPtrPkt = pPkt; + } + + return pBuff; + } + + + +/* standard PIC32 MAC allocation function for a MAC packet */ +/* this packet saves room for the FreeRTOS network descriptor */ +/* at the beginning of the data buffer */ +/* see NetworkBufferAllocate */ +/* Note: flags parameter is ignored since that's used in the Harmony stack only */ + TCPIP_MAC_PACKET * PIC32_MacPacketAllocate( uint16_t pktLen, + uint16_t segLoadLen, + TCPIP_MAC_PACKET_FLAGS flags ) + { + TCPIP_MAC_PACKET * pPkt; + + PIC32_PktAlloc( pktLen, segLoadLen, 0, &pPkt ); + + return pPkt; + } + +/* standard PIC32 MAC packet acknowledgment */ +/* function called once MAC is done with it */ + static bool PIC32_MacPacketAcknowledge( TCPIP_MAC_PACKET * pPkt, + const void * param ) + { + configASSERT( ( pPkt != 0 ) ); + + TCPIP_PKT_PacketFree( pPkt ); + + return false; + } + +/* associates the current MAC packet with a network descriptor */ +/* mainly for RX packet */ + void PIC32_MacAssociate( TCPIP_MAC_PACKET * pRxPkt, + NetworkBufferDescriptor_t * pxBufferDescriptor, + size_t pktLength ) + { + uint8_t * pPktBuff = pRxPkt->pDSeg->segLoad; + + pxBufferDescriptor->pucEthernetBuffer = pPktBuff; + pxBufferDescriptor->xDataLength = pktLength; + + /* make sure this is a properly allocated packet */ + TCPIP_MAC_PACKET ** ppkt = ( TCPIP_MAC_PACKET ** ) ( pPktBuff - PIC32_BUFFER_PKT_PTR_OSSET ); + + configASSERT( ( ( uint32_t ) ppkt & ( sizeof( uint32_t ) - 1 ) ) == 0 ); + + if( *ppkt != pRxPkt ) + { + configASSERT( false ); + } + + /* set the proper descriptor info */ + NetworkBufferDescriptor_t ** ppDcpt = ( NetworkBufferDescriptor_t ** ) ( pPktBuff - ipBUFFER_PADDING ); + + configASSERT( ( ( uint32_t ) ppDcpt & ( sizeof( uint32_t ) - 1 ) ) == 0 ); + *ppDcpt = pxBufferDescriptor; + } + +/* debug functionality */ + void PIC32_MacPacketOrphan( TCPIP_MAC_PACKET * pPkt ) + { + TCPIP_PKT_PacketFree( pPkt ); + configASSERT( false ); + } + +/* FreeRTOS allocation functions */ + +/* allocates a buffer that can be used by both: */ +/* - the FreeRTOSIP (NetworkBufferDescriptor_t->pucEthernetBuffer) */ +/* - the Harmony MAC driver: TCPIP_MAC_PACKET */ +/* See PIC32_PktAlloc for details */ +/* */ +/* NOTE: reqLength should NOT include the ipBUFFER_PADDING (which should be == 10!) */ +/* or the sizeof(TCPIP_MAC_ETHERNET_HEADER) */ +/* These are added by the MAC packet allocation! */ +/* */ + uint8_t * NetworkBufferAllocate( size_t reqLength ) + { + return PIC32_PktAlloc( sizeof( TCPIP_MAC_PACKET ), reqLength, PIC32_MacPacketAcknowledge, 0 ); + } + +/* deallocates a network buffer previously allocated */ +/* with NetworkBufferAllocate */ + void NetworkBufferFree( uint8_t * pNetworkBuffer ) + { + if( pNetworkBuffer != 0 ) + { + TCPIP_MAC_PACKET ** ppkt = ( TCPIP_MAC_PACKET ** ) ( pNetworkBuffer - PIC32_BUFFER_PKT_PTR_OSSET ); + configASSERT( ( ( uint32_t ) ppkt & ( sizeof( uint32_t ) - 1 ) ) == 0 ); + TCPIP_MAC_PACKET * pPkt = *ppkt; + configASSERT( ( pPkt != 0 ) ); + + if( pPkt->ackFunc != 0 ) + { + ( *pPkt->ackFunc )( pPkt, pPkt->ackParam ); + } + else + { /* ??? */ + PIC32_MacPacketOrphan( pPkt ); + } + } + } + +#endif /* #ifdef PIC32_USE_ETHERNET */ + +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkBuffersInitialise( void ) +{ + BaseType_t xReturn, x; + + /* Only initialise the buffers and their associated kernel objects if they + * have not been initialised before. */ + if( xNetworkBufferSemaphore == NULL ) + { + xNetworkBufferSemaphore = xSemaphoreCreateCounting( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ); + configASSERT( xNetworkBufferSemaphore ); + + if( xNetworkBufferSemaphore != NULL ) + { + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + vQueueAddToRegistry( xNetworkBufferSemaphore, "NetBufSem" ); + } + #endif /* configQUEUE_REGISTRY_SIZE */ + + /* If the trace recorder code is included name the semaphore for viewing + * in FreeRTOS+Trace. */ + #if ( ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 ) + { + extern QueueHandle_t xNetworkEventQueue; + vTraceSetQueueName( xNetworkEventQueue, "IPStackEvent" ); + vTraceSetQueueName( xNetworkBufferSemaphore, "NetworkBufferCount" ); + } + #endif /* ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 */ + + vListInitialise( &xFreeBuffersList ); + + /* Initialise all the network buffers. No storage is allocated to + * the buffers yet. */ + for( x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ ) + { + /* Initialise and set the owner of the buffer list items. */ + xNetworkBufferDescriptors[ x ].pucEthernetBuffer = NULL; + vListInitialiseItem( &( xNetworkBufferDescriptors[ x ].xBufferListItem ) ); + listSET_LIST_ITEM_OWNER( &( xNetworkBufferDescriptors[ x ].xBufferListItem ), &xNetworkBufferDescriptors[ x ] ); + + /* Currently, all buffers are available for use. */ + vListInsert( &xFreeBuffersList, &( xNetworkBufferDescriptors[ x ].xBufferListItem ) ); + } + + uxMinimumFreeNetworkBuffers = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; + } + } + + if( xNetworkBufferSemaphore == NULL ) + { + xReturn = pdFAIL; + } + else + { + xReturn = pdPASS; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +uint8_t * pucGetNetworkBuffer( size_t * pxRequestedSizeBytes ) +{ + uint8_t * pucEthernetBuffer; + size_t xSize = *pxRequestedSizeBytes; + + if( xSize < baMINIMAL_BUFFER_SIZE ) + { + /* Buffers must be at least large enough to hold a TCP-packet with + * headers, or an ARP packet, in case TCP is not included. */ + xSize = baMINIMAL_BUFFER_SIZE; + } + + /* Round up xSize to the nearest multiple of N bytes, + * where N equals 'sizeof( size_t )'. */ + if( ( xSize & ( sizeof( size_t ) - 1u ) ) != 0u ) + { + xSize = ( xSize | ( sizeof( size_t ) - 1u ) ) + 1u; + } + + *pxRequestedSizeBytes = xSize; + + /* Allocate a buffer large enough to store the requested Ethernet frame size + * and a pointer to a network buffer structure (hence the addition of + * ipBUFFER_PADDING bytes). */ + + #ifdef PIC32_USE_ETHERNET + pucEthernetBuffer = NetworkBufferAllocate( xSize - sizeof( TCPIP_MAC_ETHERNET_HEADER ) ); + #else + pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xSize + ipBUFFER_PADDING ); + #endif /* #ifdef PIC32_USE_ETHERNET */ + + configASSERT( pucEthernetBuffer ); + + if( pucEthernetBuffer != NULL ) + { + /* Enough space is left at the start of the buffer to place a pointer to + * the network buffer structure that references this Ethernet buffer. + * Return a pointer to the start of the Ethernet buffer itself. */ + #ifndef PIC32_USE_ETHERNET + pucEthernetBuffer += ipBUFFER_PADDING; + #endif /* #ifndef PIC32_USE_ETHERNET */ + } + + return pucEthernetBuffer; +} +/*-----------------------------------------------------------*/ + +void vReleaseNetworkBuffer( uint8_t * pucEthernetBuffer ) +{ + /* There is space before the Ethernet buffer in which a pointer to the + * network buffer that references this Ethernet buffer is stored. Remove the + * space before freeing the buffer. */ + #ifdef PIC32_USE_ETHERNET + NetworkBufferFree( pucEthernetBuffer ); + #else + if( pucEthernetBuffer != NULL ) + { + pucEthernetBuffer -= ipBUFFER_PADDING; + vPortFree( ( void * ) pucEthernetBuffer ); + } + #endif /* #ifdef PIC32_USE_ETHERNET */ +} +/*-----------------------------------------------------------*/ + +NetworkBufferDescriptor_t * pxGetNetworkBufferWithDescriptor( size_t xRequestedSizeBytes, + TickType_t xBlockTimeTicks ) +{ + NetworkBufferDescriptor_t * pxReturn = NULL; + size_t uxCount; + + if( ( xRequestedSizeBytes != 0u ) && ( xRequestedSizeBytes < ( size_t ) baMINIMAL_BUFFER_SIZE ) ) + { + /* ARP packets can replace application packets, so the storage must be + * at least large enough to hold an ARP. */ + xRequestedSizeBytes = baMINIMAL_BUFFER_SIZE; + } + + #ifdef PIC32_USE_ETHERNET + if( xRequestedSizeBytes != 0u ) + { + #endif /* #ifdef PIC32_USE_ETHERNET */ + xRequestedSizeBytes += 2u; + + if( ( xRequestedSizeBytes & ( sizeof( size_t ) - 1u ) ) != 0u ) + { + xRequestedSizeBytes = ( xRequestedSizeBytes | ( sizeof( size_t ) - 1u ) ) + 1u; + } + + #ifdef PIC32_USE_ETHERNET +} + #endif /* #ifdef PIC32_USE_ETHERNET */ + + /* If there is a semaphore available, there is a network buffer available. */ + if( xSemaphoreTake( xNetworkBufferSemaphore, xBlockTimeTicks ) == pdPASS ) + { + /* Protect the structure as it is accessed from tasks and interrupts. */ + taskENTER_CRITICAL(); + { + pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList ); + uxListRemove( &( pxReturn->xBufferListItem ) ); + } + taskEXIT_CRITICAL(); + + /* Reading UBaseType_t, no critical section needed. */ + uxCount = listCURRENT_LIST_LENGTH( &xFreeBuffersList ); + + if( uxMinimumFreeNetworkBuffers > uxCount ) + { + uxMinimumFreeNetworkBuffers = uxCount; + } + + /* Allocate storage of exactly the requested size to the buffer. */ + configASSERT( pxReturn->pucEthernetBuffer == NULL ); + + if( xRequestedSizeBytes > 0 ) + { + /* Extra space is obtained so a pointer to the network buffer can + * be stored at the beginning of the buffer. */ + + #ifdef PIC32_USE_ETHERNET + pxReturn->pucEthernetBuffer = NetworkBufferAllocate( xRequestedSizeBytes - sizeof( TCPIP_MAC_ETHERNET_HEADER ) ); + #else + pxReturn->pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xRequestedSizeBytes + ipBUFFER_PADDING ); + #endif /* #ifdef PIC32_USE_ETHERNET */ + + if( pxReturn->pucEthernetBuffer == NULL ) + { + /* The attempt to allocate storage for the buffer payload failed, + * so the network buffer structure cannot be used and must be + * released. */ + vReleaseNetworkBufferAndDescriptor( pxReturn ); + pxReturn = NULL; + } + else + { + /* Store a pointer to the network buffer structure in the + * buffer storage area, then move the buffer pointer on past the + * stored pointer so the pointer value is not overwritten by the + * application when the buffer is used. */ + #ifdef PIC32_USE_ETHERNET + *( ( NetworkBufferDescriptor_t ** ) ( pxReturn->pucEthernetBuffer - ipBUFFER_PADDING ) ) = pxReturn; + #else + *( ( NetworkBufferDescriptor_t ** ) ( pxReturn->pucEthernetBuffer ) ) = pxReturn; + pxReturn->pucEthernetBuffer += ipBUFFER_PADDING; + #endif /* #ifdef PIC32_USE_ETHERNET */ + + /* Store the actual size of the allocated buffer, which may be + * greater than the original requested size. */ + pxReturn->xDataLength = xRequestedSizeBytes; + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + /* make sure the buffer is not linked */ + pxReturn->pxNextBuffer = NULL; + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + } + } + else + { + /* A descriptor is being returned without an associated buffer being + * allocated. */ + } + } + + if( pxReturn == NULL ) + { + iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER(); + } + else + { + iptraceNETWORK_BUFFER_OBTAINED( pxReturn ); + } + + return pxReturn; +} +/*-----------------------------------------------------------*/ + +void vReleaseNetworkBufferAndDescriptor( NetworkBufferDescriptor_t * const pxNetworkBuffer ) +{ + BaseType_t xListItemAlreadyInFreeList; + + /* Ensure the buffer is returned to the list of free buffers before the + * counting semaphore is 'given' to say a buffer is available. Release the + * storage allocated to the buffer payload. THIS FILE SHOULD NOT BE USED + * IF THE PROJECT INCLUDES A MEMORY ALLOCATOR THAT WILL FRAGMENT THE HEAP + * MEMORY. For example, heap_2 must not be used, heap_4 can be used. */ + vReleaseNetworkBuffer( pxNetworkBuffer->pucEthernetBuffer ); + pxNetworkBuffer->pucEthernetBuffer = NULL; + + taskENTER_CRITICAL(); + { + xListItemAlreadyInFreeList = listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) ); + + if( xListItemAlreadyInFreeList == pdFALSE ) + { + vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) ); + } + } + taskEXIT_CRITICAL(); + + /* + * Update the network state machine, unless the program fails to release its 'xNetworkBufferSemaphore'. + * The program should only try to release its semaphore if 'xListItemAlreadyInFreeList' is false. + */ + if( xListItemAlreadyInFreeList == pdFALSE ) + { + if( xSemaphoreGive( xNetworkBufferSemaphore ) == pdTRUE ) + { + iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer ); + } + } + else + { + iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer ); + } +} +/*-----------------------------------------------------------*/ + +/* + * Returns the number of free network buffers + */ +UBaseType_t uxGetNumberOfFreeNetworkBuffers( void ) +{ + return listCURRENT_LIST_LENGTH( &xFreeBuffersList ); +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxGetMinimumFreeNetworkBuffers( void ) +{ + return uxMinimumFreeNetworkBuffers; +} +/*-----------------------------------------------------------*/ + +NetworkBufferDescriptor_t * pxResizeNetworkBufferWithDescriptor( NetworkBufferDescriptor_t * pxNetworkBuffer, + size_t xNewSizeBytes ) +{ + size_t xOriginalLength; + uint8_t * pucBuffer; + + #ifdef PIC32_USE_ETHERNET + xOriginalLength = pxNetworkBuffer->xDataLength; + #else + xOriginalLength = pxNetworkBuffer->xDataLength + ipBUFFER_PADDING; + xNewSizeBytes = xNewSizeBytes + ipBUFFER_PADDING; + #endif /* #ifdef PIC32_USE_ETHERNET */ + + pucBuffer = pucGetNetworkBuffer( &( xNewSizeBytes ) ); + + if( pucBuffer == NULL ) + { + /* In case the allocation fails, return NULL. */ + pxNetworkBuffer = NULL; + } + else + { + pxNetworkBuffer->xDataLength = xNewSizeBytes; + + if( xNewSizeBytes > xOriginalLength ) + { + xNewSizeBytes = xOriginalLength; + } + + #ifdef PIC32_USE_ETHERNET + memcpy( pucBuffer, pxNetworkBuffer->pucEthernetBuffer, xNewSizeBytes ); + *( ( NetworkBufferDescriptor_t ** ) ( pucBuffer - ipBUFFER_PADDING ) ) = pxNetworkBuffer; + #else + memcpy( pucBuffer - ipBUFFER_PADDING, pxNetworkBuffer->pucEthernetBuffer - ipBUFFER_PADDING, xNewSizeBytes ); + #endif /* #ifdef PIC32_USE_ETHERNET */ + + vReleaseNetworkBuffer( pxNetworkBuffer->pucEthernetBuffer ); + pxNetworkBuffer->pucEthernetBuffer = pucBuffer; + } + + return pxNetworkBuffer; +} diff --git a/FreeRTOS/source/portable/NetworkInterface/pic32mzef/NetworkInterface_eth.c b/FreeRTOS/source/portable/NetworkInterface/pic32mzef/NetworkInterface_eth.c new file mode 100644 index 0000000..131b571 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/pic32mzef/NetworkInterface_eth.c @@ -0,0 +1,891 @@ +/******************************************************************************* +* Network Interface file +* +* Summary: +* Network Interface file for FreeRTOS-Plus-TCP stack +* +* Description: +* - Interfaces PIC32 to the FreeRTOS TCP/IP stack +*******************************************************************************/ + +/******************************************************************************* +* File Name: pic32_NetworkInterface.c +* Copyright 2017 Microchip Technology Incorporated and its subsidiaries. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of +* this software and associated documentation files (the "Software"), to deal in +* the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +* of the Software, and to permit persons to whom the Software is furnished to do +* so, subject to the following conditions: +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +*******************************************************************************/ +#include + +#include "FreeRTOS.h" +#include "semphr.h" +#include "event_groups.h" +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" + +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" + + +#include "NetworkInterface.h" +#include "NetworkConfig.h" + +#include "peripheral/eth/plib_eth.h" + +#include "system_config.h" +#include "system/console/sys_console.h" +#include "system/debug/sys_debug.h" +#include "system/command/sys_command.h" + +#include "driver/ethmac/drv_ethmac.h" +#include "driver/miim/drv_miim.h" + +#include "tcpip/tcpip.h" +#include "tcpip/src/tcpip_private.h" +#include "tcpip/src/link_list.h" + +#ifdef PIC32_USE_ETHERNET + +/* local definitions and data */ + +/* debug messages */ + #if ( PIC32_MAC_DEBUG_MESSAGES != 0 ) + #define PIC32_MAC_DbgPrint( format, ... ) SYS_CONSOLE_PRINT( format, ## __VA_ARGS__ ) + #else + #define PIC32_MAC_DbgPrint( format, ... ) + #endif /* (PIC32_MAC_DEBUG_MESSAGES != 0) */ + + typedef enum + { + PIC32_MAC_EVENT_INIT_NONE = 0x000, /* no event/invalid */ + + PIC32_MAC_EVENT_INIT_DONE = 0x001, /* initialization done event */ + PIC32_MAC_EVENT_TIMEOUT = 0x002, /* periodic timeout event */ + PIC32_MAC_EVENT_IF_PENDING = 0x004, /* an interface event signal: RX, TX, errors. etc. */ + } PIC32_MAC_EVENT_TYPE; + + typedef enum + { + eMACInit, /* Must initialise MAC. */ + eMACPass, /* Initialisation was successful. */ + eMACFailed, /* Initialisation failed. */ + } eMAC_INIT_STATUS_TYPE; + + static TCPIP_STACK_HEAP_HANDLE macHeapHandle; + + static const TCPIP_MAC_OBJECT * macObject; /* the one and only MAC object; */ + + static SYS_MODULE_OBJ macObjHandle; /* the MAC object instance, obtained at initialization */ + static TCPIP_MAC_HANDLE macCliHandle; /* client handle */ + static volatile SYS_STATUS macObjStatus; /* current MAC status */ + + static TaskHandle_t macTaskHandle; + + static TimerHandle_t macTmrHandle; + + static bool macLinkStatus; /* true if link is ON */ + + static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit; + +/* local prototypes */ + static bool StartInitMac( void ); + static void StartInitCleanup( void ); + + static void SetMacCtrl( TCPIP_MAC_MODULE_CTRL * pMacCtrl ); + + static bool MacSyncFunction( void * synchHandle, + TCPIP_MAC_SYNCH_REQUEST req ); + +/* the PIC32 MAC task function */ + static void MacHandlerTask( void * params ); + +/* MAC interrupt event function */ + static void MAC_EventFunction( TCPIP_MAC_EVENT event, + const void * eventParam ); + +/* timer callback for link maintenance, etc; */ + static void MacTmrCallback( TimerHandle_t xTimer ); + +/* MAC RX packets functions */ + static void MacRxPackets( void ); + static void MacProcessRxPacket( TCPIP_MAC_PACKET * pRxPkt ); + + +/* memory allocation mapping to FreeRTOS */ + static void * _malloc( size_t nBytes ) + { + return pvPortMalloc( nBytes ); + } + +/*-----------------------------------------------------------*/ + + static void * _calloc( size_t nElems, + size_t elemSize ) + { + size_t nBytes = nElems * elemSize; + + void * ptr = pvPortMalloc( nBytes ); + + if( ptr != 0 ) + { + memset( ptr, 0, nBytes ); + } + + return ptr; + } + +/*-----------------------------------------------------------*/ + + static void _free( void * pBuff ) + { + vPortFree( pBuff ); + } + +/* extern references */ +/* */ +/* use the configuration data from the system_init.c */ + extern const TCPIP_NETWORK_CONFIG TCPIP_HOSTS_CONFIGURATION[]; + +/* BufferAllocation_2.c:: packet allocation function */ + extern TCPIP_MAC_PACKET * PIC32_MacPacketAllocate( uint16_t pktLen, + uint16_t segLoadLen, + TCPIP_MAC_PACKET_FLAGS flags ); + + extern void PIC32_MacAssociate( TCPIP_MAC_PACKET * pRxPkt, + NetworkBufferDescriptor_t * pxBufferDescriptor, + size_t pktLength ); + extern void PIC32_MacPacketOrphan( TCPIP_MAC_PACKET * pPkt ); + +/* cannot use the system_init.c::tcpipHeapConfig because FreeRTOS does not have a calloc function! */ +/* we build it here! */ + +/* make sure we're running with external heap! Redirect to FreeRTOS. */ + #if !defined( TCPIP_STACK_USE_EXTERNAL_HEAP ) || defined( TCPIP_STACK_USE_INTERNAL_HEAP ) || defined( TCPIP_STACK_USE_INTERNAL_HEAP_POOL ) + #error "TCPIP_STACK_USE_EXTERNAL_HEAP should be defined for this project!" + #endif + + static const TCPIP_STACK_HEAP_EXTERNAL_CONFIG tcpipHeapConfig = + { + .heapType = TCPIP_STACK_HEAP_TYPE_EXTERNAL_HEAP, + .heapFlags = TCPIP_STACK_HEAP_FLAG_ALLOC_UNCACHED | TCPIP_STACK_HEAP_FLAG_NO_MTHREAD_SYNC, + .heapUsage = TCPIP_STACK_HEAP_USE_DEFAULT, + .malloc_fnc = _malloc, + .calloc_fnc = _calloc, + .free_fnc = _free, + }; + + #if ( PIC32_MAC_DEBUG_COMMANDS != 0 ) + static int _Command_MacInfo( SYS_CMD_DEVICE_NODE * pCmdIO, + int argc, + char ** argv ); + static int _Command_NetInfo( SYS_CMD_DEVICE_NODE * pCmdIO, + int argc, + char ** argv ); + static int _Command_Version( SYS_CMD_DEVICE_NODE * pCmdIO, + int argc, + char ** argv ); + + static const SYS_CMD_DESCRIPTOR macCmdTbl[] = + { + { "macinfo", _Command_MacInfo, ": Check MAC statistics" }, + { "netinfo", _Command_NetInfo, ": Net info" }, + { "version", _Command_Version, ": Version info" }, + }; + #endif /* (PIC32_MAC_DEBUG_COMMANDS != 0) */ + + +/* FreeRTOS implementation functions */ + BaseType_t xNetworkInterfaceInitialise( void ) + { + BaseType_t xResult; + + if( xMacInitStatus == eMACInit ) + { + /* This is the first time this function is called. */ + if( StartInitMac() != false ) + { + /* Indicate that the MAC initialisation succeeded. */ + xMacInitStatus = eMACPass; + } + else + { + xMacInitStatus = eMACFailed; + } + } + + if( xMacInitStatus == eMACPass ) + { + xResult = xGetPhyLinkStatus(); + } + else + { + xResult = pdFAIL; + } + + PIC32_MAC_DbgPrint( "xNetworkInterfaceInitialise: %d %d\r\n", ( int ) xMacInitStatus, ( int ) xResult ); + + return xResult; + } + + +/*-----------------------------------------------------------*/ + + BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t xReleaseAfterSend ) + { + TCPIP_MAC_RES macRes; + TCPIP_MAC_PACKET * pTxPkt; + + BaseType_t retRes = pdFALSE; + + + if( ( pxDescriptor != 0 ) && ( pxDescriptor->pucEthernetBuffer != 0 ) && ( pxDescriptor->xDataLength != 0 ) ) + { + TCPIP_MAC_PACKET ** ppkt = ( TCPIP_MAC_PACKET ** ) ( pxDescriptor->pucEthernetBuffer - PIC32_BUFFER_PKT_PTR_OSSET ); + configASSERT( ( ( uint32_t ) ppkt & ( sizeof( uint32_t ) - 1 ) ) == 0 ); + pTxPkt = *ppkt; + configASSERT( pTxPkt != 0 ); + + /* prepare the packet for transmission */ + /* set the correct data length: */ + configASSERT( pTxPkt->pDSeg->segSize >= pTxPkt->pDSeg->segLen ); + pTxPkt->pDSeg->segLen = pxDescriptor->xDataLength; + pTxPkt->next = 0; /* unlink it */ + macRes = ( macObject->TCPIP_MAC_PacketTx )( macCliHandle, pTxPkt ); + + if( macRes >= 0 ) + { + retRes = pdTRUE; + pxDescriptor->pucEthernetBuffer = 0; /* it will be released by the MAC driver once it's transmitted */ + iptraceNETWORK_INTERFACE_TRANSMIT(); + } + + /* else same error occurred; this normally should not happen! But the buffer is left in there so it should be freed! */ + + /* The buffer has been sent so can be released. */ + if( xReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + } + + return retRes; + } + + +/************************************* Section: helper functions ************************************************** */ +/* */ + + void PIC32_GetMACAddress( uint8_t macAdd[ 6 ] ) + { + #if defined( __PIC32MZ__ ) || defined( __PIC32MX__ ) + PLIB_ETH_MACGetAddress( ETH_ID_0, macAdd ); + #else + #error "MAC Address: not supported architecture!" + #endif + } + + +/*-----------------------------------------------------------*/ + + const void * const PIC32_GetMacConfigData( void ) + { + #if defined( __PIC32MZ__ ) || defined( __PIC32MX__ ) + extern const TCPIP_MODULE_MAC_PIC32INT_CONFIG tcpipMACPIC32INTInitData; + + return &tcpipMACPIC32INTInitData; + #else + #error "MAC Address: not supported architecture!" + #endif + } + +/************************************* Section: worker code ************************************************** */ +/* */ + + + static bool StartInitMac( void ) + { + TCPIP_MAC_MODULE_CTRL macCtrl; + SYS_MODULE_INIT moduleInit; + EventBits_t evBits; + + + /* perform some initialization of all variables so that we can cleanup what failed */ + /* if something failed, the routine will be called again and again by FreeRTOS! */ + macHeapHandle = 0; + macObjHandle = 0; + macCliHandle = 0; + macTmrHandle = 0; + macTaskHandle = 0; + macObject = TCPIP_HOSTS_CONFIGURATION[ 0 ].pMacObject; /* the MAC object we use */ + macObjStatus = SYS_STATUS_UNINITIALIZED; + macLinkStatus = false; + + int netUpFail = 0; + + while( true ) + { + /* start the allocator */ + macHeapHandle = TCPIP_HEAP_Create( ( const TCPIP_STACK_HEAP_CONFIG * ) &tcpipHeapConfig, 0 ); + + if( macHeapHandle == 0 ) + { + netUpFail = 1; + break; + } + + if( TCPIP_PKT_Initialize( macHeapHandle, 0, 0 ) == false ) + { + netUpFail = 2; + break; + } + + moduleInit.sys.powerState = SYS_MODULE_POWER_RUN_FULL; + + /* Initialize the MAC. MAC address is defined to 0x000000000000 in + * FreeRTOSConfig.h and therefore it will be initialized to the + * factory programmed MAC address. */ + SetMacCtrl( &macCtrl ); + /* Set the mac address in the FreeRTOS+TCP stack. */ + FreeRTOS_UpdateMACAddress( macCtrl.ifPhyAddress.v ); + + TCPIP_MAC_INIT macInit = + { + .moduleInit = { moduleInit.value }, + .macControl = &macCtrl, + .moduleData = PIC32_GetMacConfigData(), + }; + + macObjHandle = ( macObject->TCPIP_MAC_Initialize )( TCPIP_MODULE_MAC_PIC32INT, &macInit.moduleInit ); + + if( macObjHandle == SYS_MODULE_OBJ_INVALID ) + { + macObjHandle = 0; + netUpFail = 4; + break; + } + + /* open the MAC */ + macCliHandle = ( macObject->TCPIP_MAC_Open )( TCPIP_MODULE_MAC_PIC32INT, DRV_IO_INTENT_READWRITE ); + + if( macCliHandle == DRV_HANDLE_INVALID ) + { + macCliHandle = 0; + netUpFail = 5; + break; + } + + if( !( macObject->TCPIP_MAC_EventMaskSet )( macCliHandle, ( TCPIP_MAC_EV_RX_DONE | TCPIP_MAC_EV_TX_DONE | TCPIP_MAC_EV_RXTX_ERRORS ), true ) ) + { + netUpFail = 6; + break; + } + + /* completed the MAC initialization */ + /* continue the initialization */ + macTmrHandle = xTimerCreate( PIC32_MAC_TIMER_NAME, PIC32_MAC_TIMER_PERIOD, pdTRUE, 0, MacTmrCallback ); + + if( ( macTmrHandle == 0 ) || ( xTimerStart( macTmrHandle, 0 ) != pdPASS ) ) + { + netUpFail = 8; + break; + } + + /* spawn the PIC32 MAC task function */ + /* and wait for its event signal */ + macObjStatus = SYS_STATUS_BUSY; + + if( xTaskCreate( MacHandlerTask, PIC32_MAC_TASK_NAME, PIC32_MAC_TASK_STACK_SIZE, xTaskGetCurrentTaskHandle(), PIC32_MAC_TASK_PRI, &macTaskHandle ) != pdPASS ) + { /* failed */ + netUpFail = 9; + break; + } + + xTaskNotifyWait( PIC32_MAC_EVENT_INIT_DONE, PIC32_MAC_EVENT_INIT_DONE, &evBits, PIC32_MAC_INIT_TIMEOUT ); + + if( ( evBits & PIC32_MAC_EVENT_INIT_DONE ) == 0 ) + { /* timed out */ + netUpFail = 10; + break; + } + + if( macObjStatus != SYS_STATUS_READY ) + { /* failed somehow ??? */ + netUpFail = 11; + break; + } + + netUpFail = 0; + break; + } + + if( netUpFail == 0 ) + { + PIC32_MAC_DbgPrint( " MAC Init success!\r\n" ); + + #if ( PIC32_MAC_DEBUG_COMMANDS != 0 ) + /* create command group */ + if( !SYS_CMD_ADDGRP( macCmdTbl, sizeof( macCmdTbl ) / sizeof( *macCmdTbl ), "mac", ": mac commands" ) ) + { + PIC32_MAC_DbgPrint( "Failed to create MAC Commands\r\n" ); + } + #endif /* (PIC32_MAC_DEBUG_COMMANDS != 0) */ + + return true; + } + else + { + StartInitCleanup(); + PIC32_MAC_DbgPrint( "MAC Init failed: %d!\r\n", netUpFail ); + + return false; + } + } + +/*-----------------------------------------------------------*/ + + static void StartInitCleanup( void ) + { + if( macHeapHandle != 0 ) + { + TCPIP_HEAP_Delete( macHeapHandle ); + macHeapHandle = 0; + } + + if( macObjHandle != 0 ) + { + ( macObject->TCPIP_MAC_Deinitialize )( macObjHandle ); + macObjHandle = 0; + } + + if( macTmrHandle != 0 ) + { + xTimerDelete( macTmrHandle, portMAX_DELAY ); + macTmrHandle = 0; + } + + if( macTaskHandle != 0 ) + { + vTaskDelete( macTaskHandle ); + macTaskHandle = 0; + } + } + +/*-----------------------------------------------------------*/ + + static void SetMacCtrl( TCPIP_MAC_MODULE_CTRL * pMacCtrl ) + { + TCPIP_MAC_ADDR macAdd; + uint8_t unsetMACAddr[ 6 ] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* not set MAC address */ + + pMacCtrl->nIfs = 1; + + pMacCtrl->mallocF = TCPIP_HEAP_MallocOutline; + pMacCtrl->callocF = TCPIP_HEAP_CallocOutline; + pMacCtrl->freeF = TCPIP_HEAP_FreeOutline; + pMacCtrl->memH = macHeapHandle; + + + pMacCtrl->pktAllocF = PIC32_MacPacketAllocate; + pMacCtrl->pktFreeF = ( TCPIP_MAC_PKT_FreeF ) _TCPIP_PKT_FREE_FNC; + pMacCtrl->pktAckF = ( TCPIP_MAC_PKT_AckF ) _TCPIP_PKT_ACK_FNC; + + pMacCtrl->synchF = MacSyncFunction; + + pMacCtrl->eventF = MAC_EventFunction; + pMacCtrl->eventParam = 0; + + pMacCtrl->moduleId = TCPIP_MODULE_MAC_PIC32INT; + pMacCtrl->netIx = 0; + pMacCtrl->macAction = TCPIP_MAC_ACTION_INIT; + pMacCtrl->powerMode = TCPIP_MAC_POWER_FULL; + + macAdd.v[ 0 ] = configMAC_ADDR0; + macAdd.v[ 1 ] = configMAC_ADDR1; + macAdd.v[ 2 ] = configMAC_ADDR2; + macAdd.v[ 3 ] = configMAC_ADDR3; + macAdd.v[ 4 ] = configMAC_ADDR4; + macAdd.v[ 5 ] = configMAC_ADDR5; + + if( memcmp( macAdd.v, unsetMACAddr, sizeof( unsetMACAddr ) ) == 0 ) + { /* if unspecified we use the factory pre-programmed address */ + PIC32_GetMACAddress( pMacCtrl->ifPhyAddress.v ); + } + else + { /* use the config suggested one */ + memcpy( pMacCtrl->ifPhyAddress.v, macAdd.v, sizeof( macAdd ) ); + } + } + +/*-----------------------------------------------------------*/ + + static bool MacSyncFunction( void * synchHandle, + TCPIP_MAC_SYNCH_REQUEST req ) + { + switch( req ) + { + case TCPIP_MAC_SYNCH_REQUEST_OBJ_CREATE: + vSemaphoreCreateBinary( *( SemaphoreHandle_t * ) synchHandle ); + + return ( *( SemaphoreHandle_t * ) synchHandle == NULL ) ? false : true; + + case TCPIP_MAC_SYNCH_REQUEST_OBJ_DELETE: + vSemaphoreDelete( *( SemaphoreHandle_t * ) synchHandle ); + *( SemaphoreHandle_t * ) synchHandle = NULL; + + return true; + + case TCPIP_MAC_SYNCH_REQUEST_OBJ_LOCK: + + return ( xSemaphoreTake( *( SemaphoreHandle_t * ) synchHandle, portMAX_DELAY ) == pdTRUE ) ? true : false; + + case TCPIP_MAC_SYNCH_REQUEST_OBJ_UNLOCK: + + return ( xSemaphoreGive( *( SemaphoreHandle_t * ) synchHandle ) == pdTRUE ) ? true : false; + + case TCPIP_MAC_SYNCH_REQUEST_CRIT_ENTER: + vTaskSuspendAll(); + + return true; + + case TCPIP_MAC_SYNCH_REQUEST_CRIT_LEAVE: + xTaskResumeAll(); + + return true; + + default: + + return false; + } + } + + +/*-----------------------------------------------------------*/ + + static void MacHandlerTask( void * params ) + { + EventBits_t evBits; + + /* perform the MAC initialization */ + while( macObjStatus == SYS_STATUS_BUSY ) + { + /* process the underlying MAC module tasks */ + ( macObject->TCPIP_MAC_Tasks )( macObjHandle ); + + SYS_STATUS macStatus = ( macObject->TCPIP_MAC_Status )( macObjHandle ); + + if( macStatus == SYS_STATUS_BUSY ) + { /* still pending */ + vTaskDelay( PIC32_MAC_TASK_INIT_PENDING_DELAY ); + } + else + { /* completed ...somehow */ + macObjStatus = macStatus; + + xTaskNotify( ( TaskHandle_t ) params, PIC32_MAC_EVENT_INIT_DONE, eSetBits ); + + if( macStatus != SYS_STATUS_READY ) + { /* failed miserably */ + vTaskDelete( 0 ); + } + + /* done, up and running */ + } + } + + while( true ) + { + xTaskNotifyWait( PIC32_MAC_EVENT_TIMEOUT | PIC32_MAC_EVENT_IF_PENDING, PIC32_MAC_EVENT_TIMEOUT | PIC32_MAC_EVENT_IF_PENDING, &evBits, portMAX_DELAY ); + + if( ( evBits & PIC32_MAC_EVENT_TIMEOUT ) != 0 ) + { /* timeout occurred... */ + ( macObject->TCPIP_MAC_Tasks )( macObjHandle ); + bool linkCurr = ( macObject->TCPIP_MAC_LinkCheck )( macCliHandle ); /* check link status */ + + if( macLinkStatus != linkCurr ) + { /* link status changed; some event could ve fired here if needed */ + PIC32_MAC_DbgPrint( " MAC link: %s!\r\n", linkCurr ? "ON" : "OFF" ); + macLinkStatus = linkCurr; + } + } + + if( ( evBits & PIC32_MAC_EVENT_IF_PENDING ) != 0 ) + { /* IF events signal */ + TCPIP_MAC_EVENT activeEvents = ( macObject->TCPIP_MAC_EventPendingGet )( macCliHandle ); + + if( activeEvents != TCPIP_MAC_EV_NONE ) + { + /* acknowledge the events */ + ( macObject->TCPIP_MAC_EventAcknowledge )( macCliHandle, activeEvents ); + + /* check for RX */ + if( ( activeEvents & ( TCPIP_MAC_EV_RX_DONE | TCPIP_MAC_EV_RX_OVFLOW | TCPIP_MAC_EV_RX_BUFNA ) ) != 0 ) + { /* RX packets available */ + MacRxPackets(); + } + + /* call the driver process function; */ + /* PIC32 driver requests it through TCPIP_MAC_ParametersGet() which is bypassed here! */ + ( macObject->TCPIP_MAC_Process )( macCliHandle ); + } + } + + /* do what you have to do and then wait for another event... */ + } + } + +/*-----------------------------------------------------------*/ + + static void MacTmrCallback( TimerHandle_t xTimer ) + { + xTaskNotify( macTaskHandle, PIC32_MAC_EVENT_TIMEOUT, eSetBits ); + } + +/* MAC interrupt event function */ +/* MAC signals an event, probably from within ISR */ +/* we care just for RX related events */ + static void MAC_EventFunction( TCPIP_MAC_EVENT event, + const void * eventParam ) + { + BaseType_t xHigherPriorityTaskWoken; + + if( ( event & ( TCPIP_MAC_EV_RX_DONE | TCPIP_MAC_EV_TX_DONE | TCPIP_MAC_EV_RXTX_ERRORS ) ) != 0 ) + { + xHigherPriorityTaskWoken = pdFALSE; + xTaskNotifyFromISR( macTaskHandle, PIC32_MAC_EVENT_IF_PENDING, eSetBits, &xHigherPriorityTaskWoken ); + + if( xHigherPriorityTaskWoken ) + { + portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); + } + } + } + +/*-----------------------------------------------------------*/ + + BaseType_t xGetPhyLinkStatus( void ) + { + return macLinkStatus == true ? pdPASS : pdFAIL; + } + + +/* receive packets from the MAC driver */ + static void MacRxPackets( void ) + { + TCPIP_MAC_PACKET * pRxPkt; + + /* get all the new MAC packets */ + while( ( pRxPkt = ( macObject->TCPIP_MAC_PacketRx )( macCliHandle, 0, 0 ) ) != 0 ) + { + MacProcessRxPacket( pRxPkt ); + } + } + +/*-----------------------------------------------------------*/ + + static void MacProcessRxPacket( TCPIP_MAC_PACKET * pRxPkt ) + { + bool pktSuccess, pktLost; + size_t pktLength; + TCPIP_MAC_DATA_SEGMENT * pSeg; + uint8_t * pPktBuff; + NetworkBufferDescriptor_t * pxBufferDescriptor; + IPStackEvent_t xRxEvent; + + pxBufferDescriptor = 0; + pktSuccess = pktLost = false; + + while( true ) + { + pktLength = 0; + int nSegs = 0; + pSeg = pRxPkt->pDSeg; + pPktBuff = pSeg->segLoad; + + /* calculate the packet size */ + do + { + pktLength += pSeg->segLen; + pSeg = pSeg->next; + nSegs++; + } while( pSeg != 0 ); + + if( nSegs > 1 ) + { /* no support in FreeRTOS for multi segment packets! */ + break; + } + + /* sizeof(TCPIP_MAC_ETHERNET_HEADER) is subtracted by the driver */ + /* but FreeRTOS needs the whole frame! */ + pktLength += sizeof( TCPIP_MAC_ETHERNET_HEADER ); + + if( eConsiderFrameForProcessing( pPktBuff ) != eProcessBuffer ) + { + break; + } + + /* get the network descriptor (no data buffer) to hold this packet */ + pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( 0, 0 ); + + if( pxBufferDescriptor == 0 ) + { + pktLost = true; + break; + } + + PIC32_MacAssociate( pRxPkt, pxBufferDescriptor, pktLength ); + + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = ( void * ) pxBufferDescriptor; + + /* Send the data to the TCP/IP stack */ + if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) + { /* failed */ + pktLost = true; + } + else + { /* success */ + pktSuccess = true; + iptraceNETWORK_INTERFACE_RECEIVE(); + } + + break; + } + + if( !pktSuccess ) + { /* something went wrong; nothing sent to the */ + if( pxBufferDescriptor != 0 ) + { + pxBufferDescriptor->pucEthernetBuffer = 0; + vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); + } + + if( pktLost ) + { + iptraceETHERNET_RX_EVENT_LOST(); + } + + /* acknowledge the packet to the MAC driver */ + if( pRxPkt->ackFunc ) + { + ( *pRxPkt->ackFunc )( pRxPkt, pRxPkt->ackParam ); + } + else + { + PIC32_MacPacketOrphan( pRxPkt ); + } + } + } + + #if ( PIC32_MAC_DEBUG_COMMANDS != 0 ) + /* */ + static int _Command_MacInfo( SYS_CMD_DEVICE_NODE * pCmdIO, + int argc, + char ** argv ) + { + TCPIP_MAC_RES macRes; + TCPIP_MAC_RX_STATISTICS rxStatistics; + TCPIP_MAC_TX_STATISTICS txStatistics; + TCPIP_MAC_STATISTICS_REG_ENTRY regEntries[ 8 ]; + TCPIP_MAC_STATISTICS_REG_ENTRY * pRegEntry; + int jx, hwEntries; + char entryName[ sizeof( pRegEntry->registerName ) + 1 ]; + + const void * cmdIoParam = pCmdIO->cmdIoParam; + + if( argc != 1 ) + { + ( *pCmdIO->pCmdApi->msg )( cmdIoParam, "Usage: macinfo \r\n" ); + ( *pCmdIO->pCmdApi->msg )( cmdIoParam, "Ex: macinfo \r\n" ); + + return false; + } + + ( *pCmdIO->pCmdApi->print )( cmdIoParam, "Interface: %s driver statistics\r\n", macObject->macName ); + macRes = ( macObject->TCPIP_MAC_StatisticsGet )( macCliHandle, &rxStatistics, &txStatistics ); + + if( macRes == TCPIP_MAC_RES_OK ) + { + ( *pCmdIO->pCmdApi->print )( cmdIoParam, "\tnRxOkPackets: %d, nRxPendBuffers: %d, nRxSchedBuffers: %d, ", + rxStatistics.nRxOkPackets, rxStatistics.nRxPendBuffers, rxStatistics.nRxSchedBuffers ); + ( *pCmdIO->pCmdApi->print )( cmdIoParam, "nRxErrorPackets: %d, nRxFragmentErrors: %d\r\n", rxStatistics.nRxErrorPackets, rxStatistics.nRxFragmentErrors ); + ( *pCmdIO->pCmdApi->print )( cmdIoParam, "\tnTxOkPackets: %d, nTxPendBuffers: %d, nTxErrorPackets: %d, nTxQueueFull: %d\r\n", + txStatistics.nTxOkPackets, txStatistics.nTxPendBuffers, txStatistics.nTxErrorPackets, txStatistics.nTxQueueFull ); + } + else + { + ( *pCmdIO->pCmdApi->msg )( cmdIoParam, "\tnot supported\r\n" ); + } + + ( *pCmdIO->pCmdApi->print )( cmdIoParam, "Interface: %s hardware statistics\r\n", macObject->macName ); + macRes = ( macObject->TCPIP_MAC_RegisterStatisticsGet )( macCliHandle, regEntries, sizeof( regEntries ) / sizeof( *regEntries ), &hwEntries ); + + if( macRes == TCPIP_MAC_RES_OK ) + { + entryName[ sizeof( entryName ) - 1 ] = 0; + + for( jx = 0, pRegEntry = regEntries; jx < hwEntries && jx < sizeof( regEntries ) / sizeof( *regEntries ); jx++, pRegEntry++ ) + { + strncpy( entryName, pRegEntry->registerName, sizeof( entryName ) - 1 ); + ( *pCmdIO->pCmdApi->print )( cmdIoParam, "\t%s: 0x%8x\r\n", entryName, pRegEntry->registerValue ); + } + } + else + { + ( *pCmdIO->pCmdApi->msg )( cmdIoParam, "\tnot supported\r\n" ); + } + + return true; + } + +/*-----------------------------------------------------------*/ + + static int _Command_NetInfo( SYS_CMD_DEVICE_NODE * pCmdIO, + int argc, + char ** argv ) + { + const void * cmdIoParam = pCmdIO->cmdIoParam; + + union + { + uint32_t ul; + uint8_t b[ 4 ]; + } + sUl; + + sUl.ul = FreeRTOS_GetIPAddress(); + + bool linkUp = FreeRTOS_IsNetworkUp() == pdTRUE; + + ( *pCmdIO->pCmdApi->print )( cmdIoParam, "IP address: %d.%d.%d.%d\r\n", sUl.b[ 0 ], sUl.b[ 1 ], sUl.b[ 2 ], sUl.b[ 3 ] ); + ( *pCmdIO->pCmdApi->print )( cmdIoParam, "Link is: %s\r\n", linkUp ? "Up" : "Down" ); + + return true; + } + + #include "aws_application_version.h" + + static int _Command_Version( SYS_CMD_DEVICE_NODE * pCmdIO, + int argc, + char ** argv ) + { + configPRINTF( ( "App version - maj: %d, min: %d, build: %d\r\n", xAppFirmwareVersion.u.x.ucMajor, xAppFirmwareVersion.u.x.ucMinor, xAppFirmwareVersion.u.x.usBuild ) ); + return 0; + } + + #endif /* (PIC32_MAC_DEBUG_COMMANDS != 0) */ +#endif /* #ifdef PIC32_USE_ETHERNET */ diff --git a/FreeRTOS/source/portable/NetworkInterface/pic32mzef/NetworkInterface_wifi.c b/FreeRTOS/source/portable/NetworkInterface/pic32mzef/NetworkInterface_wifi.c new file mode 100644 index 0000000..2c64233 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/pic32mzef/NetworkInterface_wifi.c @@ -0,0 +1,200 @@ +/******************************************************************************* +* Network Interface file +* +* Summary: +* Network Interface file for FreeRTOS-Plus-TCP stack +* +* Description: +* - Interfaces PIC32 to the FreeRTOS TCP/IP stack +*******************************************************************************/ + +/******************************************************************************* +* File Name: pic32_NetworkInterface.c +* Copyright 2017 Microchip Technology Incorporated and its subsidiaries. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of +* this software and associated documentation files (the "Software"), to deal in +* the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +* of the Software, and to permit persons to whom the Software is furnished to do +* so, subject to the following conditions: +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +*******************************************************************************/ +#ifndef PIC32_USE_ETHERNET +#include + +#include "FreeRTOS.h" +#include "semphr.h" +#include "event_groups.h" +#include "FreeRTOS_IP.h" +#include "FreeRTOS_IP_Private.h" + +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "peripheral/eth/plib_eth.h" + +#include "system_config.h" +#include "system/console/sys_console.h" +#include "system/debug/sys_debug.h" +#include "system/command/sys_command.h" + +#include "driver/ethmac/drv_ethmac.h" +#include "driver/miim/drv_miim.h" +#include "m2m_types.h" + +#include "tcpip/tcpip.h" +#include "tcpip/src/tcpip_private.h" +#include "tcpip/src/link_list.h" +#include "wilc1000_task.h" + +#include "NetworkConfig.h" + + +#include "iot_wifi.h" + +/* local definitions and data */ + + +/* FreeRTOS implementation functions */ +BaseType_t xNetworkInterfaceInitialise( void ) +{ + WIFINetworkParams_t xNetworkParams; + + xNetworkParams.ucSSIDLength = strnlen( clientcredentialWIFI_SSID, + wificonfigMAX_SSID_LEN ); + memcpy( xNetworkParams.ucSSID, + clientcredentialWIFI_SSID, + xNetworkParams.ucSSIDLength ); + + xNetworkParams.xPassword.xWPA.ucLength = strnlen( clientcredentialWIFI_PASSWORD, + wificonfigMAX_PASSPHRASE_LEN ); + memcpy( xNetworkParams.xPassword.xWPA.cPassphrase, + clientcredentialWIFI_PASSWORD, + xNetworkParams.xPassword.xWPA.ucLength ); + + xNetworkParams.xSecurity = clientcredentialWIFI_SECURITY; + xNetworkParams.ucChannel = M2M_WIFI_CH_ALL; /* Scan all channels (255) */ + + /*Turn WiFi ON */ + if( WIFI_On() != eWiFiSuccess ) + { + return pdFAIL; + } + + /* Connect to the AP */ + if( WIFI_ConnectAP( &xNetworkParams ) != eWiFiSuccess ) + { + return pdFAIL; + } + + return pdPASS; +} + + +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t xReleaseAfterSend ) +{ + BaseType_t retRes = pdFALSE; + + if( ( pxDescriptor != 0 ) && ( pxDescriptor->pucEthernetBuffer != 0 ) && ( pxDescriptor->xDataLength != 0 ) ) + { + /* There you go */ + if( WDRV_EXT_DataSend( pxDescriptor->xDataLength, pxDescriptor->pucEthernetBuffer ) == 0 ) + { + retRes = pdTRUE; + } + + /* The buffer has been sent so can be released. */ + if( xReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + } + + return retRes; +} + + +/************************************* Section: helper functions ************************************************** */ +/* */ + + + +/************************************* Section: worker code ************************************************** */ +/* */ + +void xNetworkFrameReceived( uint32_t len, + uint8_t const * const frame ) +{ + bool pktSuccess, pktLost; + NetworkBufferDescriptor_t * pxNetworkBuffer = NULL; + IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + + pktSuccess = pktLost = false; + + while( true ) + { + if( eConsiderFrameForProcessing( frame ) != eProcessBuffer ) + { + break; + } + + /* get the network descriptor (no data buffer) to hold this packet */ + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( len, 0 ); + + if( pxNetworkBuffer == NULL ) + { + pktLost = true; + break; + } + + /* Set the actual packet length, in case a larger buffer was + * returned. */ + pxNetworkBuffer->xDataLength = len; + + /* Copy the packet. */ + memcpy( pxNetworkBuffer->pucEthernetBuffer, frame, len ); + + /* Send the data to the TCP/IP stack. */ + xRxEvent.pvData = ( void * ) pxNetworkBuffer; + + if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) + { /* failed */ + pktLost = true; + } + else + { /* success */ + pktSuccess = true; + iptraceNETWORK_INTERFACE_RECEIVE(); + } + + break; + } + + if( !pktSuccess ) + { /* something went wrong; nothing sent to the */ + if( pxNetworkBuffer != NULL ) + { + pxNetworkBuffer->pucEthernetBuffer = 0; + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + if( pktLost ) + { + iptraceETHERNET_RX_EVENT_LOST(); + } + } +} + +#endif /* #ifndef PIC32_USE_ETHERNET */ diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/NetworkInterface.c b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/NetworkInterface.c new file mode 100644 index 0000000..cbe5e3e --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/NetworkInterface.c @@ -0,0 +1,549 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +/* Xilinx library files. */ +#include +#include "x_topology.h" +#include "x_emacpsif.h" +#include "x_emacpsif_hw.h" + +/* Provided memory configured as uncached. */ +#include "uncached_memory.h" + +#ifndef niEMAC_HANDLER_TASK_PRIORITY + /* Define the priority of the task prvEMACHandlerTask(). */ + #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1 +#endif + +#define niBMSR_LINK_STATUS 0x0004uL + +#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 +#endif + +#if ( ipconfigNETWORK_MTU > 1526 ) + #warning the use of Jumbo Frames has not been tested sufficiently yet. + #define USE_JUMBO_FRAMES 1 +#endif + +/* The size of each buffer when BufferAllocation_1 is used: + * http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html */ +#if ( USE_JUMBO_FRAMES == 1 ) + #define niBUFFER_1_PACKET_SIZE 10240 +#else + #define niBUFFER_1_PACKET_SIZE 1536 +#endif + +/* Naming and numbering of PHY registers. */ +#define PHY_REG_01_BMSR 0x01 /* Basic mode status register */ + +#ifndef iptraceEMAC_TASK_STARTING + #define iptraceEMAC_TASK_STARTING() do {} while( 0 ) +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to twice + * the size of the stack used by the idle task - but allow this to be overridden in + * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 8 * configMINIMAL_STACK_SIZE ) +#endif + +#if ( ipconfigZERO_COPY_RX_DRIVER == 0 || ipconfigZERO_COPY_TX_DRIVER == 0 ) + #error Please define both 'ipconfigZERO_COPY_RX_DRIVER' and 'ipconfigZERO_COPY_TX_DRIVER' as 1 +#endif + +#if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 || ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) + #warning Please define both 'ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM' and 'ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM' as 1 +#endif + +#ifndef nicUSE_UNCACHED_MEMORY + +/* + * Don't use cached memory for network buffer, which is more efficient than + * using cached memory. + */ + #define nicUSE_UNCACHED_MEMORY 0 +#endif + +/*-----------------------------------------------------------*/ + +/* + * Look for the link to be up every few milliseconds until + * xMaxTimeTicks has passed or a link is found. + */ +static BaseType_t prvGMACWaitLS( TickType_t xMaxTimeTicks ); + +/* + * A deferred interrupt handler for all MAC/DMA interrupt sources. + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/*-----------------------------------------------------------*/ + +/* EMAC data/descriptions. */ +static xemacpsif_s xEMACpsif; +struct xtopology_t xXTopology = +{ + .emac_baseaddr = XPAR_XEMACPS_0_BASEADDR, + .emac_type = xemac_type_emacps, + .intc_baseaddr = 0x0, + .intc_emac_intr = 0x0, + .scugic_baseaddr = XPAR_SCUGIC_0_CPU_BASEADDR, + .scugic_emac_intr = XPAR_XEMACPS_3_INTR, +}; + +XEmacPs_Config mac_config = +{ + .DeviceId = XPAR_PSU_ETHERNET_3_DEVICE_ID, /**< Unique ID of device */ + .BaseAddress = XPAR_PSU_ETHERNET_3_BASEADDR, /**< Physical base address of IPIF registers */ + .IsCacheCoherent = XPAR_PSU_ETHERNET_3_IS_CACHE_COHERENT +}; + +/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ +static uint32_t ulPHYLinkStatus = 0uL; + +#if ( ipconfigUSE_LLMNR == 1 ) + static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; +#endif + +/* Holds the handle of the task used as a deferred interrupt processor. The + * handle is used so direct notifications can be sent to the task for all EMAC/DMA + * related interrupts. */ +TaskHandle_t xEMACTaskHandle = NULL; + +/* The PHY index where a PHY was found. */ +static u32 ulPHYIndex; + +/*-----------------------------------------------------------*/ + +/* The function xNetworkInterfaceInitialise() will be called as + * long as it returns the value pdFAIL. + * It will go through several stages as described in 'eEMACState'. + */ +typedef enum xEMAC_STATE +{ + xEMAC_Init, + xEMAC_SetupPHY, + xEMAC_WaitPHY, + xEMAC_Ready, + xEMAC_Fatal, +} EMACState_t; + +static EMACState_t eEMACState = xEMAC_Init; + +BaseType_t xNetworkInterfaceInitialise( void ) +{ + uint32_t ulLinkSpeed, ulDMAReg; + BaseType_t xStatus, xReturn = pdFAIL; + XEmacPs * pxEMAC_PS = &( xEMACpsif.emacps ); + const TickType_t xWaitLinkDelay = pdMS_TO_TICKS( 1000U ); + + switch( eEMACState ) + { + case xEMAC_Init: + + ulPHYLinkStatus = 0U; + memset( &xEMACpsif, '\0', sizeof( xEMACpsif ) ); + + xStatus = XEmacPs_CfgInitialize( pxEMAC_PS, &mac_config, mac_config.BaseAddress ); + + if( xStatus != XST_SUCCESS ) + { + FreeRTOS_printf( ( "xEMACInit: EmacPs Configuration Failed....\n" ) ); + eEMACState = xEMAC_Fatal; + break; + } + +/* _HT_ : the use of jumbo frames has not been tested sufficiently yet. */ + + if( pxEMAC_PS->Version > 2 ) + { + #if ( USE_JUMBO_FRAMES == 1 ) + /* Enable jumbo frames for zynqmp */ + XEmacPs_SetOptions( pxEMAC_PS, XEMACPS_JUMBO_ENABLE_OPTION ); + #endif + } + + /* Initialize the mac and set the MAC address. */ + XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) ipLOCAL_MAC_ADDRESS, 1 ); + + #if ( ipconfigUSE_LLMNR == 1 ) + { + /* Also add LLMNR multicast MAC address. */ + XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) xLLMNR_MACAddress, 2 ); + } + #endif /* ipconfigUSE_LLMNR == 1 */ + + XEmacPs_SetMdioDivisor( pxEMAC_PS, MDC_DIV_224 ); + ulPHYIndex = ulDetecPHY( pxEMAC_PS ); + + if( ulPHYIndex == ~0U ) + { + FreeRTOS_printf( ( "xEMACInit: No valid PHY was found\n" ) ); + eEMACState = xEMAC_Fatal; + break; + } + + eEMACState = xEMAC_SetupPHY; + + /* Fall through. */ + + case xEMAC_SetupPHY: + ulLinkSpeed = Phy_Setup_US( pxEMAC_PS, ulPHYIndex ); + + if( ulLinkSpeed == XST_FAILURE ) + { + /* The speed could not be determined yet. + * This is not a fatal error. + * xNetworkInterfaceInitialise() will be called again + * by the IP-task. + */ + break; + } + + XEmacPs_SetOperatingSpeed( pxEMAC_PS, ulLinkSpeed ); + + /* Setting the operating speed of the MAC needs a delay. */ + vTaskDelay( pdMS_TO_TICKS( 25UL ) ); + + ulDMAReg = XEmacPs_ReadReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET ); + /* Enable 16-bytes AHB bursts */ + ulDMAReg = ulDMAReg | XEMACPS_DMACR_INCR16_AHB_BURST; + + /* DISC_WHEN_NO_AHB: when set, the GEM DMA will automatically discard receive + * packets from the receiver packet buffer memory when no AHB resource is available. */ + XEmacPs_WriteReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET, + ulDMAReg /*| XEMACPS_DMACR_DISC_WHEN_NO_AHB_MASK*/ ); + + setup_isr( &xEMACpsif ); + init_dma( &xEMACpsif ); + start_emacps( &xEMACpsif ); + eEMACState = xEMAC_WaitPHY; + + /* Fall through. */ + + case xEMAC_WaitPHY: + prvGMACWaitLS( xWaitLinkDelay ); + + if( xGetPhyLinkStatus() == pdFALSE ) + { + /* The Link Status is not yet high, Stay in 'xEMAC_WaitPHY'. */ + break; + } + + if( xEMACTaskHandle == NULL ) + { + /* The deferred interrupt handler task is created at the highest + * possible priority to ensure the interrupt handler can return directly + * to it. The task's handle is stored in xEMACTaskHandle so interrupts can + * notify the task when there is something to process. */ + xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle ); + + if( xEMACTaskHandle == NULL ) + { + eEMACState = xEMAC_Fatal; + break; + } + } + + eEMACState = xEMAC_Ready; + + /* Fall through. */ + + case xEMAC_Ready: + /* The network driver is operational. */ + xReturn = pdPASS; + break; + + case xEMAC_Fatal: + + /* A fatal error has occurred, and the driver + * can not start. */ + break; + } /* switch( eEMACState ) */ + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxBuffer, + BaseType_t bReleaseAfterSend ) +{ + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + { + ProtocolPacket_t * pxPacket; + + /* If the peripheral must calculate the checksum, it wants + * the protocol checksum to have a value of zero. */ + pxPacket = ( ProtocolPacket_t * ) ( pxBuffer->pucEthernetBuffer ); + + if( ( pxPacket->xICMPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) && + ( pxPacket->xICMPPacket.xIPHeader.ucProtocol != ipPROTOCOL_UDP ) && + ( pxPacket->xICMPPacket.xIPHeader.ucProtocol != ipPROTOCOL_TCP ) ) + { + /* The EMAC will calculate the checksum of the IP-header. + * It can only calculate protocol checksums of UDP and TCP, + * so for ICMP and other protocols it must be done manually. */ + usGenerateProtocolChecksum( ( uint8_t * ) &( pxPacket->xUDPPacket ), pxBuffer->xDataLength, pdTRUE ); + } + } + #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */ + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0UL ) + { + iptraceNETWORK_INTERFACE_TRANSMIT(); + emacps_send_message( &xEMACpsif, pxBuffer, bReleaseAfterSend ); + } + else if( bReleaseAfterSend != pdFALSE ) + { + /* No link. */ + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + } + + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +static inline unsigned long ulReadMDIO( unsigned ulRegister ) +{ + uint16_t usValue; + + XEmacPs_PhyRead( &( xEMACpsif.emacps ), ulPHYIndex, ulRegister, &usValue ); + return usValue; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvGMACWaitLS( TickType_t xMaxTimeTicks ) +{ + TickType_t xStartTime, xEndTime; + const TickType_t xShortDelay = pdMS_TO_TICKS( 20UL ); + BaseType_t xReturn; + + xStartTime = xTaskGetTickCount(); + + for( ; ; ) + { + xEndTime = xTaskGetTickCount(); + + if( xEndTime - xStartTime > xMaxTimeTicks ) + { + xReturn = pdFALSE; + break; + } + + ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0uL ) + { + xReturn = pdTRUE; + break; + } + + vTaskDelay( xShortDelay ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( nicUSE_UNCACHED_MEMORY == 0 ) + void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) + { + static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__( ( aligned( 32 ) ) ); + uint8_t * ucRAMBuffer = ucNetworkPackets; + uint32_t ul; + + for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ ) + { + pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING; + *( ( uintptr_t * ) ucRAMBuffer ) = ( uintptr_t ) &( pxNetworkBuffers[ ul ] ); + ucRAMBuffer += niBUFFER_1_PACKET_SIZE; + } + } +#else /* if ( nicUSE_UNCACHED_MEMORY == 0 ) */ + void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) + { + static uint8_t * pucNetworkPackets = NULL; + + if( pucNetworkPackets == NULL ) + { + pucNetworkPackets = pucGetUncachedMemory( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ); + + if( pucNetworkPackets != NULL ) + { + uint8_t * ucRAMBuffer = pucNetworkPackets; + 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; + } + } + } + } +#endif /* ( nicUSE_UNCACHED_MEMORY == 0 ) */ +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ + BaseType_t xReturn; + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) == 0uL ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void * pvParameters ) +{ + TimeOut_t xPhyTime; + TickType_t xPhyRemTime; + BaseType_t xResult = 0; + uint32_t xStatus; + const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + /* A possibility to set some additional task properties like calling + * portTASK_USES_FLOATING_POINT() */ + iptraceEMAC_TASK_STARTING(); + + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + + for( ; ; ) + { + #if ( ipconfigHAS_PRINTF != 0 ) + { + /* Call a function that monitors resources: the amount of free network + * buffers and the amount of free space on the heap. See FreeRTOS_IP.c + * for more detailed comments. */ + vPrintResourceStats(); + } + #endif /* ( ipconfigHAS_PRINTF != 0 ) */ + + if( ( xEMACpsif.isr_events & EMAC_IF_ALL_EVENT ) == 0 ) + { + /* No events to process now, wait for the next. */ + ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); + } + + if( ( xEMACpsif.isr_events & EMAC_IF_RX_EVENT ) != 0 ) + { + xEMACpsif.isr_events &= ~EMAC_IF_RX_EVENT; + xResult = emacps_check_rx( &xEMACpsif ); + } + + if( ( xEMACpsif.isr_events & EMAC_IF_TX_EVENT ) != 0 ) + { + xEMACpsif.isr_events &= ~EMAC_IF_TX_EVENT; + emacps_check_tx( &xEMACpsif ); + } + + if( ( xEMACpsif.isr_events & EMAC_IF_ERR_EVENT ) != 0 ) + { + xEMACpsif.isr_events &= ~EMAC_IF_ERR_EVENT; + emacps_check_errors( &xEMACpsif ); + } + + if( 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; + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) == 0uL ) + { + /* Indicate that the Link Status is high, so that + * xNetworkInterfaceOutput() can send packets. */ + ulPHYLinkStatus |= niBMSR_LINK_STATUS; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS assume 1\n" ) ); + } + } + else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) + { + xStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != ( xStatus & niBMSR_LINK_STATUS ) ) + { + ulPHYLinkStatus = xStatus; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0uL ) ); + } + + vTaskSetTimeOutState( &xPhyTime ); + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0uL ) + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); + } + else + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + } + } + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/readme.md b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/readme.md new file mode 100644 index 0000000..c58d1d3 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/readme.md @@ -0,0 +1,47 @@ +NetworkInterface for Xilinx' UltraScale+, running on Cortex A53, 64-bits + +Please include the following source files: + + $(PLUS_TCP_PATH)/portable/NetworkInterface/xilinx_ultrascale/NetworkInterface.c + $(PLUS_TCP_PATH)/portable/NetworkInterface/xilinx_ultrascale/uncached_memory.c + $(PLUS_TCP_PATH)/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_dma.c + $(PLUS_TCP_PATH)/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_physpeed.c + $(PLUS_TCP_PATH)/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_hw.c + +And include the following source files from the Xilinx library: + + $(PROCESSOR)/libsrc/emacps_v3_9/src/xemacps.c + $(PROCESSOR)/libsrc/emacps_v3_9/src/xemacps_control.c + $(PROCESSOR)/libsrc/emacps_v3_9/src/xemacps_g.c + $(PROCESSOR)/libsrc/emacps_v3_9/src/xemacps_intr.c + + E.g. psu_cortexa53_0/libsrc/emacps_v3_9/src/xemacps_intr.c + +The following source files are NOT used for the FreeRTOS+TCP interface: + + $(PROCESSOR)/libsrc/emacps_v3_9/src/xemacps_bdring.c + $(PROCESSOR)/libsrc/emacps_v3_9/src/xemacps_hw.c + $(PROCESSOR)/libsrc/emacps_v3_9/src/xemacps_sinit.c + + +It is recommended to have these defined : + +#define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 + +#define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 + + +It is obligatory to define these in your FreeRTOSIPConfig.h : + +#define ipconfigZERO_COPY_RX_DRIVER 1 + +#define ipconfigZERO_COPY_TX_DRIVER 1 + + +Please link you project with BufferAllocation_1.c ( not xxx_2.c ). + +The following option causes the memory of the network packets to be allocated +in normal ( cached ) memory. With this option, TCP processing seems a faster +than without this option. + +#define nicUSE_UNCACHED_MEMORY 0 diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/uncached_memory.c b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/uncached_memory.c new file mode 100644 index 0000000..4c36c59 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/uncached_memory.c @@ -0,0 +1,185 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +/* + * 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. + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" + +#include "x_emacpsif.h" +#include "x_topology.h" +#include "xstatus.h" + +#include "xparameters.h" +#include "xparameters_ps.h" +#include "xil_exception.h" +#include "xil_mmu.h" + +#include "uncached_memory.h" + +#if ( ipconfigULTRASCALE == 1 ) + #if defined( __aarch64__ ) || defined( ARMA53_32 ) + #ifndef uncMEMORY_SIZE + /* Reserve 2 MB of memory. */ + #define uncMEMORY_SIZE 0x200000U + #endif + #define DDR_MEMORY_END ( XPAR_PSU_DDR_0_S_AXI_HIGHADDR ) + #define uncMEMORY_ATTRIBUTE NORM_NONCACHE | INNER_SHAREABLE + #elif defined( ARMR5 ) + #ifndef uncMEMORY_SIZE + /* Reserve 1 MB of memory. */ + #define uncMEMORY_SIZE 0x100000U + #endif + #define uncMEMORY_SIZE 0x100000U + #define DDR_MEMORY_END ( XPAR_PSU_R5_DDR_0_S_AXI_HIGHADDR ) + #define uncMEMORY_ATTRIBUTE STRONG_ORDERD_SHARED | PRIV_RW_USER_RW + #else /* if defined( __aarch64__ ) || defined( ARMA53_32 ) */ + #error Please define the specific target Zynq Ultrascale+ architecture + #endif /* if defined( __aarch64__ ) || defined( ARMA53_32 ) */ +#else /* if ( ipconfigULTRASCALE == 1 ) */ + #ifndef uncMEMORY_SIZE + /* Reserve 1 MB of memory. */ + #define uncMEMORY_SIZE 0x100000U + #endif + #define DDR_MEMORY_END ( XPAR_PS7_DDR_0_S_AXI_HIGHADDR + 1 ) + #define uncMEMORY_ATTRIBUTE 0x1C02 +#endif /* ( ipconfigULTRASCALE == 1 ) */ + +/* Make sure that each pointer has an alignment of 4 KB. */ +#define uncALIGNMENT_SIZE 0x1000uL + +static void vInitialiseUncachedMemory( void ); + +static uint8_t pucUncachedMemory[ uncMEMORY_SIZE ] __attribute__( ( aligned( uncMEMORY_SIZE ) ) ); +static uint8_t * pucHeadOfMemory; +static uint32_t ulMemorySize; +static uint8_t * pucStartOfMemory = NULL; + +/* The linker file defines some pseudo variables. '_end' is one of them. + * It is located at the first free byte in RAM. */ +extern u8 _end; + +/*-----------------------------------------------------------*/ + +uint8_t ucIsCachedMemory( const uint8_t * pucBuffer ) +{ + uint8_t ucReturn; + + if( ( pucStartOfMemory != NULL ) && + ( pucBuffer >= pucStartOfMemory ) && + ( pucBuffer < ( pucStartOfMemory + uncMEMORY_SIZE ) ) ) + { + ucReturn = pdFALSE; + } + else + { + ucReturn = pdTRUE; + } + + return ucReturn; +} +/*-----------------------------------------------------------*/ + +uint8_t * pucGetUncachedMemory( uint32_t ulSize ) +{ + uint8_t * pucReturn; + uint32_t ulSkipSize; + + if( pucStartOfMemory == NULL ) + { + vInitialiseUncachedMemory(); + } + + if( ( pucStartOfMemory == NULL ) || ( ulSize > ulMemorySize ) ) + { + pucReturn = NULL; + } + else + { + pucReturn = pucHeadOfMemory; + /* Make sure that the next pointer return will have a good alignment. */ + ulSkipSize = ( ulSize + uncALIGNMENT_SIZE ) & ~( uncALIGNMENT_SIZE - 1uL ); + pucHeadOfMemory += ulSkipSize; + ulMemorySize -= ulSkipSize; + } + + return pucReturn; +} +/*-----------------------------------------------------------*/ + +static void vInitialiseUncachedMemory() +{ + /* At the end of program's space... */ + pucStartOfMemory = pucUncachedMemory; + + if( ( ( uintptr_t ) pucStartOfMemory ) + uncMEMORY_SIZE > DDR_MEMORY_END ) + { + FreeRTOS_printf( ( "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( ( uintptr_t ) pucStartOfMemory, uncMEMORY_ATTRIBUTE ); + + /* For experiments in the SDIO driver, make the remaining uncached memory + * public */ + pucHeadOfMemory = pucStartOfMemory; + ulMemorySize = uncMEMORY_SIZE; + memset( pucStartOfMemory, '\0', uncMEMORY_SIZE ); + } +} diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/uncached_memory.h b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/uncached_memory.h new file mode 100644 index 0000000..fc96a72 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/uncached_memory.h @@ -0,0 +1,22 @@ +/* + * 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 */ diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif.h b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif.h new file mode 100644 index 0000000..ab3d11a --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2010-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef __NETIF_XEMACPSIF_H__ + #define __NETIF_XEMACPSIF_H__ + + #ifdef __cplusplus + extern "C" { + #endif + + #include + + #include "xstatus.h" + #include "sleep.h" + #include "xparameters.h" + #include "xparameters_ps.h" /* defines XPAR values */ + #include "xil_types.h" + #include "xil_assert.h" + #include "xil_io.h" + #include "xil_exception.h" + #include "xpseudo_asm.h" + #include "xil_cache.h" + #include "xuartps.h" + #include "xscugic.h" + #include "xemacps.h" /* defines XEmacPs API */ + +/*#include "netif/xpqueue.h" */ +/*#include "xlwipconfig.h" */ + + void xemacpsif_setmac( uint32_t index, + uint8_t * addr ); + uint8_t * xemacpsif_getmac( uint32_t index ); +/*int xemacpsif_init(struct netif *netif); */ +/*int xemacpsif_input(struct netif *netif); */ + #ifdef NOTNOW_BHILL + unsigned get_IEEE_phy_speed( XLlTemac * xlltemacp ); + #endif + +/* xaxiemacif_hw.c */ + void xemacps_error_handler( XEmacPs * Temac ); + + struct xBD_TYPE + { + uint32_t address; + uint32_t flags; + #ifdef __aarch64__ + /* Fill it up so the struct gets a size of 16 bytes. */ + uint32_t address_high; + uint32_t reserved; + #endif + }; + +/* + * Missing declaration in 'src/xemacps_hw.h' : + * When set, the GEM DMA will automatically + * discard receive packets from the receiver packet + * buffer memory when no AHB resource is + * available. + * When low, then received packets will remain to be + * stored in the SRAM based packet buffer until + * AHB buffer resource next becomes available. + */ + #define XEMACPS_DMACR_DISC_WHEN_NO_AHB_MASK 0x01000000 + + #define EMAC_IF_RX_EVENT 1 + #define EMAC_IF_TX_EVENT 2 + #define EMAC_IF_ERR_EVENT 4 + #define EMAC_IF_ALL_EVENT 7 + +/* structure within each netif, encapsulating all information required for + * using a particular temac instance + */ + typedef struct + { + XEmacPs emacps; + + /* pointers to memory holding buffer descriptors (used only with SDMA) */ + struct xBD_TYPE * rxSegments; + struct xBD_TYPE * txSegments; + + struct xBD_TYPE * rxBdTerminator; + struct xBD_TYPE * txBdTerminator; + + unsigned char * tx_space; + unsigned uTxUnitSize; + + char * remain_mem; + unsigned remain_siz; + + volatile int rxHead, rxTail; + volatile int txHead, txTail; + + volatile int txBusy; + + volatile uint32_t isr_events; + + unsigned int last_rx_frms_cntr; + } xemacpsif_s; + +/*extern xemacpsif_s xemacpsif; */ + + int is_tx_space_available( xemacpsif_s * emac ); + +/* xaxiemacif_dma.c */ + + struct xNETWORK_BUFFER; + + int emacps_check_rx( xemacpsif_s * xemacpsif ); + void emacps_check_tx( xemacpsif_s * xemacpsif ); + int emacps_check_errors( xemacpsif_s * xemacps ); + void emacps_set_rx_buffers( xemacpsif_s * xemacpsif, + u32 ulCount ); + + extern XStatus emacps_send_message( xemacpsif_s * xemacpsif, + struct xNETWORK_BUFFER * pxBuffer, + int iReleaseAfterSend ); + extern unsigned Phy_Setup( XEmacPs * xemacpsp ); + extern uint32_t Phy_Setup_US( XEmacPs * xemacpsp, + uint32_t phy_addr ); + extern void setup_isr( xemacpsif_s * xemacpsif ); + extern XStatus init_dma( xemacpsif_s * xemacpsif ); + extern void start_emacps( xemacpsif_s * xemacpsif ); + + void EmacEnableIntr( void ); + void EmacDisableIntr( void ); + + XStatus init_axi_dma( xemacpsif_s * xemacpsif ); + void process_sent_bds( xemacpsif_s * xemacpsif ); + + void emacps_send_handler( void * arg ); + void emacps_recv_handler( void * arg ); + void emacps_error_handler( void * arg, + u8 Direction, + u32 ErrorWord ); + void HandleTxErrors( xemacpsif_s * xemacpsif ); + XEmacPs_Config * xemacps_lookup_config( unsigned mac_base ); + + void clean_dma_txdescs( xemacpsif_s * xemacpsif ); + void resetrx_on_no_rxdata( xemacpsif_s * xemacpsif ); + + #ifdef __cplusplus + } + #endif + +#endif /* __NETIF_XAXIEMACIF_H__ */ diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_dma.c b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_dma.c new file mode 100644 index 0000000..154308b --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_dma.c @@ -0,0 +1,761 @@ +/* + * FreeRTOS+TCP V3.1.0 + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + */ + +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" + +#include "x_emacpsif.h" +#include "x_topology.h" +#include "xstatus.h" + +#include "xparameters.h" +#include "xparameters_ps.h" +#include "xil_exception.h" +#include "xil_mmu.h" + +#include "uncached_memory.h" + +#ifndef ipconfigULTRASCALE + #error please define ipconfigULTRASCALE +#endif + +/* Two defines used to set or clear the EMAC interrupt */ +#if ( ipconfigULTRASCALE == 1 ) + +/* + * XPAR_SCUGIC_0_CPU_BASEADDR 0xF9020000U + * XPAR_SCUGIC_0_DIST_BASEADDR 0xF9010000U + */ + #define INTC_BASE_ADDR XPAR_SCUGIC_0_CPU_BASEADDR + #define INTC_DIST_BASE_ADDR XPAR_SCUGIC_0_DIST_BASEADDR +#else + +/* + * XPAR_SCUGIC_CPU_BASEADDR (XPS_SCU_PERIPH_BASE + 0x00000100U) + * XPAR_SCUGIC_DIST_BASEADDR (XPS_SCU_PERIPH_BASE + 0x00001000U) + */ + + #define INTC_BASE_ADDR XPAR_SCUGIC_CPU_BASEADDR + #define INTC_DIST_BASE_ADDR XPAR_SCUGIC_DIST_BASEADDR +#endif + +#if ( ipconfigPACKET_FILLER_SIZE != 2 ) + #error Please define ipconfigPACKET_FILLER_SIZE as the value '2' +#endif +#define TX_OFFSET ipconfigPACKET_FILLER_SIZE + +#if ( ipconfigNETWORK_MTU > 1526 ) + #warning the use of Jumbo Frames has not been tested sufficiently yet. + #define USE_JUMBO_FRAMES 1 +#endif + +#if ( USE_JUMBO_FRAMES == 1 ) + #define dmaRX_TX_BUFFER_SIZE 10240 +#else + #define dmaRX_TX_BUFFER_SIZE 1536 +#endif + +#if ( ipconfigULTRASCALE == 1 ) + extern XScuGic xInterruptController; +#endif + +/* Defined in NetworkInterface.c */ +extern TaskHandle_t xEMACTaskHandle; + +/* + * pxDMA_tx_buffers: these are character arrays, each one is big enough to hold 1 MTU. + * The actual TX buffers are located in uncached RAM. + */ +static unsigned char * pxDMA_tx_buffers[ ipconfigNIC_N_TX_DESC ] = { NULL }; + +/* + * pxDMA_rx_buffers: these are pointers to 'NetworkBufferDescriptor_t'. + * Once a message has been received by the EMAC, the descriptor can be passed + * immediately to the IP-task. + */ +static NetworkBufferDescriptor_t * pxDMA_rx_buffers[ ipconfigNIC_N_RX_DESC ] = { NULL }; + +/* + * The FreeRTOS+TCP port is using a fixed 'topology', which is declared in + * ./portable/NetworkInterface/Zynq/NetworkInterface.c + */ +extern struct xtopology_t xXTopology; + +static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; + +/* + * The FreeRTOS+TCP port does not make use of "src/xemacps_bdring.c". + * In stead 'struct xemacpsif_s' has a "head" and a "tail" index. + * "head" is the next index to be written, used. + * "tail" is the next index to be read, freed. + */ + +int is_tx_space_available( xemacpsif_s * xemacpsif ) +{ + size_t uxCount; + + if( xTXDescriptorSemaphore != NULL ) + { + uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + } + else + { + uxCount = ( UBaseType_t ) 0u; + } + + return uxCount; +} + +void emacps_check_tx( xemacpsif_s * xemacpsif ) +{ + int tail = xemacpsif->txTail; + int head = xemacpsif->txHead; + size_t uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + /* uxCount is the number of TX descriptors that are in use by the DMA. */ + /* When done, "TXBUF_USED" will be set. */ + + while( ( uxCount > 0 ) && ( ( xemacpsif->txSegments[ tail ].flags & XEMACPS_TXBUF_USED_MASK ) != 0 ) ) + { + if( ( tail == head ) && ( uxCount != ipconfigNIC_N_TX_DESC ) ) + { + break; + } + + { + void * pvBuffer = pxDMA_tx_buffers[ tail ]; + NetworkBufferDescriptor_t * pxBuffer; + + if( pvBuffer != NULL ) + { + pxDMA_tx_buffers[ tail ] = NULL; + pxBuffer = pxPacketBuffer_to_NetworkBuffer( pvBuffer ); + + if( pxBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + } + else + { + FreeRTOS_printf( ( "emacps_check_tx: Can not find network buffer\n" ) ); + } + } + } + + /* Clear all but the "used" and "wrap" bits. */ + if( tail < ipconfigNIC_N_TX_DESC - 1 ) + { + xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK; + } + else + { + xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK; + } + + uxCount--; + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + + if( ++tail == ipconfigNIC_N_TX_DESC ) + { + tail = 0; + } + + xemacpsif->txTail = tail; + } +} + +void emacps_send_handler( void * arg ) +{ + xemacpsif_s * xemacpsif; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + xemacpsif = ( xemacpsif_s * ) arg; + + /* This function is called from an ISR. The Xilinx ISR-handler has already + * cleared the TXCOMPL and TXSR_USEDREAD status bits in the XEMACPS_TXSR register. + * But it forgets to do a read-back. Do so now to avoid ever-returning ISR's. */ + ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_TXSR_OFFSET ); + + /* In this port for FreeRTOS+TCP, the EMAC interrupts will only set a bit in + * "isr_events". The task in NetworkInterface will wake-up and do the necessary work. + */ + xemacpsif->isr_events |= EMAC_IF_TX_EVENT; + xemacpsif->txBusy = pdFALSE; + + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} + +static BaseType_t xValidLength( BaseType_t xLength ) +{ + BaseType_t xReturn; + + if( ( xLength >= ( BaseType_t ) sizeof( struct xARP_PACKET ) ) && ( ( ( uint32_t ) xLength ) <= dmaRX_TX_BUFFER_SIZE ) ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} + +XStatus emacps_send_message( xemacpsif_s * xemacpsif, + NetworkBufferDescriptor_t * pxBuffer, + int iReleaseAfterSend ) +{ + int head = xemacpsif->txHead; + int iHasSent = 0; + uint32_t ulBaseAddress = xemacpsif->emacps.Config.BaseAddress; + TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 5000U ); + + /* This driver wants to own all network buffers which are to be transmitted. */ + configASSERT( iReleaseAfterSend != pdFALSE ); + + /* Open a do {} while ( 0 ) loop to be able to call break. */ + do + { + uint32_t ulFlags = 0; + + if( xValidLength( pxBuffer->xDataLength ) != pdTRUE ) + { + break; + } + + if( xTXDescriptorSemaphore == NULL ) + { + break; + } + + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + FreeRTOS_printf( ( "emacps_send_message: Time-out waiting for TX buffer\n" ) ); + break; + } + + /* Pass the pointer (and its ownership) directly to DMA. */ + pxDMA_tx_buffers[ head ] = pxBuffer->pucEthernetBuffer; + + if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheFlushRange( ( INTPTR ) pxBuffer->pucEthernetBuffer, ( INTPTR ) pxBuffer->xDataLength ); + } + + /* Buffer has been transferred, do not release it. */ + iReleaseAfterSend = pdFALSE; + + /* Packets will be sent one-by-one, so for each packet + * the TXBUF_LAST bit will be set. */ + ulFlags |= XEMACPS_TXBUF_LAST_MASK; + ulFlags |= ( pxBuffer->xDataLength & XEMACPS_TXBUF_LEN_MASK ); + + if( head == ( ipconfigNIC_N_TX_DESC - 1 ) ) + { + ulFlags |= XEMACPS_TXBUF_WRAP_MASK; + } + + /* Copy the address of the buffer and set the flags. */ + xemacpsif->txSegments[ head ].address = ( uintptr_t ) pxDMA_tx_buffers[ head ]; + xemacpsif->txSegments[ head ].flags = ulFlags; + + iHasSent = pdTRUE; + + if( ++head == ipconfigNIC_N_TX_DESC ) + { + head = 0; + } + + /* Update the TX-head index. These variable are declared volatile so they will be + * accessed as little as possible. */ + xemacpsif->txHead = head; + } while( pdFALSE ); + + if( iReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + pxBuffer = NULL; + } + + /* Data Synchronization Barrier */ + dsb(); + + if( iHasSent != pdFALSE ) + { + /* Make STARTTX high */ + uint32_t ulValue = XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET ); + /* Start transmit */ + xemacpsif->txBusy = pdTRUE; + XEmacPs_WriteReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET, ( ulValue | XEMACPS_NWCTRL_STARTTX_MASK ) ); + /* Read back the register to make sure the data is flushed. */ + ( void ) XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET ); + } + + dsb(); + + return 0; +} + +void emacps_recv_handler( void * arg ) +{ + xemacpsif_s * xemacpsif; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + xemacpsif = ( xemacpsif_s * ) arg; + xemacpsif->isr_events |= EMAC_IF_RX_EVENT; + + /* The driver has already cleared the FRAMERX, BUFFNA and error bits + * in the XEMACPS_RXSR register, + * But it forgets to do a read-back. Do so now. */ + ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET ); + + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} + +static void prvPassEthMessages( NetworkBufferDescriptor_t * pxDescriptor ) +{ + IPStackEvent_t xRxEvent; + + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = ( void * ) pxDescriptor; + + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 1000U ) != pdPASS ) + { + /* The buffer could not be sent to the stack so must be released again. + * This is a deferred handler taskr, not a real interrupt, so it is ok to + * use the task level function here. */ + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + do + { + NetworkBufferDescriptor_t * pxNext = pxDescriptor->pxNextBuffer; + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + pxDescriptor = pxNext; + } while( pxDescriptor != NULL ); + } + #else + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "prvPassEthMessages: Can not queue return packet!\n" ) ); + } +} + +int emacps_check_rx( xemacpsif_s * xemacpsif ) +{ + NetworkBufferDescriptor_t * pxBuffer, * pxNewBuffer; + int rx_bytes; + volatile int msgCount = 0; + int head = xemacpsif->rxHead; + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + NetworkBufferDescriptor_t * pxFirstDescriptor = NULL; + NetworkBufferDescriptor_t * pxLastDescriptor = NULL; + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + + /* There seems to be an issue (SI# 692601), see comments below. */ + resetrx_on_no_rxdata( xemacpsif ); + + /* This FreeRTOS+TCP driver shall be compiled with the option + * "ipconfigUSE_LINKED_RX_MESSAGES" enabled. It allows the driver to send a + * chain of RX messages within one message to the IP-task. */ + for( ; ; ) + { + if( ( ( xemacpsif->rxSegments[ head ].address & XEMACPS_RXBUF_NEW_MASK ) == 0 ) || + ( pxDMA_rx_buffers[ head ] == NULL ) ) + { + break; + } + + pxNewBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 ); + + if( pxNewBuffer == NULL ) + { + /* A packet has been received, but there is no replacement for this Network Buffer. + * The packet will be dropped, and it Network Buffer will stay in place. */ + FreeRTOS_printf( ( "emacps_check_rx: unable to allocate a Network Buffer\n" ) ); + pxNewBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ head ]; + } + else + { + pxBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ head ]; + + /* Just avoiding to use or refer to the same buffer again */ + pxDMA_rx_buffers[ head ] = pxNewBuffer; + + /* + * Adjust the buffer size to the actual number of bytes received. + */ + rx_bytes = xemacpsif->rxSegments[ head ].flags & XEMACPS_RXBUF_LEN_MASK; + + pxBuffer->xDataLength = rx_bytes; + + if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheInvalidateRange( ( ( uintptr_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( unsigned ) rx_bytes ); + } + + /* store it in the receive queue, where it'll be processed by a + * different handler. */ + iptraceNETWORK_INTERFACE_RECEIVE(); + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + pxBuffer->pxNextBuffer = NULL; + + if( pxFirstDescriptor == NULL ) + { + /* Becomes the first message */ + pxFirstDescriptor = pxBuffer; + } + else if( pxLastDescriptor != NULL ) + { + /* Add to the tail */ + pxLastDescriptor->pxNextBuffer = pxBuffer; + } + + pxLastDescriptor = pxBuffer; + } + #else /* if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) */ + { + prvPassEthMessages( pxBuffer ); + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + + msgCount++; + } + + { + if( ucIsCachedMemory( pxNewBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheInvalidateRange( ( ( uintptr_t ) pxNewBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( uint32_t ) dmaRX_TX_BUFFER_SIZE ); + } + + { + uintptr_t addr = ( ( uintptr_t ) pxNewBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK; + + if( head == ( ipconfigNIC_N_RX_DESC - 1 ) ) + { + addr |= XEMACPS_RXBUF_WRAP_MASK; + } + + /* Clearing 'XEMACPS_RXBUF_NEW_MASK' 0x00000001 *< Used bit.. */ + xemacpsif->rxSegments[ head ].flags = 0; + xemacpsif->rxSegments[ head ].address = addr; + /* Make sure that the value has reached the peripheral by reading it back. */ + ( void ) xemacpsif->rxSegments[ head ].address; + } + } + + if( ++head == ipconfigNIC_N_RX_DESC ) + { + head = 0; + } + + xemacpsif->rxHead = head; + } + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + if( pxFirstDescriptor != NULL ) + { + prvPassEthMessages( pxFirstDescriptor ); + } + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + + return msgCount; +} + +void clean_dma_txdescs( xemacpsif_s * xemacpsif ) +{ + int index; + + for( index = 0; index < ipconfigNIC_N_TX_DESC; index++ ) + { + xemacpsif->txSegments[ index ].address = ( uintptr_t ) NULL; + xemacpsif->txSegments[ index ].flags = XEMACPS_TXBUF_USED_MASK; + pxDMA_tx_buffers[ index ] = ( unsigned char * ) NULL; + } + + xemacpsif->txSegments[ ipconfigNIC_N_TX_DESC - 1 ].flags = + XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK; +} + +XStatus init_dma( xemacpsif_s * xemacpsif ) +{ + NetworkBufferDescriptor_t * pxBuffer; + + int iIndex; + UBaseType_t xRxSize; + UBaseType_t xTxSize; + struct xtopology_t * xtopologyp = &xXTopology; + XEmacPs_BdRing * rxRing; + XEmacPs * emac = &( xemacpsif->emacps ); + XEmacPs_Bd bdTemplate; + XEmacPs_Bd * dmaBdPtr = NULL; + uint32_t status; + + xRxSize = ipconfigNIC_N_RX_DESC * sizeof( xemacpsif->rxSegments[ 0 ] ); + + xTxSize = ipconfigNIC_N_TX_DESC * sizeof( xemacpsif->txSegments[ 0 ] ); + + xemacpsif->uTxUnitSize = ( dmaRX_TX_BUFFER_SIZE + 0x1000UL ) & ~0xfffUL; + + /* + * We allocate 65536 bytes for RX BDs which can accommodate a + * maximum of 8192 BDs which is much more than any application + * will ever need. + */ + xemacpsif->rxSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xRxSize ) ); + xemacpsif->txSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xTxSize ) ); + + configASSERT( xemacpsif->rxSegments ); + configASSERT( xemacpsif->txSegments ); + configASSERT( ( ( ( uintptr_t ) xemacpsif->rxSegments ) % XEMACPS_DMABD_MINIMUM_ALIGNMENT ) == 0 ); + configASSERT( ( ( ( uintptr_t ) xemacpsif->txSegments ) % XEMACPS_DMABD_MINIMUM_ALIGNMENT ) == 0 ); + + + rxRing = &( XEmacPs_GetRxRing( emac ) ); + XEmacPs_BdClear( bdTemplate ); + + status = XEmacPs_BdRingCreate( rxRing, ( UINTPTR ) xemacpsif->rxSegments, + ( UINTPTR ) xemacpsif->rxSegments, XEMACPS_DMABD_MINIMUM_ALIGNMENT, + ipconfigNIC_N_RX_DESC ); + + if( status != 0 ) + { + return status; + } + + status = XEmacPs_BdRingClone( rxRing, &bdTemplate, XEMACPS_RECV ); + + if( status != 0 ) + { + return status; + } + + if( xTXDescriptorSemaphore == NULL ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ipconfigNIC_N_TX_DESC, ( UBaseType_t ) ipconfigNIC_N_TX_DESC ); + configASSERT( xTXDescriptorSemaphore != NULL ); + } + + /* + * Allocate RX descriptors, 1 RxBD at a time. + */ + for( iIndex = 0; iIndex < ipconfigNIC_N_RX_DESC; iIndex++ ) + { + pxBuffer = pxDMA_rx_buffers[ iIndex ]; + + if( pxBuffer == NULL ) + { + pxBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 ); + + if( pxBuffer == NULL ) + { + FreeRTOS_printf( ( "Unable to allocate a network buffer in recv_handler\n" ) ); + return -1; + } + } + + status = XEmacPs_BdRingAlloc( rxRing, 1, &dmaBdPtr ); + + if( status != 0 ) + { + return status; + } + + XEmacPs_BdSetAddressRx( dmaBdPtr, + ( ( uintptr_t ) pxBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK ); + + status = XEmacPs_BdRingToHw( rxRing, 1, dmaBdPtr ); + + if( status != 0 ) + { + return status; + } + + /* Writing for debug - can look at it in RX processing */ + #ifdef __aarch64__ + xemacpsif->rxSegments[ iIndex ].reserved = iIndex; + #endif + + pxDMA_rx_buffers[ iIndex ] = pxBuffer; + + /* Make sure this memory is not in cache for now. */ + if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheInvalidateRange( ( ( uintptr_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, + ( unsigned ) dmaRX_TX_BUFFER_SIZE ); + } + } + + xemacpsif->rxSegments[ ipconfigNIC_N_RX_DESC - 1 ].address |= XEMACPS_RXBUF_WRAP_MASK; + + clean_dma_txdescs( xemacpsif ); + + { + uint32_t value; + value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET ); + + /* 1xxxx: Attempt to use INCR16 AHB bursts */ + value = ( value & ~( XEMACPS_DMACR_BLENGTH_MASK ) ) | XEMACPS_DMACR_INCR16_AHB_BURST; + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + value |= XEMACPS_DMACR_TCPCKSUM_MASK; + #else + #warning Are you sure the EMAC should not calculate outgoing checksums? + value &= ~XEMACPS_DMACR_TCPCKSUM_MASK; + #endif + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET, value ); + } + { + uint32_t value; + value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET ); + + /* Network buffers are 32-bit aligned + 2 bytes (because ipconfigPACKET_FILLER_SIZE = 2 ). + * Now tell the EMAC that received messages should be stored at "address + 2". */ + value = ( value & ~XEMACPS_NWCFG_RXOFFS_MASK ) | 0x8000; + + #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM != 0 ) + value |= XEMACPS_NWCFG_RXCHKSUMEN_MASK; + #else + #warning Are you sure the EMAC should not calculate incoming checksums? + value &= ~( ( uint32_t ) XEMACPS_NWCFG_RXCHKSUMEN_MASK ); + #endif + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET, value ); + } + + /* Set terminating BDs for US+ GEM */ + if( xemacpsif->emacps.Version > 2 ) + { + xemacpsif->rxBdTerminator = ( struct xBD_TYPE * ) pucGetUncachedMemory( sizeof( *xemacpsif->rxBdTerminator ) ); + xemacpsif->txBdTerminator = ( struct xBD_TYPE * ) pucGetUncachedMemory( sizeof( *xemacpsif->txBdTerminator ) ); + + XEmacPs_BdClear( xemacpsif->rxBdTerminator ); + XEmacPs_BdSetAddressRx( xemacpsif->rxBdTerminator, ( XEMACPS_RXBUF_NEW_MASK | + XEMACPS_RXBUF_WRAP_MASK ) ); + XEmacPs_Out32( ( emac->Config.BaseAddress + XEMACPS_RXQ1BASE_OFFSET ), + ( UINTPTR ) xemacpsif->rxBdTerminator ); + + XEmacPs_BdClear( xemacpsif->txBdTerminator ); + XEmacPs_BdSetStatus( xemacpsif->txBdTerminator, + ( XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK ) ); + XEmacPs_Out32( ( emac->Config.BaseAddress + XEMACPS_TXQBASE_OFFSET ), + ( UINTPTR ) xemacpsif->txBdTerminator ); + } + + /* These variables will be used in XEmacPs_Start (see src/xemacps.c). */ + XEmacPs_SetQueuePtr( emac, ( uintptr_t ) xemacpsif->rxSegments, 0, XEMACPS_RECV ); + + if( xemacpsif->emacps.Version > 2 ) + { + XEmacPs_SetQueuePtr( emac, ( uintptr_t ) xemacpsif->txSegments, 1, XEMACPS_SEND ); + } + else + { + XEmacPs_SetQueuePtr( emac, ( uintptr_t ) xemacpsif->txSegments, 0, XEMACPS_SEND ); + } + + XScuGic_Connect( &xInterruptController, + xtopologyp->scugic_emac_intr, + XEmacPs_IntrHandler, + emac ); + + /* + * Enable the interrupt for emacps. + */ + EmacEnableIntr(); + + return 0; +} + +/* + * resetrx_on_no_rxdata(): + * + * It is called at regular intervals through the API xemacpsif_resetrx_on_no_rxdata + * called by the user. + * The EmacPs has a HW bug (SI# 692601) on the Rx path for heavy Rx traffic. + * Under heavy Rx traffic because of the HW bug there are times when the Rx path + * becomes unresponsive. The workaround for it is to check for the Rx path for + * traffic (by reading the stats registers regularly). If the stats register + * does not increment for sometime (proving no Rx traffic), the function resets + * the Rx data path. + * + */ + +void resetrx_on_no_rxdata( xemacpsif_s * xemacpsif ) +{ + uint32_t regctrl; + uint32_t tempcntr; + + tempcntr = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXCNT_OFFSET ); + + if( ( tempcntr == 0 ) && ( xemacpsif->last_rx_frms_cntr == 0 ) ) + { + regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET ); + regctrl &= ( ~XEMACPS_NWCTRL_RXEN_MASK ); + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, regctrl ); + regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET ); + regctrl |= ( XEMACPS_NWCTRL_RXEN_MASK ); + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, regctrl ); + } + + xemacpsif->last_rx_frms_cntr = tempcntr; +} + +void EmacDisableIntr( void ) +{ + XScuGic_DisableIntr( INTC_DIST_BASE_ADDR, xXTopology.scugic_emac_intr ); +} + +void EmacEnableIntr( void ) +{ + XScuGic_Enable( &xInterruptController, xXTopology.scugic_emac_intr ); +} diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_hw.c b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_hw.c new file mode 100644 index 0000000..183f828 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_hw.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2010-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "x_emacpsif.h" + +extern TaskHandle_t xEMACTaskHandle; + +/*** IMPORTANT: Define PEEP in xemacpsif.h and sys_arch_raw.c + *** to run it on a PEEP board + ***/ + +void setup_isr( xemacpsif_s * xemacpsif ) +{ + /* + * Setup callbacks + */ + XEmacPs_SetHandler( &xemacpsif->emacps, XEMACPS_HANDLER_DMASEND, + ( void * ) emacps_send_handler, + ( void * ) xemacpsif ); + + XEmacPs_SetHandler( &xemacpsif->emacps, XEMACPS_HANDLER_DMARECV, + ( void * ) emacps_recv_handler, + ( void * ) xemacpsif ); + + XEmacPs_SetHandler( &xemacpsif->emacps, XEMACPS_HANDLER_ERROR, + ( void * ) emacps_error_handler, + ( void * ) xemacpsif ); +} + +void start_emacps( xemacpsif_s * xemacps ) +{ + /* start the temac */ + XEmacPs_Start( &xemacps->emacps ); +} + +extern struct xtopology_t xXTopology; + +volatile int error_msg_count = 0; +volatile const char * last_err_msg = ""; + +struct xERROR_MSG +{ + void * arg; + u8 Direction; + u32 ErrorWord; +}; + +static struct xERROR_MSG xErrorList[ 8 ]; +static BaseType_t xErrorHead, xErrorTail; + +void emacps_error_handler( void * arg, + u8 Direction, + u32 ErrorWord ) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xemacpsif_s * xemacpsif; + BaseType_t xNextHead = xErrorHead; + + xemacpsif = ( xemacpsif_s * ) ( arg ); + + if( ( Direction != XEMACPS_SEND ) || ( ErrorWord != XEMACPS_TXSR_USEDREAD_MASK ) ) + { + if( ++xNextHead == ( sizeof( xErrorList ) / sizeof( xErrorList[ 0 ] ) ) ) + { + xNextHead = 0; + } + + if( xNextHead != xErrorTail ) + { + xErrorList[ xErrorHead ].arg = arg; + xErrorList[ xErrorHead ].Direction = Direction; + xErrorList[ xErrorHead ].ErrorWord = ErrorWord; + + xErrorHead = xNextHead; + + xemacpsif = ( xemacpsif_s * ) ( arg ); + xemacpsif->isr_events |= EMAC_IF_ERR_EVENT; + } + + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + } + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} + +static void emacps_handle_error( void * arg, + u8 Direction, + u32 ErrorWord ); + +int emacps_check_errors( xemacpsif_s * xemacps ) +{ + int xResult; + + ( void ) xemacps; + + if( xErrorHead == xErrorTail ) + { + xResult = 0; + } + else + { + xResult = 1; + emacps_handle_error( + xErrorList[ xErrorTail ].arg, + xErrorList[ xErrorTail ].Direction, + xErrorList[ xErrorTail ].ErrorWord ); + } + + return xResult; +} + +static void emacps_handle_error( void * arg, + u8 Direction, + u32 ErrorWord ) +{ + xemacpsif_s * xemacpsif; + struct xtopology_t * xtopologyp; + XEmacPs * xemacps; + + xemacpsif = ( xemacpsif_s * ) ( arg ); + + xtopologyp = &xXTopology; + + xemacps = &xemacpsif->emacps; + + /* Do not appear to be used. */ + ( void ) xemacps; + ( void ) xtopologyp; + + last_err_msg = NULL; + + if( ErrorWord != 0 ) + { + switch( Direction ) + { + case XEMACPS_RECV: + + if( ( ErrorWord & XEMACPS_RXSR_HRESPNOK_MASK ) != 0 ) + { + last_err_msg = "Receive DMA error"; + xNetworkInterfaceInitialise(); + } + + if( ( ErrorWord & XEMACPS_RXSR_RXOVR_MASK ) != 0 ) + { + last_err_msg = "Receive over run"; + emacps_recv_handler( arg ); + } + + if( ( ErrorWord & XEMACPS_RXSR_BUFFNA_MASK ) != 0 ) + { + last_err_msg = "Receive buffer not available"; + emacps_recv_handler( arg ); + } + + break; + + case XEMACPS_SEND: + + if( ( ErrorWord & XEMACPS_TXSR_HRESPNOK_MASK ) != 0 ) + { + last_err_msg = "Transmit DMA error"; + xNetworkInterfaceInitialise(); + } + + if( ( ErrorWord & XEMACPS_TXSR_URUN_MASK ) != 0 ) + { + last_err_msg = "Transmit under run"; + HandleTxErrors( xemacpsif ); + } + + if( ( ErrorWord & XEMACPS_TXSR_BUFEXH_MASK ) != 0 ) + { + last_err_msg = "Transmit buffer exhausted"; + HandleTxErrors( xemacpsif ); + } + + if( ( ErrorWord & XEMACPS_TXSR_RXOVR_MASK ) != 0 ) + { + last_err_msg = "Transmit retry excessed limits"; + HandleTxErrors( xemacpsif ); + } + + if( ( ErrorWord & XEMACPS_TXSR_FRAMERX_MASK ) != 0 ) + { + last_err_msg = "Transmit collision"; + emacps_check_tx( xemacpsif ); + } + + break; + } + } + + /* Break on this statement and inspect error_msg if you like */ + if( last_err_msg != NULL ) + { + error_msg_count++; + FreeRTOS_printf( ( "emacps_handle_error: %s\n", last_err_msg ) ); + } +} + +void HandleTxErrors( xemacpsif_s * xemacpsif ) +{ + u32 netctrlreg; + + /*taskENTER_CRITICAL() */ + { + netctrlreg = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET ); + netctrlreg = netctrlreg & ( ~XEMACPS_NWCTRL_TXEN_MASK ); + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, netctrlreg ); + + clean_dma_txdescs( xemacpsif ); + netctrlreg = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET ); + netctrlreg = netctrlreg | ( XEMACPS_NWCTRL_TXEN_MASK ); + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, netctrlreg ); + } + /*taskEXIT_CRITICAL( ); */ +} diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_hw.h b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_hw.h new file mode 100644 index 0000000..88115c9 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_hw.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef __XEMACPSIF_HW_H_ + #define __XEMACPSIF_HW_H_ + + #include "x_emacpsif.h" +/*#include "lwip/netif.h" */ + + #ifdef __cplusplus + extern "C" { + #endif + + XEmacPs_Config * lookup_config( unsigned mac_base ); + +/*void init_emacps(xemacpsif_s *xemacpsif, struct netif *netif); */ + + int emacps_check_errors( xemacpsif_s * xemacps ); + +/* Defined in x_emacpsif_physpeed.c. */ + uint32_t ulDetecPHY( XEmacPs * xemacpsp ); + + + #ifdef __cplusplus + } + #endif + +#endif /* ifndef __XEMACPSIF_HW_H_ */ diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_physpeed.c b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_physpeed.c new file mode 100644 index 0000000..658a46d --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_physpeed.c @@ -0,0 +1,1448 @@ +/* + * Copyright (c) 2007-2008, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Advanced Micro Devices, Inc. nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/* + * Some portions copyright (c) 2010-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +/* Standard includes. */ +#include +#include +#include + +#include "x_emacpsif.h" +/*#include "lwipopts.h" */ +#include "xparameters_ps.h" +#include "xparameters.h" + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/*/ * FreeRTOS+TCP includes. * / */ +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" + +#define NOP() asm ( "nop" ); + +#define phyMIN_PHY_ADDRESS 0 +#define phyMAX_PHY_ADDRESS 31 + +void test_sleep( uint32_t uxTicks ) +{ + for( uint32_t j = 0U; j < uxTicks; j++ ) + { + for( uint32_t i = 0U; i < 100000000U; i++ ) + { + NOP(); + } + } +} + +void my_sleep( uint32_t uxTicks ) +{ + sleep( uxTicks ); +} + +/*** IMPORTANT: Define PEEP in xemacpsif.h and sys_arch_raw.c + *** to run it on a PEEP board + ***/ + +/* Advertisement control register. */ +#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_1000FULL 0x0200 +#define ADVERTISE_1000HALF 0x0100 + +#define ADVERTISE_100_AND_10 \ + ( ADVERTISE_10FULL | ADVERTISE_100FULL | \ + ADVERTISE_10HALF | ADVERTISE_100HALF ) +#define ADVERTISE_100 ( ADVERTISE_100FULL | ADVERTISE_100HALF ) +#define ADVERTISE_10 ( ADVERTISE_10FULL | ADVERTISE_10HALF ) + +#define ADVERTISE_1000 0x0300 + +/*#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 IEEE_CONTROL_REG_OFFSET 0 +#define IEEE_STATUS_REG_OFFSET 1 +#define IEEE_AUTONEGO_ADVERTISE_REG 4 +#define IEEE_PARTNER_ABILITIES_1_REG_OFFSET 5 +#define IEEE_PARTNER_ABILITIES_2_REG_OFFSET 8 +#define IEEE_PARTNER_ABILITIES_3_REG_OFFSET 10 +#define IEEE_1000_ADVERTISE_REG_OFFSET 9 +#define IEEE_MMD_ACCESS_CONTROL_REG 13 +#define IEEE_MMD_ACCESS_ADDRESS_DATA_REG 14 +#define IEEE_COPPER_SPECIFIC_CONTROL_REG 16 +#define IEEE_SPECIFIC_STATUS_REG 17 +#define IEEE_COPPER_SPECIFIC_STATUS_REG_2 19 +#define IEEE_EXT_PHY_SPECIFIC_CONTROL_REG 20 +#define IEEE_CONTROL_REG_MAC 21 +#define IEEE_PAGE_ADDRESS_REGISTER 22 + +#define IEEE_CTRL_1GBPS_LINKSPEED_MASK 0x2040 +#define IEEE_CTRL_LINKSPEED_MASK 0x0040 +#define IEEE_CTRL_LINKSPEED_1000M 0x0040 +#define IEEE_CTRL_LINKSPEED_100M 0x2000 +#define IEEE_CTRL_LINKSPEED_10M 0x0000 +#define IEEE_CTRL_FULL_DUPLEX 0x100 +#define IEEE_CTRL_RESET_MASK 0x8000 +#define IEEE_CTRL_AUTONEGOTIATE_ENABLE 0x1000 +#define IEEE_STAT_AUTONEGOTIATE_CAPABLE 0x0008 +#define IEEE_STAT_AUTONEGOTIATE_COMPLETE 0x0020 +#define IEEE_STAT_AUTONEGOTIATE_RESTART 0x0200 +#define IEEE_STAT_LINK_STATUS 0x0004 +#define IEEE_STAT_1GBPS_EXTENSIONS 0x0100 +#define IEEE_AN1_ABILITY_MASK 0x1FE0 +#define IEEE_AN3_ABILITY_MASK_1GBPS 0x0C00 +#define IEEE_AN1_ABILITY_MASK_100MBPS 0x0380 +#define IEEE_AN1_ABILITY_MASK_10MBPS 0x0060 +#define IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK 0x0030 + +#define IEEE_SPEED_MASK 0xC000 +#define IEEE_SPEED_1000 0x8000 +#define IEEE_SPEED_100 0x4000 + +#define IEEE_ASYMMETRIC_PAUSE_MASK 0x0800 +#define IEEE_PAUSE_MASK 0x0400 +#define IEEE_AUTONEG_ERROR_MASK 0x8000 + +#define IEEE_MMD_ACCESS_CTRL_DEVAD_MASK 0x1F +#define IEEE_MMD_ACCESS_CTRL_PIDEVAD_MASK 0x801F +#define IEEE_MMD_ACCESS_CTRL_NOPIDEVAD_MASK 0x401F + +#define PHY_DETECT_REG 1 +#define PHY_IDENTIFIER_1_REG 2 +#define PHY_IDENTIFIER_2_REG 3 +#define PHY_DETECT_MASK 0x1808 +#define PHY_MARVELL_IDENTIFIER 0x0141 +#define PHY_TI_IDENTIFIER 0x2000 +#define PHY_REALTEK_IDENTIFIER 0x001c +#define PHY_AR8035_IDENTIFIER 0x004D +#define PHY_XILINX_PCS_PMA_ID1 0x0174 +#define PHY_XILINX_PCS_PMA_ID2 0x0C00 + +#define XEMACPS_GMII2RGMII_SPEED1000_FD 0x140 +#define XEMACPS_GMII2RGMII_SPEED100_FD 0x2100 +#define XEMACPS_GMII2RGMII_SPEED10_FD 0x100 +#define XEMACPS_GMII2RGMII_REG_NUM 0x10 + +#define PHY_REGCR 0x0D +#define PHY_ADDAR 0x0E +#define PHY_RGMIIDCTL 0x86 +#define PHY_RGMIICTL 0x32 +#define PHY_STS 0x11 +#define PHY_TI_CR 0x10 +#define PHY_TI_CFG4 0x31 + +#define PHY_REGCR_ADDR 0x001F +#define PHY_REGCR_DATA 0x401F +#define PHY_TI_CRVAL 0x5048 +#define PHY_TI_CFG4RESVDBIT7 0x80 + +/* Frequency setting */ +#define SLCR_LOCK_ADDR ( XPS_SYS_CTRL_BASEADDR + 0x4 ) +#define SLCR_UNLOCK_ADDR ( XPS_SYS_CTRL_BASEADDR + 0x8 ) +#define SLCR_GEM0_CLK_CTRL_ADDR ( XPS_SYS_CTRL_BASEADDR + 0x140 ) +#define SLCR_GEM1_CLK_CTRL_ADDR ( XPS_SYS_CTRL_BASEADDR + 0x144 ) +#ifdef PEEP + #define SLCR_GEM_10M_CLK_CTRL_VALUE 0x00103031 + #define SLCR_GEM_100M_CLK_CTRL_VALUE 0x00103001 + #define SLCR_GEM_1G_CLK_CTRL_VALUE 0x00103011 +#endif +#define SLCR_GEM_SRCSEL_EMIO 0x40 +#define SLCR_LOCK_KEY_VALUE 0x767B +#define SLCR_UNLOCK_KEY_VALUE 0xDF0D +#define SLCR_ADDR_GEM_RST_CTRL ( XPS_SYS_CTRL_BASEADDR + 0x214 ) +#define EMACPS_SLCR_DIV_MASK 0xFC0FC0FF + +#define ZYNQ_EMACPS_0_BASEADDR 0xE000B000 +#define ZYNQ_EMACPS_1_BASEADDR 0xE000C000 + +#define ZYNQMP_EMACPS_0_BASEADDR 0xFF0B0000 +#define ZYNQMP_EMACPS_1_BASEADDR 0xFF0C0000 +#define ZYNQMP_EMACPS_2_BASEADDR 0xFF0D0000 +#define ZYNQMP_EMACPS_3_BASEADDR 0xFF0E0000 + +#define CRL_APB_GEM0_REF_CTRL 0xFF5E0050 +#define CRL_APB_GEM1_REF_CTRL 0xFF5E0054 +#define CRL_APB_GEM2_REF_CTRL 0xFF5E0058 +#define CRL_APB_GEM3_REF_CTRL 0xFF5E005C + +#define CRL_APB_GEM_DIV0_MASK 0x00003F00 +#define CRL_APB_GEM_DIV0_SHIFT 8 +#define CRL_APB_GEM_DIV1_MASK 0x003F0000 +#define CRL_APB_GEM_DIV1_SHIFT 16 + +#define VERSAL_EMACPS_0_BASEADDR 0xFF0C0000 +#define VERSAL_EMACPS_1_BASEADDR 0xFF0D0000 + +#define VERSAL_CRL_GEM0_REF_CTRL 0xFF5E0118 +#define VERSAL_CRL_GEM1_REF_CTRL 0xFF5E011C + +#define VERSAL_CRL_GEM_DIV_MASK 0x0003FF00 +#define VERSAL_CRL_APB_GEM_DIV_SHIFT 8 + + +#define GEM_VERSION_ZYNQMP 7 +#define GEM_VERSION_VERSAL 0x107 + +u32 phymapemac0[ 32 ]; +u32 phymapemac1[ 32 ]; + +static uint16_t prvAR803x_debug_reg_read( XEmacPs * xemacpsp, + uint32_t phy_addr, + u16 reg ); +static uint16_t prvAR803x_debug_reg_write( XEmacPs * xemacpsp, + uint32_t phy_addr, + u16 reg, + u16 value ); +static int prvAR803x_debug_reg_mask( XEmacPs * xemacpsp, + uint32_t phy_addr, + u16 reg, + u16 clear, + u16 set ); +static void prvSET_AR803x_TX_Timing( XEmacPs * xemacpsp, + uint32_t phy_addr ); + +uint32_t ulDetecPHY( XEmacPs * xemacpsp ) +{ + u16 PhyReg1; + u16 PhyReg2; + u32 phy_addr; + u32 Status; + + for( phy_addr = phyMIN_PHY_ADDRESS; phy_addr <= phyMAX_PHY_ADDRESS; phy_addr++ ) + { + Status = XEmacPs_PhyRead( xemacpsp, phy_addr, PHY_IDENTIFIER_1_REG, &PhyReg1 ); + Status |= XEmacPs_PhyRead( xemacpsp, phy_addr, PHY_IDENTIFIER_2_REG, &PhyReg2 ); + + if( ( Status == XST_SUCCESS ) && + ( PhyReg1 > 0x0000 ) && ( PhyReg1 < 0xffff ) && + ( PhyReg2 > 0x0000 ) && ( PhyReg2 < 0xffff ) ) + { + /* Found a valid PHY address */ + break; + } + } + + return ( phy_addr <= phyMAX_PHY_ADDRESS ) ? phy_addr : ~0U; +} + + + +unsigned configure_IEEE_phy_speed_US( XEmacPs * xemacpsp, + unsigned speed, + u32 phy_addr ) +{ + u16 control; + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2 ); + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control ); + control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control ); + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0 ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control ); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + control &= ~IEEE_CTRL_LINKSPEED_1000M; + control &= ~IEEE_CTRL_LINKSPEED_100M; + control &= ~IEEE_CTRL_LINKSPEED_10M; + + if( speed == 1000 ) + { + control |= IEEE_CTRL_LINKSPEED_1000M; + } + + else if( speed == 100 ) + { + control |= IEEE_CTRL_LINKSPEED_100M; + /* Dont advertise PHY speed of 1000 Mbps */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, 0 ); + /* Dont advertise PHY speed of 10 Mbps */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_100 ); + } + + else if( speed == 10 ) + { + control |= IEEE_CTRL_LINKSPEED_10M; + /* Dont advertise PHY speed of 1000 Mbps */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, 0 ); + /* Dont advertise PHY speed of 100 Mbps */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_10 ); + } + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, + control | IEEE_CTRL_RESET_MASK ); + { + volatile int wait; + + for( wait = 0; wait < 100000; wait++ ) + { + } + } + return 0; +} + +static uint32_t get_TI_phy_speed( XEmacPs * xemacpsp, + uint32_t phy_addr ) +{ + uint16_t control; + uint16_t status; + uint16_t status_speed; + uint32_t timeout_counter = 0; + uint32_t phyregtemp; + int i; + uint32_t RetStatus; + + XEmacPs_PhyRead( xemacpsp, phy_addr, 0x1F, ( uint16_t * ) &phyregtemp ); + phyregtemp |= 0x4000; + XEmacPs_PhyWrite( xemacpsp, phy_addr, 0x1F, phyregtemp ); + RetStatus = XEmacPs_PhyRead( xemacpsp, phy_addr, 0x1F, ( uint16_t * ) &phyregtemp ); + + if( RetStatus != XST_SUCCESS ) + { + FreeRTOS_printf( ( "Error during sw reset \n\r" ) ); + return XST_FAILURE; + } + + XEmacPs_PhyRead( xemacpsp, phy_addr, 0, ( uint16_t * ) &phyregtemp ); + phyregtemp |= 0x8000; + XEmacPs_PhyWrite( xemacpsp, phy_addr, 0, phyregtemp ); + + /* + * Delay + */ + for( i = 0; i < 1000000000; i++ ) + { + } + + RetStatus = XEmacPs_PhyRead( xemacpsp, phy_addr, 0, ( uint16_t * ) &phyregtemp ); + + if( RetStatus != XST_SUCCESS ) + { + FreeRTOS_printf( ( "Error during reset \n\r" ) ); + return XST_FAILURE; + } + + /* FIFO depth */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_TI_CR, PHY_TI_CRVAL ); + RetStatus = XEmacPs_PhyRead( xemacpsp, phy_addr, PHY_TI_CR, ( uint16_t * ) &phyregtemp ); + + if( RetStatus != XST_SUCCESS ) + { + FreeRTOS_printf( ( "Error writing to 0x10 \n\r" ) ); + return XST_FAILURE; + } + + /* TX/RX tuning */ + /* Write to PHY_RGMIIDCTL */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_ADDAR, PHY_RGMIIDCTL ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA ); + RetStatus = XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_ADDAR, 0xA8 ); + + if( RetStatus != XST_SUCCESS ) + { + FreeRTOS_printf( ( "Error in tuning" ) ); + return XST_FAILURE; + } + + /* Read PHY_RGMIIDCTL */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_ADDAR, PHY_RGMIIDCTL ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA ); + RetStatus = XEmacPs_PhyRead( xemacpsp, phy_addr, PHY_ADDAR, ( uint16_t * ) &phyregtemp ); + + if( RetStatus != XST_SUCCESS ) + { + FreeRTOS_printf( ( "Error in tuning" ) ); + return XST_FAILURE; + } + + /* Write PHY_RGMIICTL */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_ADDAR, PHY_RGMIICTL ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA ); + RetStatus = XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_ADDAR, 0xD3 ); + + if( RetStatus != XST_SUCCESS ) + { + FreeRTOS_printf( ( "Error in tuning" ) ); + return XST_FAILURE; + } + + /* Read PHY_RGMIICTL */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_ADDAR, PHY_RGMIICTL ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA ); + RetStatus = XEmacPs_PhyRead( xemacpsp, phy_addr, PHY_ADDAR, ( uint16_t * ) &phyregtemp ); + + if( RetStatus != XST_SUCCESS ) + { + FreeRTOS_printf( ( "Error in tuning" ) ); + return XST_FAILURE; + } + + /* SW workaround for unstable link when RX_CTRL is not STRAP MODE 3 or 4 */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_ADDAR, PHY_TI_CFG4 ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA ); + RetStatus = XEmacPs_PhyRead( xemacpsp, phy_addr, PHY_ADDAR, ( uint16_t * ) &phyregtemp ); + phyregtemp &= ~( PHY_TI_CFG4RESVDBIT7 ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_ADDAR, PHY_TI_CFG4 ); + XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA ); + RetStatus = XEmacPs_PhyWrite( xemacpsp, phy_addr, PHY_ADDAR, phyregtemp ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control ); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + control |= ADVERTISE_100; + control |= ADVERTISE_10; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + &control ); + control |= ADVERTISE_1000; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status ); + + FreeRTOS_printf( ( "Waiting for PHY to complete autonegotiation.\n" ) ); + + while( !( status & IEEE_STAT_AUTONEGOTIATE_COMPLETE ) ) + { + my_sleep( 1 ); + timeout_counter++; + + if( timeout_counter == 30 ) + { + FreeRTOS_printf( ( "Auto negotiation error \n" ) ); + return XST_FAILURE; + } + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status ); + } + + FreeRTOS_printf( ( "autonegotiation complete \n" ) ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, PHY_STS, &status_speed ); + + if( ( status_speed & 0xC000 ) == 0x8000 ) + { + return 1000; + } + else if( ( status_speed & 0xC000 ) == 0x4000 ) + { + return 100; + } + else + { + return 10; + } + + return XST_SUCCESS; +} + +static uint32_t get_Marvell_phy_speed( XEmacPs * xemacpsp, + uint32_t phy_addr ) +{ + uint16_t temp; + uint16_t control; + uint16_t status; + uint16_t status_speed; + uint32_t timeout_counter = 0; + uint32_t temp_speed; + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2 ); + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control ); + control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control ); + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0 ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control ); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + control |= ADVERTISE_100; + control |= ADVERTISE_10; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + &control ); + control |= ADVERTISE_1000; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + control ); + + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0 ); + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, + &control ); + control |= ( 7 << 12 ); /* max number of gigabit attempts */ + control |= ( 1 << 11 ); /* enable downshift */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, + control ); + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + control |= IEEE_CTRL_RESET_MASK; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control ); + + while( 1 ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + + if( control & IEEE_CTRL_RESET_MASK ) + { + continue; + } + else + { + break; + } + } + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status ); + + FreeRTOS_printf( ( "Waiting for PHY to complete autonegotiation.\n" ) ); + + while( !( status & IEEE_STAT_AUTONEGOTIATE_COMPLETE ) ) + { + my_sleep( 1 ); + XEmacPs_PhyRead( xemacpsp, phy_addr, + IEEE_COPPER_SPECIFIC_STATUS_REG_2, &temp ); + timeout_counter++; + + if( timeout_counter == 30 ) + { + FreeRTOS_printf( ( "Auto negotiation error \n" ) ); + return XST_FAILURE; + } + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status ); + } + + FreeRTOS_printf( ( "autonegotiation complete \n" ) ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_SPECIFIC_STATUS_REG, + &status_speed ); + + if( status_speed & 0x400 ) + { + temp_speed = status_speed & IEEE_SPEED_MASK; + + if( temp_speed == IEEE_SPEED_1000 ) + { + return 1000; + } + else if( temp_speed == IEEE_SPEED_100 ) + { + return 100; + } + else + { + return 10; + } + } + + return XST_SUCCESS; +} + +static uint32_t get_Realtek_phy_speed( XEmacPs * xemacpsp, + uint32_t phy_addr ) +{ + uint16_t control; + uint16_t status; + uint16_t status_speed; + uint32_t timeout_counter = 0; + uint32_t temp_speed; + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control ); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + control |= ADVERTISE_100; + control |= ADVERTISE_10; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + &control ); + control |= ADVERTISE_1000; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + control |= IEEE_CTRL_RESET_MASK; + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control ); + + while( 1 ) + { + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control ); + + if( control & IEEE_CTRL_RESET_MASK ) + { + continue; + } + else + { + break; + } + } + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status ); + + FreeRTOS_printf( ( "Waiting for PHY to complete autonegotiation.\n" ) ); + + while( !( status & IEEE_STAT_AUTONEGOTIATE_COMPLETE ) ) + { + my_sleep( 1 ); + timeout_counter++; + + if( timeout_counter == 30 ) + { + FreeRTOS_printf( ( "Auto negotiation error \n" ) ); + return XST_FAILURE; + } + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status ); + } + + FreeRTOS_printf( ( "autonegotiation complete \n" ) ); + + XEmacPs_PhyRead( xemacpsp, phy_addr, IEEE_SPECIFIC_STATUS_REG, + &status_speed ); + + if( status_speed & 0x400 ) + { + temp_speed = status_speed & IEEE_SPEED_MASK; + + if( temp_speed == IEEE_SPEED_1000 ) + { + return 1000; + } + else if( temp_speed == IEEE_SPEED_100 ) + { + return 100; + } + else + { + return 10; + } + } + + return XST_FAILURE; +} + +/* Here is a XEmacPs_PhyRead() that returns the value of a register. */ +static uint16_t XEmacPs_PhyRead2( XEmacPs * InstancePtr, + u32 PhyAddress, + u32 RegisterNum ) +{ + LONG lResult; + uint16_t usReturn = 0U; + + lResult = XEmacPs_PhyRead( InstancePtr, PhyAddress, RegisterNum, &( usReturn ) ); + + if( lResult != ( LONG ) ( XST_SUCCESS ) ) + { + usReturn = 0U; + } + + return usReturn; +} + +static uint32_t ar8035CheckStatus( XEmacPs * xemacpsp, + uint32_t phy_addr ); + +static void prvSET_AR803x_TX_Timing( XEmacPs * xemacpsp, + uint32_t phy_addr ) +{ +/* + * rgmii_tx_clk_dly: tx clock delay control bit: + * 1 = rgmii tx clock delay enable <<= this option + * 0 = rgmii tx clock delay disable. + * + * Gtx_dly_val: select the delay of gtx_clk. + * 00:0.25ns + * 01:1.3ns <<= this option + * 10:2.4ns + * 11:3.4ns + */ + prvAR803x_debug_reg_write( xemacpsp, phy_addr, 0x5, 0x2d47 ); + prvAR803x_debug_reg_write( xemacpsp, phy_addr, 0xb, 0xbc20 ); +} + +static uint32_t get_AR8035_phy_speed( XEmacPs * xemacpsp, + uint32_t phy_addr ) +{ + uint32_t timeout_counter = 0; + + /*Reset PHY transceiver */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, IEEE_CTRL_RESET_MASK ); + + /*Wait for the reset to complete */ + while( ( XEmacPs_PhyRead2( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET ) & IEEE_CTRL_RESET_MASK ) != 0U ) + { + } + + /*Basic mode control register */ + /* IEEE_CTRL_LINKSPEED_100M, also known as 'AR8035_BMCR_SPEED_SEL_LSB' */ + /* IEEE_STAT_1GBPS_EXTENSIONS also known as: AR8035_BMCR_DUPLEX_MODE' */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, IEEE_CTRL_LINKSPEED_100M | + IEEE_CTRL_AUTONEGOTIATE_ENABLE | IEEE_STAT_1GBPS_EXTENSIONS ); + +#define AR8035_ANAR_SELECTOR_DEFAULT 0x0001 + + /*Auto-negotiation advertisement register */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, + IEEE_CTRL_AUTONEGOTIATE_ENABLE | + IEEE_ASYMMETRIC_PAUSE_MASK | + IEEE_PAUSE_MASK | + ADVERTISE_100FULL | + ADVERTISE_100HALF | + ADVERTISE_10FULL | + ADVERTISE_10HALF | + AR8035_ANAR_SELECTOR_DEFAULT ); + + /*1000 BASE-T control register */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, ADVERTISE_1000FULL ); + +#define AR8035_FUNC_CTRL 0x10 +#define AR8035_FUNC_CTRL_ASSERT_CRS_ON_TX 0x0800 +#define AR8035_FUNC_CTRL_MDIX_MODE 0x0060 +#define AR8035_FUNC_CTRL_MDIX_MODE_MANUAL_MDI 0x0000 +#define AR8035_FUNC_CTRL_MDIX_MODE_MANUAL_MDIX 0x0020 +#define AR8035_FUNC_CTRL_MDIX_MODE_AUTO 0x0060 +#define AR8035_FUNC_CTRL_SQE_TEST 0x0004 +#define AR8035_FUNC_CTRL_POLARITY_REVERSAL 0x0002 +#define AR8035_FUNC_CTRL_JABBER_DIS 0x0001 + + /*Function control register */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, AR8035_FUNC_CTRL, + AR8035_FUNC_CTRL_ASSERT_CRS_ON_TX | AR8035_FUNC_CTRL_MDIX_MODE_AUTO | + AR8035_FUNC_CTRL_POLARITY_REVERSAL ); + + /*Dump PHY registers for debugging purpose */ +/* ar8035DumpPhyReg(interface); */ + +#define AR8035_INT_EN 0x12 +#define AR8035_INT_STATUS_LINK_FAIL 0x0800 +#define AR8035_INT_STATUS_LINK_SUCCESS 0x0400 + + /*The PHY will generate interrupts when link status changes are detected */ + XEmacPs_PhyWrite( xemacpsp, phy_addr, AR8035_INT_EN, AR8035_INT_STATUS_LINK_FAIL | + AR8035_INT_STATUS_LINK_SUCCESS ); + + while( pdTRUE ) + { + uint32_t status; + my_sleep( 1 ); + + timeout_counter++; + + if( timeout_counter == 30 ) + { + FreeRTOS_printf( ( "Auto negotiation error \n" ) ); + return XST_FAILURE; + } + + status = ar8035CheckStatus( xemacpsp, phy_addr ); + + if( status > 10 ) + { + prvSET_AR803x_TX_Timing( xemacpsp, phy_addr ); + return status; + } + } +} + +static void ar8035Tick( XEmacPs * xemacpsp, + uint32_t phy_addr ) +{ + uint16_t value; + BaseType_t linkState; + + /*Read basic status register */ + value = XEmacPs_PhyRead2( xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET ); + /*Retrieve current link state */ + linkState = ( value & IEEE_STAT_LINK_STATUS ) ? TRUE : FALSE; +} + +#define AR803X_DEBUG_ADDR 0x1D +#define AR803X_DEBUG_DATA 0x1E +static uint16_t prvAR803x_debug_reg_read( XEmacPs * xemacpsp, + uint32_t phy_addr, + u16 reg ) +{ + XEmacPs_PhyWrite( xemacpsp, phy_addr, AR803X_DEBUG_ADDR, reg ); + + return XEmacPs_PhyRead2( xemacpsp, phy_addr, AR803X_DEBUG_DATA ); +} + +static uint16_t prvAR803x_debug_reg_write( XEmacPs * xemacpsp, + uint32_t phy_addr, + u16 reg, + u16 value ) +{ + XEmacPs_PhyWrite( xemacpsp, phy_addr, AR803X_DEBUG_ADDR, reg ); + + return XEmacPs_PhyWrite( xemacpsp, phy_addr, AR803X_DEBUG_DATA, value ); +} + +static int prvAR803x_debug_reg_mask( XEmacPs * xemacpsp, + uint32_t phy_addr, + u16 reg, + u16 clear, + u16 set ) +{ + u16 val; + int ret; + + ret = prvAR803x_debug_reg_read( xemacpsp, phy_addr, reg ); + + if( ret < 0 ) + { + return ret; + } + + val = ret & 0xffff; + val &= ~clear; + val |= set; + + return XEmacPs_PhyWrite( xemacpsp, phy_addr, AR803X_DEBUG_DATA, val ); +} + +static uint32_t ar8035CheckStatus( XEmacPs * xemacpsp, + uint32_t phy_addr ) +{ + uint16_t status; + uint32_t linkSpeed = 0U; + BaseType_t linkState = pdFALSE; + + /*Read status register to acknowledge the interrupt */ + status = XEmacPs_PhyRead2( xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_STATUS_REG_2 ); + +#define AR8035_INT_STATUS_LINK_FAIL 0x0800 +#define AR8035_INT_STATUS_LINK_SUCCESS 0x0400 + + /*Link status change? */ + if( status & ( AR8035_INT_STATUS_LINK_FAIL | AR8035_INT_STATUS_LINK_SUCCESS ) ) + { + /*Read PHY status register */ + status = XEmacPs_PhyRead2( xemacpsp, phy_addr, IEEE_SPECIFIC_STATUS_REG ); + +#define AR8035_PHY_STATUS_LINK 0x0400 + + /*Link is up? */ + if( status & AR8035_PHY_STATUS_LINK ) + { +#define AR8035_PHY_STATUS_SPEED 0xC000 +#define AR8035_PHY_STATUS_SPEED_10MBPS 0x0000 +#define AR8035_PHY_STATUS_SPEED_100MBPS 0x4000 +#define AR8035_PHY_STATUS_SPEED_1000MBPS 0x8000 + + /*Check current speed */ + switch( status & AR8035_PHY_STATUS_SPEED ) + { + /*10BASE-T */ + case AR8035_PHY_STATUS_SPEED_10MBPS: + linkSpeed = 10; + break; + + /*100BASE-TX */ + case AR8035_PHY_STATUS_SPEED_100MBPS: + linkSpeed = 100; + break; + + /*1000BASE-T */ + case AR8035_PHY_STATUS_SPEED_1000MBPS: + linkSpeed = 1000; + break; + + /*Unknown speed */ + default: + /*Debug message */ + FreeRTOS_printf( ( "Invalid speed ( status %04X )\n", status ) ); + break; + } + +#define AR8035_PHY_STATUS_DUPLEX 0x2000 + + /*Check current duplex mode */ + if( status & AR8035_PHY_STATUS_DUPLEX ) + { + FreeRTOS_printf( ( "Full duplex\n" ) ); + } + else + { + FreeRTOS_printf( ( "Half duplex\n" ) ); + } + + /*Update link state */ + linkState = TRUE; + + /*Adjust MAC configuration parameters for proper operation */ + /*interface->nicDriver->updateMacConfig(interface); */ + } + else + { + /*Update link state */ + linkState = FALSE; + } + + /*Process link state change event */ +/* nicNotifyLinkChange(interface); */ + } + + return linkSpeed; +} + + +static const char * pcGetPHIName( uint16_t usID ) +{ + const char * pcReturn = ""; + static char pcName[ 16 ]; + + switch( usID ) + { + case PHY_TI_IDENTIFIER: + pcReturn = "TI_dp83869hm"; + break; + + case PHY_REALTEK_IDENTIFIER: + pcReturn = "Realtek RTL8212"; + break; + + case PHY_AR8035_IDENTIFIER: + pcReturn = "Atheros_ar8035"; + break; + + case PHY_MARVELL_IDENTIFIER: + pcReturn = "Marvell_88E1512"; + break; + + default: + snprintf( pcName, sizeof pcName, "Unkkwn PHY %04X", usID ); + pcReturn = pcName; + break; + } + + return pcReturn; +} + +static uint32_t get_IEEE_phy_speed_US( XEmacPs * xemacpsp, + uint32_t phy_addr ) +{ + uint16_t phy_identity; + uint32_t RetStatus; + + XEmacPs_PhyRead( xemacpsp, phy_addr, PHY_IDENTIFIER_1_REG, + &phy_identity ); + + FreeRTOS_printf( ( "Start %s PHY autonegotiation. ID = 0x%04X\n", pcGetPHIName( phy_identity ), phy_identity ) ); + + switch( phy_identity ) + { + case PHY_TI_IDENTIFIER: + RetStatus = get_TI_phy_speed( xemacpsp, phy_addr ); + break; + + case PHY_REALTEK_IDENTIFIER: + RetStatus = get_Realtek_phy_speed( xemacpsp, phy_addr ); + break; + + case PHY_MARVELL_IDENTIFIER: + RetStatus = get_Marvell_phy_speed( xemacpsp, phy_addr ); + break; + + case PHY_AR8035_IDENTIFIER: + RetStatus = get_AR8035_phy_speed( xemacpsp, phy_addr ); + /* RetStatus = get_Marvell_phy_speed(xemacpsp, phy_addr); */ + /* RetStatus = get_Realtek_phy_speed(xemacpsp, phy_addr); */ + prvSET_AR803x_TX_Timing( xemacpsp, phy_addr ); + break; + + default: + FreeRTOS_printf( ( "Don't know how to handle PHY ID %04X\n", phy_identity ) ); + RetStatus = XST_FAILURE; + break; + } + + return RetStatus; +} + +static void SetUpSLCRDivisors( u32 mac_baseaddr, + s32 speed ) +{ + volatile u32 slcrBaseAddress; + u32 SlcrDiv0 = 0; + u32 SlcrDiv1 = 0; + u32 SlcrTxClkCntrl; + u32 gigeversion; + volatile u32 CrlApbBaseAddr; + u32 CrlApbDiv0 = 0; + u32 CrlApbDiv1 = 0; + u32 CrlApbGemCtrl; + + gigeversion = ( ( Xil_In32( mac_baseaddr + 0xFC ) ) >> 16 ) & 0xFFF; + + if( gigeversion == 2 ) + { + *( volatile u32 * ) ( SLCR_UNLOCK_ADDR ) = SLCR_UNLOCK_KEY_VALUE; + + if( mac_baseaddr == ZYNQ_EMACPS_0_BASEADDR ) + { + slcrBaseAddress = SLCR_GEM0_CLK_CTRL_ADDR; + } + else + { + slcrBaseAddress = SLCR_GEM1_CLK_CTRL_ADDR; + } + + if( ( *( volatile u32 * ) ( UINTPTR ) ( slcrBaseAddress ) ) & + SLCR_GEM_SRCSEL_EMIO ) + { + return; + } + + if( speed == 1000 ) + { + if( mac_baseaddr == XPAR_XEMACPS_0_BASEADDR ) + { + #ifdef XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV1; + #endif + } + else + { + #ifdef XPAR_PS7_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_1_ENET_SLCR_1000MBPS_DIV1; + #endif + } + } + else if( speed == 100 ) + { + if( mac_baseaddr == XPAR_XEMACPS_0_BASEADDR ) + { + #ifdef XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV1; + #endif + } + else + { + #ifdef XPAR_PS7_ETHERNET_1_ENET_SLCR_100MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_1_ENET_SLCR_100MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_1_ENET_SLCR_100MBPS_DIV1; + #endif + } + } + else + { + if( mac_baseaddr == XPAR_XEMACPS_0_BASEADDR ) + { + #ifdef XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV1; + #endif + } + else + { + #ifdef XPAR_PS7_ETHERNET_1_ENET_SLCR_10MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_1_ENET_SLCR_10MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_1_ENET_SLCR_10MBPS_DIV1; + #endif + } + } + + if( ( SlcrDiv0 != 0 ) && ( SlcrDiv1 != 0 ) ) + { + SlcrTxClkCntrl = *( volatile u32 * ) ( UINTPTR ) ( slcrBaseAddress ); + SlcrTxClkCntrl &= EMACPS_SLCR_DIV_MASK; + SlcrTxClkCntrl |= ( SlcrDiv1 << 20 ); + SlcrTxClkCntrl |= ( SlcrDiv0 << 8 ); + *( volatile u32 * ) ( UINTPTR ) ( slcrBaseAddress ) = SlcrTxClkCntrl; + *( volatile u32 * ) ( SLCR_LOCK_ADDR ) = SLCR_LOCK_KEY_VALUE; + } + else + { + FreeRTOS_printf( ( "Clock Divisors incorrect - Please check\n" ) ); + } + } + else if( gigeversion == GEM_VERSION_ZYNQMP ) + { + /* Setup divisors in CRL_APB for Zynq Ultrascale+ MPSoC */ + if( mac_baseaddr == ZYNQMP_EMACPS_0_BASEADDR ) + { + CrlApbBaseAddr = CRL_APB_GEM0_REF_CTRL; + } + else if( mac_baseaddr == ZYNQMP_EMACPS_1_BASEADDR ) + { + CrlApbBaseAddr = CRL_APB_GEM1_REF_CTRL; + } + else if( mac_baseaddr == ZYNQMP_EMACPS_2_BASEADDR ) + { + CrlApbBaseAddr = CRL_APB_GEM2_REF_CTRL; + } + else if( mac_baseaddr == ZYNQMP_EMACPS_3_BASEADDR ) + { + CrlApbBaseAddr = CRL_APB_GEM3_REF_CTRL; + } + + if( speed == 1000 ) + { + if( mac_baseaddr == ZYNQMP_EMACPS_0_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_0_ENET_SLCR_1000MBPS_DIV1; + #endif + } + else if( mac_baseaddr == ZYNQMP_EMACPS_1_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_1_ENET_SLCR_1000MBPS_DIV1; + #endif + } + else if( mac_baseaddr == ZYNQMP_EMACPS_2_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_2_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_2_ENET_SLCR_1000MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_2_ENET_SLCR_1000MBPS_DIV1; + #endif + } + else if( mac_baseaddr == ZYNQMP_EMACPS_3_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_3_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_3_ENET_SLCR_1000MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_3_ENET_SLCR_1000MBPS_DIV1; + #endif + } + } + else if( speed == 100 ) + { + if( mac_baseaddr == ZYNQMP_EMACPS_0_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_0_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_0_ENET_SLCR_100MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_0_ENET_SLCR_100MBPS_DIV1; + #endif + } + else if( mac_baseaddr == ZYNQMP_EMACPS_1_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_1_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_1_ENET_SLCR_100MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_1_ENET_SLCR_100MBPS_DIV1; + #endif + } + else if( mac_baseaddr == ZYNQMP_EMACPS_2_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_2_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_2_ENET_SLCR_100MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_2_ENET_SLCR_100MBPS_DIV1; + #endif + } + else if( mac_baseaddr == ZYNQMP_EMACPS_3_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_3_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_3_ENET_SLCR_100MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_3_ENET_SLCR_100MBPS_DIV1; + #endif + } + } + else + { + if( mac_baseaddr == ZYNQMP_EMACPS_0_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_0_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_0_ENET_SLCR_10MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_0_ENET_SLCR_10MBPS_DIV1; + #endif + } + else if( mac_baseaddr == ZYNQMP_EMACPS_1_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_1_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_1_ENET_SLCR_10MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_1_ENET_SLCR_10MBPS_DIV1; + #endif + } + else if( mac_baseaddr == ZYNQMP_EMACPS_2_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_2_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_2_ENET_SLCR_10MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_2_ENET_SLCR_10MBPS_DIV1; + #endif + } + else if( mac_baseaddr == ZYNQMP_EMACPS_3_BASEADDR ) + { + #ifdef XPAR_PSU_ETHERNET_3_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_3_ENET_SLCR_10MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_3_ENET_SLCR_10MBPS_DIV1; + #endif + } + } + + if( ( CrlApbDiv0 != 0 ) && ( CrlApbDiv1 != 0 ) ) + { + #if EL1_NONSECURE + XSmc_OutVar RegRead; + RegRead = Xil_Smc( MMIO_READ_SMC_FID, ( u64 ) ( CrlApbBaseAddr ), + 0, 0, 0, 0, 0, 0 ); + CrlApbGemCtrl = RegRead.Arg0 >> 32; + #else + CrlApbGemCtrl = *( volatile u32 * ) ( UINTPTR ) ( CrlApbBaseAddr ); + #endif + CrlApbGemCtrl &= ~CRL_APB_GEM_DIV0_MASK; + CrlApbGemCtrl |= CrlApbDiv0 << CRL_APB_GEM_DIV0_SHIFT; + CrlApbGemCtrl &= ~CRL_APB_GEM_DIV1_MASK; + CrlApbGemCtrl |= CrlApbDiv1 << CRL_APB_GEM_DIV1_SHIFT; + #if EL1_NONSECURE + Xil_Smc( MMIO_WRITE_SMC_FID, ( u64 ) ( CrlApbBaseAddr ) | ( ( u64 ) ( 0xFFFFFFFF ) << 32 ), + ( u64 ) CrlApbGemCtrl, 0, 0, 0, 0, 0 ); + + do + { + RegRead = Xil_Smc( MMIO_READ_SMC_FID, ( u64 ) ( CrlApbBaseAddr ), + 0, 0, 0, 0, 0, 0 ); + } while( ( RegRead.Arg0 >> 32 ) != CrlApbGemCtrl ); + #else + *( volatile u32 * ) ( UINTPTR ) ( CrlApbBaseAddr ) = CrlApbGemCtrl; + #endif + } + else + { + FreeRTOS_printf( ( "Clock Divisors incorrect - Please check\n" ) ); + } + } + else if( gigeversion == GEM_VERSION_VERSAL ) + { + /* Setup divisors in CRL for Versal */ + if( mac_baseaddr == VERSAL_EMACPS_0_BASEADDR ) + { + CrlApbBaseAddr = VERSAL_CRL_GEM0_REF_CTRL; + #if EL1_NONSECURE + ClkId = CLK_GEM0_REF; + #endif + } + else if( mac_baseaddr == VERSAL_EMACPS_1_BASEADDR ) + { + CrlApbBaseAddr = VERSAL_CRL_GEM1_REF_CTRL; + #if EL1_NONSECURE + ClkId = CLK_GEM1_REF; + #endif + } + + if( speed == 1000 ) + { + if( mac_baseaddr == VERSAL_EMACPS_0_BASEADDR ) + { + #ifdef XPAR_PSV_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSV_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0; + #endif + } + else if( mac_baseaddr == VERSAL_EMACPS_1_BASEADDR ) + { + #ifdef XPAR_PSV_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSV_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0; + #endif + } + } + else if( speed == 100 ) + { + if( mac_baseaddr == VERSAL_EMACPS_0_BASEADDR ) + { + #ifdef XPAR_PSV_ETHERNET_0_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSV_ETHERNET_0_ENET_SLCR_100MBPS_DIV0; + #endif + } + else if( mac_baseaddr == VERSAL_EMACPS_1_BASEADDR ) + { + #ifdef XPAR_PSV_ETHERNET_1_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSV_ETHERNET_1_ENET_SLCR_100MBPS_DIV0; + #endif + } + } + else + { + if( mac_baseaddr == VERSAL_EMACPS_0_BASEADDR ) + { + #ifdef XPAR_PSV_ETHERNET_0_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSV_ETHERNET_0_ENET_SLCR_10MBPS_DIV0; + #endif + } + else if( mac_baseaddr == VERSAL_EMACPS_1_BASEADDR ) + { + #ifdef XPAR_PSV_ETHERNET_1_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSV_ETHERNET_1_ENET_SLCR_10MBPS_DIV0; + #endif + } + } + + if( CrlApbDiv0 != 0 ) + { + #if EL1_NONSECURE + Xil_Smc( PM_SET_DIVIDER_SMC_FID, ( ( ( u64 ) CrlApbDiv0 << 32 ) | ClkId ), 0, 0, 0, 0, 0, 0 ); + #else + CrlApbGemCtrl = Xil_In32( ( UINTPTR ) CrlApbBaseAddr ); + CrlApbGemCtrl &= ~VERSAL_CRL_GEM_DIV_MASK; + CrlApbGemCtrl |= CrlApbDiv0 << VERSAL_CRL_APB_GEM_DIV_SHIFT; + + Xil_Out32( ( UINTPTR ) CrlApbBaseAddr, CrlApbGemCtrl ); + #endif + } + else + { + FreeRTOS_printf( ( "Clock Divisors incorrect - Please check\n" ) ); + } + } +} + +u32 Phy_Setup_US( XEmacPs * xemacpsp, + u32 phy_addr ) +{ + u32 link_speed = 0; + u32 conv_present = 0; + u32 convspeeddupsetting = 0; + u32 convphyaddr = 0; + + #ifdef XPAR_GMII2RGMIICON_0N_ETH0_ADDR + convphyaddr = XPAR_GMII2RGMIICON_0N_ETH0_ADDR; + conv_present = 1; + #else + #ifdef XPAR_GMII2RGMIICON_0N_ETH1_ADDR + convphyaddr = XPAR_GMII2RGMIICON_0N_ETH1_ADDR; + conv_present = 1; + #endif + #endif + + #ifdef ipconfigNIC_LINKSPEED_AUTODETECT + link_speed = get_IEEE_phy_speed_US( xemacpsp, phy_addr ); + + if( link_speed == 1000 ) + { + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 1000 ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED1000_FD; + } + else if( link_speed == 100 ) + { + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 100 ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED100_FD; + } + else if( link_speed != XST_FAILURE ) + { + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 10 ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED10_FD; + } + else + { + FreeRTOS_printf( ( "Phy setup error \n" ) ); + return XST_FAILURE; + } + #elif defined( ipconfigNIC_LINKSPEED1000 ) + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 1000, , phy_addr ); + link_speed = 1000; + configure_IEEE_phy_speed_US( xemacpsp, link_speed ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED1000_FD; + my_sleep( 1 ); + #elif defined( ipconfigNIC_LINKSPEED100 ) + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 100 ); + link_speed = 100; + configure_IEEE_phy_speed_US( xemacpsp, link_speed, phy_addr ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED100_FD; + my_sleep( 1 ); + #elif defined( ipconfigNIC_LINKSPEED10 ) + SetUpSLCRDivisors( xemacpsp->Config.BaseAddress, 10 ); + link_speed = 10; + configure_IEEE_phy_speed_US( xemacpsp, link_speed, , phy_addr ); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED10_FD; + my_sleep( 1 ); + #endif /* ifdef ipconfigNIC_LINKSPEED_AUTODETECT */ + + if( conv_present ) + { + XEmacPs_PhyWrite( xemacpsp, convphyaddr, + XEMACPS_GMII2RGMII_REG_NUM, convspeeddupsetting ); + } + + FreeRTOS_printf( ( "link speed: %d\n", link_speed ) ); + return link_speed; +} diff --git a/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_topology.h b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_topology.h new file mode 100644 index 0000000..d6fe110 --- /dev/null +++ b/FreeRTOS/source/portable/NetworkInterface/xilinx_ultrascale/x_topology.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2007-2013 Xilinx, Inc. All rights reserved. + * + * Xilinx, Inc. + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A + * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS + * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR + * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION + * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE + * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. + * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO + * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO + * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE + * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef __XTOPOLOGY_H_ + #define __XTOPOLOGY_H_ + + #ifdef __cplusplus + extern "C" { + #endif + + enum xemac_types + { + xemac_type_unknown = -1, xemac_type_xps_emaclite, xemac_type_xps_ll_temac, xemac_type_axi_ethernet, xemac_type_emacps + }; + + struct xtopology_t + { + unsigned emac_baseaddr; + enum xemac_types emac_type; + unsigned intc_baseaddr; + unsigned intc_emac_intr; /* valid only for xemac_type_xps_emaclite */ + unsigned scugic_baseaddr; /* valid only for Zynq */ + unsigned scugic_emac_intr; /* valid only for GEM */ + }; + + extern int x_topology_n_emacs; + extern struct xtopology_t x_topology[]; + + int x_topology_find_index( unsigned base ); + + #ifdef __cplusplus + } + #endif + +#endif /* ifndef __XTOPOLOGY_H_ */ diff --git a/RTE/Device/GD32F107VC/gd32f107c_eval.c b/RTE/Device/GD32F107VC/gd32f107c_eval.c new file mode 100644 index 0000000..be8feaa --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f107c_eval.c @@ -0,0 +1,203 @@ +/*! + \file gd32f107c_eval.c + \brief firmware functions to manage leds, keys, COM ports +*/ + +/* + Copyright (C) 2017 GigaDevice + + 2014-12-26, V1.0.0, demo for GD32F10x + 2017-06-30, V2.0.0, demo for GD32F10x +*/ + + + +#include "gd32f107c_eval.h" + +/* private variables */ +static uint32_t GPIO_PORT[LEDn] = {LED2_GPIO_PORT, LED3_GPIO_PORT, + LED4_GPIO_PORT, LED5_GPIO_PORT}; +static uint32_t GPIO_PIN[LEDn] = {LED2_PIN, LED3_PIN, LED4_PIN, LED5_PIN}; + +static rcu_periph_enum COM_CLK[COMn] = {EVAL_COM1_CLK, EVAL_COM2_CLK}; +static uint32_t COM_TX_PIN[COMn] = {EVAL_COM1_TX_PIN, EVAL_COM2_TX_PIN}; +static uint32_t COM_RX_PIN[COMn] = {EVAL_COM1_RX_PIN, EVAL_COM2_RX_PIN}; +static uint32_t COM_GPIO_PORT[COMn] = {EVAL_COM1_GPIO_PORT, EVAL_COM2_GPIO_PORT}; +static rcu_periph_enum COM_GPIO_CLK[COMn] = {EVAL_COM1_GPIO_CLK, EVAL_COM2_GPIO_CLK}; + +static rcu_periph_enum GPIO_CLK[LEDn] = {LED2_GPIO_CLK, LED3_GPIO_CLK, + LED4_GPIO_CLK, LED5_GPIO_CLK}; + +static uint32_t KEY_PORT[KEYn] = {WAKEUP_KEY_GPIO_PORT, + TAMPER_KEY_GPIO_PORT, + USER_KEY_GPIO_PORT}; +static uint32_t KEY_PIN[KEYn] = {WAKEUP_KEY_PIN, + TAMPER_KEY_PIN, + USER_KEY_PIN}; +static rcu_periph_enum KEY_CLK[KEYn] = {WAKEUP_KEY_GPIO_CLK, + TAMPER_KEY_GPIO_CLK, + USER_KEY_GPIO_CLK}; +static exti_line_enum KEY_EXTI_LINE[KEYn] = {WAKEUP_KEY_EXTI_LINE, + TAMPER_KEY_EXTI_LINE, + USER_KEY_EXTI_LINE}; +static uint8_t KEY_PORT_SOURCE[KEYn] = {WAKEUP_KEY_EXTI_PORT_SOURCE, + TAMPER_KEY_EXTI_PORT_SOURCE, + USER_KEY_EXTI_PORT_SOURCE}; +static uint8_t KEY_PIN_SOURCE[KEYn] = {WAKEUP_KEY_EXTI_PIN_SOURCE, + TAMPER_KEY_EXTI_PIN_SOURCE, + USER_KEY_EXTI_PIN_SOURCE}; +static uint8_t KEY_IRQn[KEYn] = {WAKEUP_KEY_EXTI_IRQn, + TAMPER_KEY_EXTI_IRQn, + USER_KEY_EXTI_IRQn}; + +/*! + \brief configure led GPIO + \param[in] lednum: specify the led to be configured + \arg LED2 + \arg LED3 + \arg LED4 + \arg LED5 + \param[out] none + \retval none +*/ +void gd_eval_led_init (led_typedef_enum lednum) +{ + /* enable the led clock */ + rcu_periph_clock_enable(GPIO_CLK[lednum]); + /* configure led GPIO port */ + gpio_init(GPIO_PORT[lednum], GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN[lednum]); + + GPIO_BC(GPIO_PORT[lednum]) = GPIO_PIN[lednum]; +} + +/*! + \brief turn on selected led + \param[in] lednum: specify the led to be turned on + \arg LED2 + \arg LED3 + \arg LED4 + \arg LED5 + \param[out] none + \retval none +*/ +void gd_eval_led_on(led_typedef_enum lednum) +{ + GPIO_BOP(GPIO_PORT[lednum]) = GPIO_PIN[lednum]; +} + +/*! + \brief turn off selected led + \param[in] lednum: specify the led to be turned off + \arg LED2 + \arg LED3 + \arg LED4 + \arg LED5 + \param[out] none + \retval none +*/ +void gd_eval_led_off(led_typedef_enum lednum) +{ + GPIO_BC(GPIO_PORT[lednum]) = GPIO_PIN[lednum]; +} + +/*! + \brief toggle selected led + \param[in] lednum: specify the led to be toggled + \arg LED2 + \arg LED3 + \arg LED4 + \arg LED5 + \param[out] none + \retval none +*/ +void gd_eval_led_toggle(led_typedef_enum lednum) +{ + gpio_bit_write(GPIO_PORT[lednum], GPIO_PIN[lednum], + (bit_status)(1-gpio_input_bit_get(GPIO_PORT[lednum], GPIO_PIN[lednum]))); + +} + +/*! + \brief configure key + \param[in] key_num: specify the key to be configured + \arg KEY_TAMPER: tamper key + \arg KEY_WAKEUP: wakeup key + \arg KEY_USER: user key + \param[in] key_mode: specify button mode + \arg KEY_MODE_GPIO: key will be used as simple IO + \arg KEY_MODE_EXTI: key will be connected to EXTI line with interrupt + \param[out] none + \retval none +*/ +void gd_eval_key_init(key_typedef_enum key_num, keymode_typedef_enum key_mode) +{ + /* enable the key clock */ + rcu_periph_clock_enable(KEY_CLK[key_num]); + rcu_periph_clock_enable(RCU_AF); + + /* configure button pin as input */ + gpio_init(KEY_PORT[key_num], GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, KEY_PIN[key_num]); + + if (key_mode == KEY_MODE_EXTI) { + /* enable and set key EXTI interrupt to the lowest priority */ + nvic_irq_enable(KEY_IRQn[key_num], 2U, 0U); + + /* connect key EXTI line to key GPIO pin */ + gpio_exti_source_select(KEY_PORT_SOURCE[key_num], KEY_PIN_SOURCE[key_num]); + + /* configure key EXTI line */ + exti_init(KEY_EXTI_LINE[key_num], EXTI_INTERRUPT, EXTI_TRIG_FALLING); + exti_interrupt_flag_clear(KEY_EXTI_LINE[key_num]); + } +} + +/*! + \brief return the selected key state + \param[in] key: specify the key to be checked + \arg KEY_TAMPER: tamper key + \arg KEY_WAKEUP: wakeup key + \arg KEY_USER: user key + \param[out] none + \retval the key's GPIO pin value +*/ +uint8_t gd_eval_key_state_get(key_typedef_enum key) +{ + return gpio_input_bit_get(KEY_PORT[key], KEY_PIN[key]); +} + +/*! + \brief configure COM port + \param[in] com: COM on the board + \arg EVAL_COM1: COM1 on the board + \arg EVAL_COM2: COM2 on the board + \param[out] none + \retval none +*/ +void gd_eval_com_init(uint32_t com) +{ + uint32_t com_id = 0U; + if(EVAL_COM1 == com){ + com_id = 0U; + }else{ + com_id = 1U; + } + + /* enable GPIO clock */ + rcu_periph_clock_enable(COM_GPIO_CLK[com_id]); + + /* enable USART clock */ + rcu_periph_clock_enable(COM_CLK[com_id]); + + /* connect port to USARTx_Tx */ + gpio_init(COM_GPIO_PORT[com_id], GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, COM_TX_PIN[com_id]); + + /* connect port to USARTx_Rx */ + gpio_init(COM_GPIO_PORT[com_id], GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, COM_RX_PIN[com_id]); + + /* USART configure */ + usart_deinit(com); + usart_baudrate_set(com, 115200U); + usart_receive_config(com, USART_RECEIVE_ENABLE); + usart_transmit_config(com, USART_TRANSMIT_ENABLE); + usart_enable(com); +} diff --git a/RTE/Device/GD32F107VC/gd32f107c_eval.c.base@2.0.2 b/RTE/Device/GD32F107VC/gd32f107c_eval.c.base@2.0.2 new file mode 100644 index 0000000..be8feaa --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f107c_eval.c.base@2.0.2 @@ -0,0 +1,203 @@ +/*! + \file gd32f107c_eval.c + \brief firmware functions to manage leds, keys, COM ports +*/ + +/* + Copyright (C) 2017 GigaDevice + + 2014-12-26, V1.0.0, demo for GD32F10x + 2017-06-30, V2.0.0, demo for GD32F10x +*/ + + + +#include "gd32f107c_eval.h" + +/* private variables */ +static uint32_t GPIO_PORT[LEDn] = {LED2_GPIO_PORT, LED3_GPIO_PORT, + LED4_GPIO_PORT, LED5_GPIO_PORT}; +static uint32_t GPIO_PIN[LEDn] = {LED2_PIN, LED3_PIN, LED4_PIN, LED5_PIN}; + +static rcu_periph_enum COM_CLK[COMn] = {EVAL_COM1_CLK, EVAL_COM2_CLK}; +static uint32_t COM_TX_PIN[COMn] = {EVAL_COM1_TX_PIN, EVAL_COM2_TX_PIN}; +static uint32_t COM_RX_PIN[COMn] = {EVAL_COM1_RX_PIN, EVAL_COM2_RX_PIN}; +static uint32_t COM_GPIO_PORT[COMn] = {EVAL_COM1_GPIO_PORT, EVAL_COM2_GPIO_PORT}; +static rcu_periph_enum COM_GPIO_CLK[COMn] = {EVAL_COM1_GPIO_CLK, EVAL_COM2_GPIO_CLK}; + +static rcu_periph_enum GPIO_CLK[LEDn] = {LED2_GPIO_CLK, LED3_GPIO_CLK, + LED4_GPIO_CLK, LED5_GPIO_CLK}; + +static uint32_t KEY_PORT[KEYn] = {WAKEUP_KEY_GPIO_PORT, + TAMPER_KEY_GPIO_PORT, + USER_KEY_GPIO_PORT}; +static uint32_t KEY_PIN[KEYn] = {WAKEUP_KEY_PIN, + TAMPER_KEY_PIN, + USER_KEY_PIN}; +static rcu_periph_enum KEY_CLK[KEYn] = {WAKEUP_KEY_GPIO_CLK, + TAMPER_KEY_GPIO_CLK, + USER_KEY_GPIO_CLK}; +static exti_line_enum KEY_EXTI_LINE[KEYn] = {WAKEUP_KEY_EXTI_LINE, + TAMPER_KEY_EXTI_LINE, + USER_KEY_EXTI_LINE}; +static uint8_t KEY_PORT_SOURCE[KEYn] = {WAKEUP_KEY_EXTI_PORT_SOURCE, + TAMPER_KEY_EXTI_PORT_SOURCE, + USER_KEY_EXTI_PORT_SOURCE}; +static uint8_t KEY_PIN_SOURCE[KEYn] = {WAKEUP_KEY_EXTI_PIN_SOURCE, + TAMPER_KEY_EXTI_PIN_SOURCE, + USER_KEY_EXTI_PIN_SOURCE}; +static uint8_t KEY_IRQn[KEYn] = {WAKEUP_KEY_EXTI_IRQn, + TAMPER_KEY_EXTI_IRQn, + USER_KEY_EXTI_IRQn}; + +/*! + \brief configure led GPIO + \param[in] lednum: specify the led to be configured + \arg LED2 + \arg LED3 + \arg LED4 + \arg LED5 + \param[out] none + \retval none +*/ +void gd_eval_led_init (led_typedef_enum lednum) +{ + /* enable the led clock */ + rcu_periph_clock_enable(GPIO_CLK[lednum]); + /* configure led GPIO port */ + gpio_init(GPIO_PORT[lednum], GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN[lednum]); + + GPIO_BC(GPIO_PORT[lednum]) = GPIO_PIN[lednum]; +} + +/*! + \brief turn on selected led + \param[in] lednum: specify the led to be turned on + \arg LED2 + \arg LED3 + \arg LED4 + \arg LED5 + \param[out] none + \retval none +*/ +void gd_eval_led_on(led_typedef_enum lednum) +{ + GPIO_BOP(GPIO_PORT[lednum]) = GPIO_PIN[lednum]; +} + +/*! + \brief turn off selected led + \param[in] lednum: specify the led to be turned off + \arg LED2 + \arg LED3 + \arg LED4 + \arg LED5 + \param[out] none + \retval none +*/ +void gd_eval_led_off(led_typedef_enum lednum) +{ + GPIO_BC(GPIO_PORT[lednum]) = GPIO_PIN[lednum]; +} + +/*! + \brief toggle selected led + \param[in] lednum: specify the led to be toggled + \arg LED2 + \arg LED3 + \arg LED4 + \arg LED5 + \param[out] none + \retval none +*/ +void gd_eval_led_toggle(led_typedef_enum lednum) +{ + gpio_bit_write(GPIO_PORT[lednum], GPIO_PIN[lednum], + (bit_status)(1-gpio_input_bit_get(GPIO_PORT[lednum], GPIO_PIN[lednum]))); + +} + +/*! + \brief configure key + \param[in] key_num: specify the key to be configured + \arg KEY_TAMPER: tamper key + \arg KEY_WAKEUP: wakeup key + \arg KEY_USER: user key + \param[in] key_mode: specify button mode + \arg KEY_MODE_GPIO: key will be used as simple IO + \arg KEY_MODE_EXTI: key will be connected to EXTI line with interrupt + \param[out] none + \retval none +*/ +void gd_eval_key_init(key_typedef_enum key_num, keymode_typedef_enum key_mode) +{ + /* enable the key clock */ + rcu_periph_clock_enable(KEY_CLK[key_num]); + rcu_periph_clock_enable(RCU_AF); + + /* configure button pin as input */ + gpio_init(KEY_PORT[key_num], GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, KEY_PIN[key_num]); + + if (key_mode == KEY_MODE_EXTI) { + /* enable and set key EXTI interrupt to the lowest priority */ + nvic_irq_enable(KEY_IRQn[key_num], 2U, 0U); + + /* connect key EXTI line to key GPIO pin */ + gpio_exti_source_select(KEY_PORT_SOURCE[key_num], KEY_PIN_SOURCE[key_num]); + + /* configure key EXTI line */ + exti_init(KEY_EXTI_LINE[key_num], EXTI_INTERRUPT, EXTI_TRIG_FALLING); + exti_interrupt_flag_clear(KEY_EXTI_LINE[key_num]); + } +} + +/*! + \brief return the selected key state + \param[in] key: specify the key to be checked + \arg KEY_TAMPER: tamper key + \arg KEY_WAKEUP: wakeup key + \arg KEY_USER: user key + \param[out] none + \retval the key's GPIO pin value +*/ +uint8_t gd_eval_key_state_get(key_typedef_enum key) +{ + return gpio_input_bit_get(KEY_PORT[key], KEY_PIN[key]); +} + +/*! + \brief configure COM port + \param[in] com: COM on the board + \arg EVAL_COM1: COM1 on the board + \arg EVAL_COM2: COM2 on the board + \param[out] none + \retval none +*/ +void gd_eval_com_init(uint32_t com) +{ + uint32_t com_id = 0U; + if(EVAL_COM1 == com){ + com_id = 0U; + }else{ + com_id = 1U; + } + + /* enable GPIO clock */ + rcu_periph_clock_enable(COM_GPIO_CLK[com_id]); + + /* enable USART clock */ + rcu_periph_clock_enable(COM_CLK[com_id]); + + /* connect port to USARTx_Tx */ + gpio_init(COM_GPIO_PORT[com_id], GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, COM_TX_PIN[com_id]); + + /* connect port to USARTx_Rx */ + gpio_init(COM_GPIO_PORT[com_id], GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, COM_RX_PIN[com_id]); + + /* USART configure */ + usart_deinit(com); + usart_baudrate_set(com, 115200U); + usart_receive_config(com, USART_RECEIVE_ENABLE); + usart_transmit_config(com, USART_TRANSMIT_ENABLE); + usart_enable(com); +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_dbg.c b/RTE/Device/GD32F107VC/gd32f10x_dbg.c new file mode 100644 index 0000000..2e29a93 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_dbg.c @@ -0,0 +1,152 @@ +/*! + \file gd32f10x_dbg.c + \brief DBG driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_dbg.h" + +/*! + \brief read DBG_ID code register + \param[in] none + \param[out] none + \retval DBG_ID code +*/ +uint32_t dbg_id_get(void) +{ + return DBG_ID; +} + +/*! + \brief enable low power behavior when the mcu is in debug mode + \param[in] dbg_low_power: + one or more parameters can be selected which are shown as below: + \arg DBG_LOW_POWER_SLEEP: keep debugger connection during sleep mode + \arg DBG_LOW_POWER_DEEPSLEEP: keep debugger connection during deepsleep mode + \arg DBG_LOW_POWER_STANDBY: keep debugger connection during standby mode + \param[out] none + \retval none +*/ +void dbg_low_power_enable(uint32_t dbg_low_power) +{ + DBG_CTL |= dbg_low_power; +} + +/*! + \brief disable low power behavior when the mcu is in debug mode + \param[in] dbg_low_power: + one or more parameters can be selected which are shown as below: + \arg DBG_LOW_POWER_SLEEP: donot keep debugger connection during sleep mode + \arg DBG_LOW_POWER_DEEPSLEEP: donot keep debugger connection during deepsleep mode + \arg DBG_LOW_POWER_STANDBY: donot keep debugger connection during standby mode + \param[out] none + \retval none +*/ +void dbg_low_power_disable(uint32_t dbg_low_power) +{ + DBG_CTL &= ~dbg_low_power; +} + +/*! + \brief enable peripheral behavior when the mcu is in debug mode + \param[in] dbg_periph: refer to dbg_periph_enum + one or more parameters can be selected which are shown as below: + \arg DBG_FWDGT_HOLD : debug FWDGT kept when core is halted + \arg DBG_WWDGT_HOLD : debug WWDGT kept when core is halted + \arg DBG_CANx_HOLD (x=0,1,CAN1 is only available for CL series): hold CANx counter when core is halted + \arg DBG_I2Cx_HOLD (x=0,1): hold I2Cx smbus when core is halted + \arg DBG_TIMERx_HOLD (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for HD series): hold TIMERx counter when core is halted + \param[out] none + \retval none +*/ +void dbg_periph_enable(dbg_periph_enum dbg_periph) +{ + DBG_CTL |= (uint32_t)dbg_periph; +} + +/*! + \brief disable peripheral behavior when the mcu is in debug mode + \param[in] dbg_periph: refer to dbg_periph_enum + one or more parameters can be selected which are shown as below: + \arg DBG_FWDGT_HOLD : debug FWDGT kept when core is halted + \arg DBG_WWDGT_HOLD : debug WWDGT kept when core is halted + \arg DBG_CANx_HOLD (x=0,1,CAN1 is only available for CL series): hold CAN0 counter when core is halted + \arg DBG_I2Cx_HOLD (x=0,1): hold I2Cx smbus when core is halted + \arg DBG_TIMERx_HOLD (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD and CL series): hold TIMERx counter when core is halted + \param[out] none + \retval none +*/ +void dbg_periph_disable(dbg_periph_enum dbg_periph) +{ + DBG_CTL &= ~(uint32_t)dbg_periph; +} + +/*! + \brief enable trace pin assignment + \param[in] none + \param[out] none + \retval none +*/ +void dbg_trace_pin_enable(void) +{ + DBG_CTL |= DBG_CTL_TRACE_IOEN; +} + +/*! + \brief disable trace pin assignment + \param[in] none + \param[out] none + \retval none +*/ +void dbg_trace_pin_disable(void) +{ + DBG_CTL &= ~DBG_CTL_TRACE_IOEN; +} + +/*! + \brief trace pin mode selection + \param[in] trace_mode: + only one parameter can be selected which is shown as below: + \arg TRACE_MODE_ASYNC: trace pin used for async mode + \arg TRACE_MODE_SYNC_DATASIZE_1: trace pin used for sync mode and data size is 1 + \arg TRACE_MODE_SYNC_DATASIZE_2: trace pin used for sync mode and data size is 2 + \arg TRACE_MODE_SYNC_DATASIZE_4: trace pin used for sync mode and data size is 4 + \param[out] none + \retval none +*/ +void dbg_trace_pin_mode_set(uint32_t trace_mode) +{ + DBG_CTL &= ~DBG_CTL_TRACE_MODE; + DBG_CTL |= trace_mode; +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_dbg.c.base@2.0.2 b/RTE/Device/GD32F107VC/gd32f10x_dbg.c.base@2.0.2 new file mode 100644 index 0000000..2e29a93 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_dbg.c.base@2.0.2 @@ -0,0 +1,152 @@ +/*! + \file gd32f10x_dbg.c + \brief DBG driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_dbg.h" + +/*! + \brief read DBG_ID code register + \param[in] none + \param[out] none + \retval DBG_ID code +*/ +uint32_t dbg_id_get(void) +{ + return DBG_ID; +} + +/*! + \brief enable low power behavior when the mcu is in debug mode + \param[in] dbg_low_power: + one or more parameters can be selected which are shown as below: + \arg DBG_LOW_POWER_SLEEP: keep debugger connection during sleep mode + \arg DBG_LOW_POWER_DEEPSLEEP: keep debugger connection during deepsleep mode + \arg DBG_LOW_POWER_STANDBY: keep debugger connection during standby mode + \param[out] none + \retval none +*/ +void dbg_low_power_enable(uint32_t dbg_low_power) +{ + DBG_CTL |= dbg_low_power; +} + +/*! + \brief disable low power behavior when the mcu is in debug mode + \param[in] dbg_low_power: + one or more parameters can be selected which are shown as below: + \arg DBG_LOW_POWER_SLEEP: donot keep debugger connection during sleep mode + \arg DBG_LOW_POWER_DEEPSLEEP: donot keep debugger connection during deepsleep mode + \arg DBG_LOW_POWER_STANDBY: donot keep debugger connection during standby mode + \param[out] none + \retval none +*/ +void dbg_low_power_disable(uint32_t dbg_low_power) +{ + DBG_CTL &= ~dbg_low_power; +} + +/*! + \brief enable peripheral behavior when the mcu is in debug mode + \param[in] dbg_periph: refer to dbg_periph_enum + one or more parameters can be selected which are shown as below: + \arg DBG_FWDGT_HOLD : debug FWDGT kept when core is halted + \arg DBG_WWDGT_HOLD : debug WWDGT kept when core is halted + \arg DBG_CANx_HOLD (x=0,1,CAN1 is only available for CL series): hold CANx counter when core is halted + \arg DBG_I2Cx_HOLD (x=0,1): hold I2Cx smbus when core is halted + \arg DBG_TIMERx_HOLD (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for HD series): hold TIMERx counter when core is halted + \param[out] none + \retval none +*/ +void dbg_periph_enable(dbg_periph_enum dbg_periph) +{ + DBG_CTL |= (uint32_t)dbg_periph; +} + +/*! + \brief disable peripheral behavior when the mcu is in debug mode + \param[in] dbg_periph: refer to dbg_periph_enum + one or more parameters can be selected which are shown as below: + \arg DBG_FWDGT_HOLD : debug FWDGT kept when core is halted + \arg DBG_WWDGT_HOLD : debug WWDGT kept when core is halted + \arg DBG_CANx_HOLD (x=0,1,CAN1 is only available for CL series): hold CAN0 counter when core is halted + \arg DBG_I2Cx_HOLD (x=0,1): hold I2Cx smbus when core is halted + \arg DBG_TIMERx_HOLD (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD and CL series): hold TIMERx counter when core is halted + \param[out] none + \retval none +*/ +void dbg_periph_disable(dbg_periph_enum dbg_periph) +{ + DBG_CTL &= ~(uint32_t)dbg_periph; +} + +/*! + \brief enable trace pin assignment + \param[in] none + \param[out] none + \retval none +*/ +void dbg_trace_pin_enable(void) +{ + DBG_CTL |= DBG_CTL_TRACE_IOEN; +} + +/*! + \brief disable trace pin assignment + \param[in] none + \param[out] none + \retval none +*/ +void dbg_trace_pin_disable(void) +{ + DBG_CTL &= ~DBG_CTL_TRACE_IOEN; +} + +/*! + \brief trace pin mode selection + \param[in] trace_mode: + only one parameter can be selected which is shown as below: + \arg TRACE_MODE_ASYNC: trace pin used for async mode + \arg TRACE_MODE_SYNC_DATASIZE_1: trace pin used for sync mode and data size is 1 + \arg TRACE_MODE_SYNC_DATASIZE_2: trace pin used for sync mode and data size is 2 + \arg TRACE_MODE_SYNC_DATASIZE_4: trace pin used for sync mode and data size is 4 + \param[out] none + \retval none +*/ +void dbg_trace_pin_mode_set(uint32_t trace_mode) +{ + DBG_CTL &= ~DBG_CTL_TRACE_MODE; + DBG_CTL |= trace_mode; +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_exti.c b/RTE/Device/GD32F107VC/gd32f10x_exti.c new file mode 100644 index 0000000..35818f2 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_exti.c @@ -0,0 +1,251 @@ +/*! + \file gd32f10x_exti.c + \brief EXTI driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ +#include "gd32f10x_exti.h" + +#define EXTI_REG_RESET_VALUE ((uint32_t)0x00000000U) + +/*! + \brief deinitialize the EXTI + \param[in] none + \param[out] none + \retval none +*/ +void exti_deinit(void) +{ + /* reset the value of all the EXTI registers */ + EXTI_INTEN = EXTI_REG_RESET_VALUE; + EXTI_EVEN = EXTI_REG_RESET_VALUE; + EXTI_RTEN = EXTI_REG_RESET_VALUE; + EXTI_FTEN = EXTI_REG_RESET_VALUE; + EXTI_SWIEV = EXTI_REG_RESET_VALUE; +} + +/*! + \brief initialize the EXTI + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[in] mode: interrupt or event mode, refer to exti_mode_enum + only one parameter can be selected which is shown as below: + \arg EXTI_INTERRUPT: interrupt mode + \arg EXTI_EVENT: event mode + \param[in] trig_type: interrupt trigger type, refer to exti_trig_type_enum + only one parameter can be selected which is shown as below: + \arg EXTI_TRIG_RISING: rising edge trigger + \arg EXTI_TRIG_FALLING: falling trigger + \arg EXTI_TRIG_BOTH: rising and falling trigger + \arg EXTI_TRIG_NONE: without rising edge or falling edge trigger + \param[out] none + \retval none +*/ +void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type) +{ + /* reset the EXTI line x */ + EXTI_INTEN &= ~(uint32_t)linex; + EXTI_EVEN &= ~(uint32_t)linex; + EXTI_RTEN &= ~(uint32_t)linex; + EXTI_FTEN &= ~(uint32_t)linex; + + /* set the EXTI mode and enable the interrupts or events from EXTI line x */ + switch(mode){ + case EXTI_INTERRUPT: + EXTI_INTEN |= (uint32_t)linex; + break; + case EXTI_EVENT: + EXTI_EVEN |= (uint32_t)linex; + break; + default: + break; + } + + /* set the EXTI trigger type */ + switch(trig_type){ + case EXTI_TRIG_RISING: + EXTI_RTEN |= (uint32_t)linex; + EXTI_FTEN &= ~(uint32_t)linex; + break; + case EXTI_TRIG_FALLING: + EXTI_RTEN &= ~(uint32_t)linex; + EXTI_FTEN |= (uint32_t)linex; + break; + case EXTI_TRIG_BOTH: + EXTI_RTEN |= (uint32_t)linex; + EXTI_FTEN |= (uint32_t)linex; + break; + case EXTI_TRIG_NONE: + default: + break; + } +} + +/*! + \brief enable the interrupts from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_enable(exti_line_enum linex) +{ + EXTI_INTEN |= (uint32_t)linex; +} + +/*! + \brief enable the events from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_event_enable(exti_line_enum linex) +{ + EXTI_EVEN |= (uint32_t)linex; +} + +/*! + \brief disable the interrupt from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_disable(exti_line_enum linex) +{ + EXTI_INTEN &= ~(uint32_t)linex; +} + +/*! + \brief disable the events from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_event_disable(exti_line_enum linex) +{ + EXTI_EVEN &= ~(uint32_t)linex; +} + +/*! + \brief get EXTI lines flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval FlagStatus: status of flag (RESET or SET) +*/ +FlagStatus exti_flag_get(exti_line_enum linex) +{ + if(RESET != (EXTI_PD & (uint32_t)linex)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear EXTI lines pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_flag_clear(exti_line_enum linex) +{ + EXTI_PD = (uint32_t)linex; +} + +/*! + \brief get EXTI lines flag when the interrupt flag is set + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval FlagStatus: status of flag (RESET or SET) +*/ +FlagStatus exti_interrupt_flag_get(exti_line_enum linex) +{ + if(RESET != (EXTI_PD & (uint32_t)linex)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear EXTI lines pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_flag_clear(exti_line_enum linex) +{ + EXTI_PD = (uint32_t)linex; +} + +/*! + \brief enable EXTI software interrupt event + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_software_interrupt_enable(exti_line_enum linex) +{ + EXTI_SWIEV |= (uint32_t)linex; +} + +/*! + \brief disable EXTI software interrupt event + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_software_interrupt_disable(exti_line_enum linex) +{ + EXTI_SWIEV &= ~(uint32_t)linex; +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_exti.c.base@2.0.2 b/RTE/Device/GD32F107VC/gd32f10x_exti.c.base@2.0.2 new file mode 100644 index 0000000..35818f2 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_exti.c.base@2.0.2 @@ -0,0 +1,251 @@ +/*! + \file gd32f10x_exti.c + \brief EXTI driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ +#include "gd32f10x_exti.h" + +#define EXTI_REG_RESET_VALUE ((uint32_t)0x00000000U) + +/*! + \brief deinitialize the EXTI + \param[in] none + \param[out] none + \retval none +*/ +void exti_deinit(void) +{ + /* reset the value of all the EXTI registers */ + EXTI_INTEN = EXTI_REG_RESET_VALUE; + EXTI_EVEN = EXTI_REG_RESET_VALUE; + EXTI_RTEN = EXTI_REG_RESET_VALUE; + EXTI_FTEN = EXTI_REG_RESET_VALUE; + EXTI_SWIEV = EXTI_REG_RESET_VALUE; +} + +/*! + \brief initialize the EXTI + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[in] mode: interrupt or event mode, refer to exti_mode_enum + only one parameter can be selected which is shown as below: + \arg EXTI_INTERRUPT: interrupt mode + \arg EXTI_EVENT: event mode + \param[in] trig_type: interrupt trigger type, refer to exti_trig_type_enum + only one parameter can be selected which is shown as below: + \arg EXTI_TRIG_RISING: rising edge trigger + \arg EXTI_TRIG_FALLING: falling trigger + \arg EXTI_TRIG_BOTH: rising and falling trigger + \arg EXTI_TRIG_NONE: without rising edge or falling edge trigger + \param[out] none + \retval none +*/ +void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type) +{ + /* reset the EXTI line x */ + EXTI_INTEN &= ~(uint32_t)linex; + EXTI_EVEN &= ~(uint32_t)linex; + EXTI_RTEN &= ~(uint32_t)linex; + EXTI_FTEN &= ~(uint32_t)linex; + + /* set the EXTI mode and enable the interrupts or events from EXTI line x */ + switch(mode){ + case EXTI_INTERRUPT: + EXTI_INTEN |= (uint32_t)linex; + break; + case EXTI_EVENT: + EXTI_EVEN |= (uint32_t)linex; + break; + default: + break; + } + + /* set the EXTI trigger type */ + switch(trig_type){ + case EXTI_TRIG_RISING: + EXTI_RTEN |= (uint32_t)linex; + EXTI_FTEN &= ~(uint32_t)linex; + break; + case EXTI_TRIG_FALLING: + EXTI_RTEN &= ~(uint32_t)linex; + EXTI_FTEN |= (uint32_t)linex; + break; + case EXTI_TRIG_BOTH: + EXTI_RTEN |= (uint32_t)linex; + EXTI_FTEN |= (uint32_t)linex; + break; + case EXTI_TRIG_NONE: + default: + break; + } +} + +/*! + \brief enable the interrupts from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_enable(exti_line_enum linex) +{ + EXTI_INTEN |= (uint32_t)linex; +} + +/*! + \brief enable the events from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_event_enable(exti_line_enum linex) +{ + EXTI_EVEN |= (uint32_t)linex; +} + +/*! + \brief disable the interrupt from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_disable(exti_line_enum linex) +{ + EXTI_INTEN &= ~(uint32_t)linex; +} + +/*! + \brief disable the events from EXTI line x + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_event_disable(exti_line_enum linex) +{ + EXTI_EVEN &= ~(uint32_t)linex; +} + +/*! + \brief get EXTI lines flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval FlagStatus: status of flag (RESET or SET) +*/ +FlagStatus exti_flag_get(exti_line_enum linex) +{ + if(RESET != (EXTI_PD & (uint32_t)linex)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear EXTI lines pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_flag_clear(exti_line_enum linex) +{ + EXTI_PD = (uint32_t)linex; +} + +/*! + \brief get EXTI lines flag when the interrupt flag is set + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval FlagStatus: status of flag (RESET or SET) +*/ +FlagStatus exti_interrupt_flag_get(exti_line_enum linex) +{ + if(RESET != (EXTI_PD & (uint32_t)linex)){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear EXTI lines pending flag + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_interrupt_flag_clear(exti_line_enum linex) +{ + EXTI_PD = (uint32_t)linex; +} + +/*! + \brief enable EXTI software interrupt event + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_software_interrupt_enable(exti_line_enum linex) +{ + EXTI_SWIEV |= (uint32_t)linex; +} + +/*! + \brief disable EXTI software interrupt event + \param[in] linex: EXTI line number, refer to exti_line_enum + only one parameter can be selected which is shown as below: + \arg EXTI_x (x=0..19): EXTI line x + \param[out] none + \retval none +*/ +void exti_software_interrupt_disable(exti_line_enum linex) +{ + EXTI_SWIEV &= ~(uint32_t)linex; +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_gpio.c b/RTE/Device/GD32F107VC/gd32f10x_gpio.c new file mode 100644 index 0000000..d31a660 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_gpio.c @@ -0,0 +1,538 @@ +/*! + \file gd32f10x_gpio.c + \brief GPIO driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_gpio.h" + +#define AFIO_EXTI_SOURCE_MASK ((uint8_t)0x03U) /*!< AFIO exti source selection mask*/ +#define AFIO_EXTI_SOURCE_FIELDS ((uint8_t)0x04U) /*!< select AFIO exti source registers */ +#define LSB_16BIT_MASK ((uint16_t)0xFFFFU) /*!< LSB 16-bit mask */ +#define PCF_POSITION_MASK ((uint32_t)0x000F0000U) /*!< AFIO_PCF register position mask */ +#define PCF_SWJCFG_MASK ((uint32_t)0xF0FFFFFFU) /*!< AFIO_PCF register SWJCFG mask */ +#define PCF_LOCATION1_MASK ((uint32_t)0x00200000U) /*!< AFIO_PCF register location1 mask */ +#define PCF_LOCATION2_MASK ((uint32_t)0x00100000U) /*!< AFIO_PCF register location2 mask */ +#define AFIO_PCF1_FIELDS ((uint32_t)0x80000000U) /*!< select AFIO_PCF1 register */ +#define GPIO_OUTPUT_PORT_OFFSET ((uint32_t)4U) /*!< GPIO event output port offset*/ + +/*! + \brief reset GPIO port + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval none +*/ +void gpio_deinit(uint32_t gpio_periph) +{ + switch(gpio_periph){ + case GPIOA: + /* reset GPIOA */ + rcu_periph_reset_enable(RCU_GPIOARST); + rcu_periph_reset_disable(RCU_GPIOARST); + break; + case GPIOB: + /* reset GPIOB */ + rcu_periph_reset_enable(RCU_GPIOBRST); + rcu_periph_reset_disable(RCU_GPIOBRST); + break; + case GPIOC: + /* reset GPIOC */ + rcu_periph_reset_enable(RCU_GPIOCRST); + rcu_periph_reset_disable(RCU_GPIOCRST); + break; + case GPIOD: + /* reset GPIOD */ + rcu_periph_reset_enable(RCU_GPIODRST); + rcu_periph_reset_disable(RCU_GPIODRST); + break; + case GPIOE: + /* reset GPIOE */ + rcu_periph_reset_enable(RCU_GPIOERST); + rcu_periph_reset_disable(RCU_GPIOERST); + break; + case GPIOF: + /* reset GPIOF */ + rcu_periph_reset_enable(RCU_GPIOFRST); + rcu_periph_reset_disable(RCU_GPIOFRST); + break; + case GPIOG: + /* reset GPIOG */ + rcu_periph_reset_enable(RCU_GPIOGRST); + rcu_periph_reset_disable(RCU_GPIOGRST); + break; + default: + break; + } +} + +/*! + \brief reset alternate function I/O(AFIO) + \param[in] none + \param[out] none + \retval none +*/ +void gpio_afio_deinit(void) +{ + rcu_periph_reset_enable(RCU_AFRST); + rcu_periph_reset_disable(RCU_AFRST); +} + +/*! + \brief GPIO parameter initialization + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] mode: gpio pin mode + only one parameter can be selected which is shown as below: + \arg GPIO_MODE_AIN: analog input mode + \arg GPIO_MODE_IN_FLOATING: floating input mode + \arg GPIO_MODE_IPD: pull-down input mode + \arg GPIO_MODE_IPU: pull-up input mode + \arg GPIO_MODE_OUT_OD: GPIO output with open-drain + \arg GPIO_MODE_OUT_PP: GPIO output with push-pull + \arg GPIO_MODE_AF_OD: AFIO output with open-drain + \arg GPIO_MODE_AF_PP: AFIO output with push-pull + \param[in] speed: gpio output max speed value + only one parameter can be selected which is shown as below: + \arg GPIO_OSPEED_10MHZ: output max speed 10MHz + \arg GPIO_OSPEED_2MHZ: output max speed 2MHz + \arg GPIO_OSPEED_50MHZ: output max speed 50MHz + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + + \param[out] none + \retval none +*/ +void gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin) +{ + uint16_t i; + uint32_t temp_mode = 0U; + uint32_t reg = 0U; + + /* GPIO mode configuration */ + temp_mode = (uint32_t)(mode & ((uint32_t)0x0FU)); + + /* GPIO speed configuration */ + if(((uint32_t)0x00U) != ((uint32_t)mode & ((uint32_t)0x10U))){ + /* output mode max speed:10MHz,2MHz,50MHz */ + temp_mode |= (uint32_t)speed; + } + + /* configure the eight low port pins with GPIO_CTL0 */ + for(i = 0U;i < 8U;i++){ + if((1U << i) & pin){ + reg = GPIO_CTL0(gpio_periph); + + /* clear the specified pin mode bits */ + reg &= ~GPIO_MODE_MASK(i); + /* set the specified pin mode bits */ + reg |= GPIO_MODE_SET(i, temp_mode); + + /* set IPD or IPU */ + if(GPIO_MODE_IPD == mode){ + /* reset the corresponding OCTL bit */ + GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin); + }else{ + /* set the corresponding OCTL bit */ + if(GPIO_MODE_IPU == mode){ + GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin); + } + } + /* set GPIO_CTL0 register */ + GPIO_CTL0(gpio_periph) = reg; + } + } + /* configure the eight high port pins with GPIO_CTL1 */ + for(i = 8U;i < 16U;i++){ + if((1U << i) & pin){ + reg = GPIO_CTL1(gpio_periph); + + /* clear the specified pin mode bits */ + reg &= ~GPIO_MODE_MASK(i - 8U); + /* set the specified pin mode bits */ + reg |= GPIO_MODE_SET(i - 8U, temp_mode); + + /* set IPD or IPU */ + if(GPIO_MODE_IPD == mode){ + /* reset the corresponding OCTL bit */ + GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin); + }else{ + /* set the corresponding OCTL bit */ + if(GPIO_MODE_IPU == mode){ + GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin); + } + } + /* set GPIO_CTL1 register */ + GPIO_CTL1(gpio_periph) = reg; + } + } +} + +/*! + \brief set GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_bit_set(uint32_t gpio_periph, uint32_t pin) +{ + GPIO_BOP(gpio_periph) = (uint32_t)pin; +} + +/*! + \brief reset GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_bit_reset(uint32_t gpio_periph, uint32_t pin) +{ + GPIO_BC(gpio_periph) = (uint32_t)pin; +} + +/*! + \brief write data to the specified GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[in] bit_value: SET or RESET + \arg RESET: clear the port pin + \arg SET: set the port pin + \param[out] none + \retval none +*/ +void gpio_bit_write(uint32_t gpio_periph, uint32_t pin, bit_status bit_value) +{ + if(RESET != bit_value){ + GPIO_BOP(gpio_periph) = (uint32_t)pin; + }else{ + GPIO_BC(gpio_periph) = (uint32_t)pin; + } +} + +/*! + \brief write data to the specified GPIO port + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] data: specify the value to be written to the port output data register + \param[out] none + \retval none +*/ +void gpio_port_write(uint32_t gpio_periph,uint16_t data) +{ + GPIO_OCTL(gpio_periph) = (uint32_t)data; +} + +/*! + \brief get GPIO pin input status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval input status of gpio pin: SET or RESET +*/ +FlagStatus gpio_input_bit_get(uint32_t gpio_periph,uint32_t pin) +{ + if((uint32_t)RESET != (GPIO_ISTAT(gpio_periph)&(pin))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get GPIO port input status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval input status of gpio all pins +*/ +uint16_t gpio_input_port_get(uint32_t gpio_periph) +{ + return (uint16_t)(GPIO_ISTAT(gpio_periph)); +} + +/*! + \brief get GPIO pin output status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval output status of gpio pin: SET or RESET +*/ +FlagStatus gpio_output_bit_get(uint32_t gpio_periph, uint32_t pin) +{ + if((uint32_t)RESET !=(GPIO_OCTL(gpio_periph)&(pin))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get GPIO port output status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval output status of gpio all pins +*/ +uint16_t gpio_output_port_get(uint32_t gpio_periph) +{ + return ((uint16_t)GPIO_OCTL(gpio_periph)); +} + +/*! + \brief configure GPIO pin remap + \param[in] gpio_remap: select the pin to remap + \arg GPIO_SPI0_REMAP: SPI0 remapping + \arg GPIO_I2C0_REMAP: I2C0 remapping + \arg GPIO_USART0_REMAP: USART0 remapping + \arg GPIO_USART1_REMAP: USART1 remapping + \arg GPIO_USART2_PARTIAL_REMAP: USART2 partial remapping + \arg GPIO_USART2_FULL_REMAP: USART2 full remapping + \arg GPIO_TIMER0_PARTIAL_REMAP: TIMER0 partial remapping + \arg GPIO_TIMER0_FULL_REMAP: TIMER0 full remapping + \arg GPIO_TIMER1_PARTIAL_REMAP1: TIMER1 partial remapping + \arg GPIO_TIMER1_PARTIAL_REMAP2: TIMER1 partial remapping + \arg GPIO_TIMER1_FULL_REMAP: TIMER1 full remapping + \arg GPIO_TIMER2_PARTIAL_REMAP: TIMER2 partial remapping + \arg GPIO_TIMER2_FULL_REMAP: TIMER2 full remapping + \arg GPIO_TIMER3_REMAP: TIMER3 remapping + \arg GPIO_CAN_PARTIAL_REMAP: CAN partial remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_CAN_FULL_REMAP: CAN full remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_CAN0_PARTIAL_REMAP: CAN0 partial remapping(only for GD32F10X_CL devices) + \arg GPIO_CAN0_FULL_REMAP: CAN0 full remapping(only for GD32F10X_CL devices) + \arg GPIO_PD01_REMAP: PD01 remapping + \arg GPIO_TIMER4CH3_IREMAP: TIMER4 channel3 internal remapping(only for GD32F10X_CL devices and GD32F10X_HD devices) + \arg GPIO_ADC0_ETRGINS_REMAP: ADC0 external trigger inserted conversion remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_ADC0_ETRGREG_REMAP: ADC0 external trigger regular conversion remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_ADC1_ETRGINS_REMAP: ADC1 external trigger inserted conversion remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_ADC1_ETRGREG_REMAP: ADC1 external trigger regular conversion remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_ENET_REMAP: ENET remapping(only for GD32F10X_CL devices) + \arg GPIO_CAN1_REMAP: CAN1 remapping(only for GD32F10X_CL devices) + \arg GPIO_SWJ_NONJTRST_REMAP: full SWJ(JTAG-DP + SW-DP),but without NJTRST + \arg GPIO_SWJ_SWDPENABLE_REMAP: JTAG-DP disabled and SW-DP enabled + \arg GPIO_SWJ_DISABLE_REMAP: JTAG-DP disabled and SW-DP disabled + \arg GPIO_SPI2_REMAP: SPI2 remapping(only for GD32F10X_CL, GD32F10X_HD and GD32F10X_XD devices) + \arg GPIO_TIMER1ITI1_REMAP: TIMER1 internal trigger 1 remapping(only for GD32F10X_CL devices) + \arg GPIO_PTP_PPS_REMAP: ethernet PTP PPS remapping(only for GD32F10X_CL devices) + \arg GPIO_TIMER8_REMAP: TIMER8 remapping + \arg GPIO_TIMER9_REMAP: TIMER9 remapping + \arg GPIO_TIMER10_REMAP: TIMER10 remapping + \arg GPIO_TIMER12_REMAP: TIMER12 remapping + \arg GPIO_TIMER13_REMAP: TIMER13 remapping + \arg GPIO_EXMC_NADV_REMAP: EXMC_NADV connect/disconnect + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void gpio_pin_remap_config(uint32_t remap, ControlStatus newvalue) +{ + uint32_t remap1 = 0U, remap2 = 0U, temp_reg = 0U, temp_mask = 0U; + + if(AFIO_PCF1_FIELDS == (remap & AFIO_PCF1_FIELDS)){ + /* get AFIO_PCF1 regiter value */ + temp_reg = AFIO_PCF1; + }else{ + /* get AFIO_PCF0 regiter value */ + temp_reg = AFIO_PCF0; + } + + temp_mask = (remap & PCF_POSITION_MASK) >> 0x10U; + remap1 = remap & LSB_16BIT_MASK; + + /* judge pin remap type */ + if((PCF_LOCATION1_MASK | PCF_LOCATION2_MASK) == (remap & (PCF_LOCATION1_MASK | PCF_LOCATION2_MASK))){ + temp_reg &= PCF_SWJCFG_MASK; + AFIO_PCF0 &= PCF_SWJCFG_MASK; + }else if(PCF_LOCATION2_MASK == (remap & PCF_LOCATION2_MASK)){ + remap2 = ((uint32_t)0x03U) << temp_mask; + temp_reg &= ~remap2; + temp_reg |= ~PCF_SWJCFG_MASK; + }else{ + temp_reg &= ~(remap1 << ((remap >> 0x15U)*0x10U)); + temp_reg |= ~PCF_SWJCFG_MASK; + } + + /* set pin remap value */ + if(DISABLE != newvalue){ + temp_reg |= (remap1 << ((remap >> 0x15U)*0x10U)); + } + + if(AFIO_PCF1_FIELDS == (remap & AFIO_PCF1_FIELDS)){ + /* set AFIO_PCF1 regiter value */ + AFIO_PCF1 = temp_reg; + }else{ + /* set AFIO_PCF0 regiter value */ + AFIO_PCF0 = temp_reg; + } +} + +/*! + \brief select GPIO pin exti sources + \param[in] gpio_outputport: gpio event output port + \arg GPIO_PORT_SOURCE_GPIOA: output port source A + \arg GPIO_PORT_SOURCE_GPIOB: output port source B + \arg GPIO_PORT_SOURCE_GPIOC: output port source C + \arg GPIO_PORT_SOURCE_GPIOD: output port source D + \arg GPIO_PORT_SOURCE_GPIOE: output port source E + \arg GPIO_PORT_SOURCE_GPIOF: output port source F + \arg GPIO_PORT_SOURCE_GPIOG: output port source G + \param[in] gpio_outputpin: GPIO_PIN_SOURCE_x(x=0..15) + \param[out] none + \retval none +*/ +void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin) +{ + uint32_t source = 0U; + source = ((uint32_t)0x0FU) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK)); + + /* select EXTI sources */ + if(GPIO_PIN_SOURCE_4 > output_pin){ + /* select EXTI0/EXTI1/EXTI2/EXTI3 */ + AFIO_EXTISS0 &= ~source; + AFIO_EXTISS0 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else if(GPIO_PIN_SOURCE_8 > output_pin){ + /* select EXTI4/EXTI5/EXTI6/EXTI7 */ + AFIO_EXTISS1 &= ~source; + AFIO_EXTISS1 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else if(GPIO_PIN_SOURCE_12 > output_pin){ + /* select EXTI8/EXTI9/EXTI10/EXTI11 */ + AFIO_EXTISS2 &= ~source; + AFIO_EXTISS2 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else{ + /* select EXTI12/EXTI13/EXTI14/EXTI15 */ + AFIO_EXTISS3 &= ~source; + AFIO_EXTISS3 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + } +} + +/*! + \brief configure GPIO pin event output + \param[in] output_port: gpio event output port + only one parameter can be selected which are shown as below: + \arg GPIO_EVENT_PORT_GPIOA: event output port A + \arg GPIO_EVENT_PORT_GPIOB: event output port B + \arg GPIO_EVENT_PORT_GPIOC: event output port C + \arg GPIO_EVENT_PORT_GPIOD: event output port D + \arg GPIO_EVENT_PORT_GPIOE: event output port E + \arg GPIO_EVENT_PORT_GPIOE: event output port F + \arg GPIO_EVENT_PORT_GPIOE: event output port G + \param[in] output_pin: + only one parameter can be selected which are shown as below: + \arg GPIO_EVENT_PIN_x(x=0..15) + \param[out] none + \retval none +*/ +void gpio_event_output_config(uint8_t output_port, uint8_t output_pin) +{ + uint32_t reg = 0U; + reg = AFIO_EC; + + /* clear AFIO_EC_PORT and AFIO_EC_PIN bits */ + reg &= (uint32_t)(~(AFIO_EC_PORT|AFIO_EC_PIN)); + + reg |= (uint32_t)((uint32_t)output_port << GPIO_OUTPUT_PORT_OFFSET); + reg |= (uint32_t)output_pin; + + AFIO_EC = reg; +} + +/*! + \brief enable GPIO pin event output + \param[in] none + \param[out] none + \retval none +*/ +void gpio_event_output_enable(void) +{ + AFIO_EC |= AFIO_EC_EOE; +} + +/*! + \brief disable GPIO pin event output + \param[in] none + \param[out] none + \retval none +*/ +void gpio_event_output_disable(void) +{ + AFIO_EC &= (uint32_t)(~AFIO_EC_EOE); +} + +/*! + \brief lock GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_pin_lock(uint32_t gpio_periph, uint32_t pin) +{ + uint32_t lock = 0x00010000U; + lock |= pin; + + /* lock key writing sequence: write 1 -> write 0 -> write 1 -> read 0 -> read 1 */ + GPIO_LOCK(gpio_periph) = (uint32_t)lock; + GPIO_LOCK(gpio_periph) = (uint32_t)pin; + GPIO_LOCK(gpio_periph) = (uint32_t)lock; + lock = GPIO_LOCK(gpio_periph); + lock = GPIO_LOCK(gpio_periph); +} + +#ifdef GD32F10X_CL +/*! + \brief select ethernet MII or RMII PHY + \param[in] gpio_enetsel: ethernet MII or RMII PHY selection + \arg GPIO_ENET_PHY_MII: configure ethernet MAC for connection with an MII PHY + \arg GPIO_ENET_PHY_RMII: configure ethernet MAC for connection with an RMII PHY + \param[out] none + \retval none +*/ +void gpio_ethernet_phy_select(uint32_t gpio_enetsel) +{ + /* clear AFIO_PCF0_ENET_PHY_SEL bit */ + AFIO_PCF0 &= (uint32_t)(~AFIO_PCF0_ENET_PHY_SEL); + + /* select MII or RMII PHY */ + AFIO_PCF0 |= (uint32_t)gpio_enetsel; +} +#endif diff --git a/RTE/Device/GD32F107VC/gd32f10x_gpio.c.base@2.0.2 b/RTE/Device/GD32F107VC/gd32f10x_gpio.c.base@2.0.2 new file mode 100644 index 0000000..d31a660 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_gpio.c.base@2.0.2 @@ -0,0 +1,538 @@ +/*! + \file gd32f10x_gpio.c + \brief GPIO driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_gpio.h" + +#define AFIO_EXTI_SOURCE_MASK ((uint8_t)0x03U) /*!< AFIO exti source selection mask*/ +#define AFIO_EXTI_SOURCE_FIELDS ((uint8_t)0x04U) /*!< select AFIO exti source registers */ +#define LSB_16BIT_MASK ((uint16_t)0xFFFFU) /*!< LSB 16-bit mask */ +#define PCF_POSITION_MASK ((uint32_t)0x000F0000U) /*!< AFIO_PCF register position mask */ +#define PCF_SWJCFG_MASK ((uint32_t)0xF0FFFFFFU) /*!< AFIO_PCF register SWJCFG mask */ +#define PCF_LOCATION1_MASK ((uint32_t)0x00200000U) /*!< AFIO_PCF register location1 mask */ +#define PCF_LOCATION2_MASK ((uint32_t)0x00100000U) /*!< AFIO_PCF register location2 mask */ +#define AFIO_PCF1_FIELDS ((uint32_t)0x80000000U) /*!< select AFIO_PCF1 register */ +#define GPIO_OUTPUT_PORT_OFFSET ((uint32_t)4U) /*!< GPIO event output port offset*/ + +/*! + \brief reset GPIO port + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval none +*/ +void gpio_deinit(uint32_t gpio_periph) +{ + switch(gpio_periph){ + case GPIOA: + /* reset GPIOA */ + rcu_periph_reset_enable(RCU_GPIOARST); + rcu_periph_reset_disable(RCU_GPIOARST); + break; + case GPIOB: + /* reset GPIOB */ + rcu_periph_reset_enable(RCU_GPIOBRST); + rcu_periph_reset_disable(RCU_GPIOBRST); + break; + case GPIOC: + /* reset GPIOC */ + rcu_periph_reset_enable(RCU_GPIOCRST); + rcu_periph_reset_disable(RCU_GPIOCRST); + break; + case GPIOD: + /* reset GPIOD */ + rcu_periph_reset_enable(RCU_GPIODRST); + rcu_periph_reset_disable(RCU_GPIODRST); + break; + case GPIOE: + /* reset GPIOE */ + rcu_periph_reset_enable(RCU_GPIOERST); + rcu_periph_reset_disable(RCU_GPIOERST); + break; + case GPIOF: + /* reset GPIOF */ + rcu_periph_reset_enable(RCU_GPIOFRST); + rcu_periph_reset_disable(RCU_GPIOFRST); + break; + case GPIOG: + /* reset GPIOG */ + rcu_periph_reset_enable(RCU_GPIOGRST); + rcu_periph_reset_disable(RCU_GPIOGRST); + break; + default: + break; + } +} + +/*! + \brief reset alternate function I/O(AFIO) + \param[in] none + \param[out] none + \retval none +*/ +void gpio_afio_deinit(void) +{ + rcu_periph_reset_enable(RCU_AFRST); + rcu_periph_reset_disable(RCU_AFRST); +} + +/*! + \brief GPIO parameter initialization + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] mode: gpio pin mode + only one parameter can be selected which is shown as below: + \arg GPIO_MODE_AIN: analog input mode + \arg GPIO_MODE_IN_FLOATING: floating input mode + \arg GPIO_MODE_IPD: pull-down input mode + \arg GPIO_MODE_IPU: pull-up input mode + \arg GPIO_MODE_OUT_OD: GPIO output with open-drain + \arg GPIO_MODE_OUT_PP: GPIO output with push-pull + \arg GPIO_MODE_AF_OD: AFIO output with open-drain + \arg GPIO_MODE_AF_PP: AFIO output with push-pull + \param[in] speed: gpio output max speed value + only one parameter can be selected which is shown as below: + \arg GPIO_OSPEED_10MHZ: output max speed 10MHz + \arg GPIO_OSPEED_2MHZ: output max speed 2MHz + \arg GPIO_OSPEED_50MHZ: output max speed 50MHz + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + + \param[out] none + \retval none +*/ +void gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin) +{ + uint16_t i; + uint32_t temp_mode = 0U; + uint32_t reg = 0U; + + /* GPIO mode configuration */ + temp_mode = (uint32_t)(mode & ((uint32_t)0x0FU)); + + /* GPIO speed configuration */ + if(((uint32_t)0x00U) != ((uint32_t)mode & ((uint32_t)0x10U))){ + /* output mode max speed:10MHz,2MHz,50MHz */ + temp_mode |= (uint32_t)speed; + } + + /* configure the eight low port pins with GPIO_CTL0 */ + for(i = 0U;i < 8U;i++){ + if((1U << i) & pin){ + reg = GPIO_CTL0(gpio_periph); + + /* clear the specified pin mode bits */ + reg &= ~GPIO_MODE_MASK(i); + /* set the specified pin mode bits */ + reg |= GPIO_MODE_SET(i, temp_mode); + + /* set IPD or IPU */ + if(GPIO_MODE_IPD == mode){ + /* reset the corresponding OCTL bit */ + GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin); + }else{ + /* set the corresponding OCTL bit */ + if(GPIO_MODE_IPU == mode){ + GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin); + } + } + /* set GPIO_CTL0 register */ + GPIO_CTL0(gpio_periph) = reg; + } + } + /* configure the eight high port pins with GPIO_CTL1 */ + for(i = 8U;i < 16U;i++){ + if((1U << i) & pin){ + reg = GPIO_CTL1(gpio_periph); + + /* clear the specified pin mode bits */ + reg &= ~GPIO_MODE_MASK(i - 8U); + /* set the specified pin mode bits */ + reg |= GPIO_MODE_SET(i - 8U, temp_mode); + + /* set IPD or IPU */ + if(GPIO_MODE_IPD == mode){ + /* reset the corresponding OCTL bit */ + GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin); + }else{ + /* set the corresponding OCTL bit */ + if(GPIO_MODE_IPU == mode){ + GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin); + } + } + /* set GPIO_CTL1 register */ + GPIO_CTL1(gpio_periph) = reg; + } + } +} + +/*! + \brief set GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_bit_set(uint32_t gpio_periph, uint32_t pin) +{ + GPIO_BOP(gpio_periph) = (uint32_t)pin; +} + +/*! + \brief reset GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_bit_reset(uint32_t gpio_periph, uint32_t pin) +{ + GPIO_BC(gpio_periph) = (uint32_t)pin; +} + +/*! + \brief write data to the specified GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[in] bit_value: SET or RESET + \arg RESET: clear the port pin + \arg SET: set the port pin + \param[out] none + \retval none +*/ +void gpio_bit_write(uint32_t gpio_periph, uint32_t pin, bit_status bit_value) +{ + if(RESET != bit_value){ + GPIO_BOP(gpio_periph) = (uint32_t)pin; + }else{ + GPIO_BC(gpio_periph) = (uint32_t)pin; + } +} + +/*! + \brief write data to the specified GPIO port + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] data: specify the value to be written to the port output data register + \param[out] none + \retval none +*/ +void gpio_port_write(uint32_t gpio_periph,uint16_t data) +{ + GPIO_OCTL(gpio_periph) = (uint32_t)data; +} + +/*! + \brief get GPIO pin input status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval input status of gpio pin: SET or RESET +*/ +FlagStatus gpio_input_bit_get(uint32_t gpio_periph,uint32_t pin) +{ + if((uint32_t)RESET != (GPIO_ISTAT(gpio_periph)&(pin))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get GPIO port input status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval input status of gpio all pins +*/ +uint16_t gpio_input_port_get(uint32_t gpio_periph) +{ + return (uint16_t)(GPIO_ISTAT(gpio_periph)); +} + +/*! + \brief get GPIO pin output status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval output status of gpio pin: SET or RESET +*/ +FlagStatus gpio_output_bit_get(uint32_t gpio_periph, uint32_t pin) +{ + if((uint32_t)RESET !=(GPIO_OCTL(gpio_periph)&(pin))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief get GPIO port output status + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[out] none + \retval output status of gpio all pins +*/ +uint16_t gpio_output_port_get(uint32_t gpio_periph) +{ + return ((uint16_t)GPIO_OCTL(gpio_periph)); +} + +/*! + \brief configure GPIO pin remap + \param[in] gpio_remap: select the pin to remap + \arg GPIO_SPI0_REMAP: SPI0 remapping + \arg GPIO_I2C0_REMAP: I2C0 remapping + \arg GPIO_USART0_REMAP: USART0 remapping + \arg GPIO_USART1_REMAP: USART1 remapping + \arg GPIO_USART2_PARTIAL_REMAP: USART2 partial remapping + \arg GPIO_USART2_FULL_REMAP: USART2 full remapping + \arg GPIO_TIMER0_PARTIAL_REMAP: TIMER0 partial remapping + \arg GPIO_TIMER0_FULL_REMAP: TIMER0 full remapping + \arg GPIO_TIMER1_PARTIAL_REMAP1: TIMER1 partial remapping + \arg GPIO_TIMER1_PARTIAL_REMAP2: TIMER1 partial remapping + \arg GPIO_TIMER1_FULL_REMAP: TIMER1 full remapping + \arg GPIO_TIMER2_PARTIAL_REMAP: TIMER2 partial remapping + \arg GPIO_TIMER2_FULL_REMAP: TIMER2 full remapping + \arg GPIO_TIMER3_REMAP: TIMER3 remapping + \arg GPIO_CAN_PARTIAL_REMAP: CAN partial remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_CAN_FULL_REMAP: CAN full remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_CAN0_PARTIAL_REMAP: CAN0 partial remapping(only for GD32F10X_CL devices) + \arg GPIO_CAN0_FULL_REMAP: CAN0 full remapping(only for GD32F10X_CL devices) + \arg GPIO_PD01_REMAP: PD01 remapping + \arg GPIO_TIMER4CH3_IREMAP: TIMER4 channel3 internal remapping(only for GD32F10X_CL devices and GD32F10X_HD devices) + \arg GPIO_ADC0_ETRGINS_REMAP: ADC0 external trigger inserted conversion remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_ADC0_ETRGREG_REMAP: ADC0 external trigger regular conversion remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_ADC1_ETRGINS_REMAP: ADC1 external trigger inserted conversion remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_ADC1_ETRGREG_REMAP: ADC1 external trigger regular conversion remapping(only for GD32F10X_MD, GD32F10X_HD devices and GD32F10X_XD devices) + \arg GPIO_ENET_REMAP: ENET remapping(only for GD32F10X_CL devices) + \arg GPIO_CAN1_REMAP: CAN1 remapping(only for GD32F10X_CL devices) + \arg GPIO_SWJ_NONJTRST_REMAP: full SWJ(JTAG-DP + SW-DP),but without NJTRST + \arg GPIO_SWJ_SWDPENABLE_REMAP: JTAG-DP disabled and SW-DP enabled + \arg GPIO_SWJ_DISABLE_REMAP: JTAG-DP disabled and SW-DP disabled + \arg GPIO_SPI2_REMAP: SPI2 remapping(only for GD32F10X_CL, GD32F10X_HD and GD32F10X_XD devices) + \arg GPIO_TIMER1ITI1_REMAP: TIMER1 internal trigger 1 remapping(only for GD32F10X_CL devices) + \arg GPIO_PTP_PPS_REMAP: ethernet PTP PPS remapping(only for GD32F10X_CL devices) + \arg GPIO_TIMER8_REMAP: TIMER8 remapping + \arg GPIO_TIMER9_REMAP: TIMER9 remapping + \arg GPIO_TIMER10_REMAP: TIMER10 remapping + \arg GPIO_TIMER12_REMAP: TIMER12 remapping + \arg GPIO_TIMER13_REMAP: TIMER13 remapping + \arg GPIO_EXMC_NADV_REMAP: EXMC_NADV connect/disconnect + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void gpio_pin_remap_config(uint32_t remap, ControlStatus newvalue) +{ + uint32_t remap1 = 0U, remap2 = 0U, temp_reg = 0U, temp_mask = 0U; + + if(AFIO_PCF1_FIELDS == (remap & AFIO_PCF1_FIELDS)){ + /* get AFIO_PCF1 regiter value */ + temp_reg = AFIO_PCF1; + }else{ + /* get AFIO_PCF0 regiter value */ + temp_reg = AFIO_PCF0; + } + + temp_mask = (remap & PCF_POSITION_MASK) >> 0x10U; + remap1 = remap & LSB_16BIT_MASK; + + /* judge pin remap type */ + if((PCF_LOCATION1_MASK | PCF_LOCATION2_MASK) == (remap & (PCF_LOCATION1_MASK | PCF_LOCATION2_MASK))){ + temp_reg &= PCF_SWJCFG_MASK; + AFIO_PCF0 &= PCF_SWJCFG_MASK; + }else if(PCF_LOCATION2_MASK == (remap & PCF_LOCATION2_MASK)){ + remap2 = ((uint32_t)0x03U) << temp_mask; + temp_reg &= ~remap2; + temp_reg |= ~PCF_SWJCFG_MASK; + }else{ + temp_reg &= ~(remap1 << ((remap >> 0x15U)*0x10U)); + temp_reg |= ~PCF_SWJCFG_MASK; + } + + /* set pin remap value */ + if(DISABLE != newvalue){ + temp_reg |= (remap1 << ((remap >> 0x15U)*0x10U)); + } + + if(AFIO_PCF1_FIELDS == (remap & AFIO_PCF1_FIELDS)){ + /* set AFIO_PCF1 regiter value */ + AFIO_PCF1 = temp_reg; + }else{ + /* set AFIO_PCF0 regiter value */ + AFIO_PCF0 = temp_reg; + } +} + +/*! + \brief select GPIO pin exti sources + \param[in] gpio_outputport: gpio event output port + \arg GPIO_PORT_SOURCE_GPIOA: output port source A + \arg GPIO_PORT_SOURCE_GPIOB: output port source B + \arg GPIO_PORT_SOURCE_GPIOC: output port source C + \arg GPIO_PORT_SOURCE_GPIOD: output port source D + \arg GPIO_PORT_SOURCE_GPIOE: output port source E + \arg GPIO_PORT_SOURCE_GPIOF: output port source F + \arg GPIO_PORT_SOURCE_GPIOG: output port source G + \param[in] gpio_outputpin: GPIO_PIN_SOURCE_x(x=0..15) + \param[out] none + \retval none +*/ +void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin) +{ + uint32_t source = 0U; + source = ((uint32_t)0x0FU) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK)); + + /* select EXTI sources */ + if(GPIO_PIN_SOURCE_4 > output_pin){ + /* select EXTI0/EXTI1/EXTI2/EXTI3 */ + AFIO_EXTISS0 &= ~source; + AFIO_EXTISS0 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else if(GPIO_PIN_SOURCE_8 > output_pin){ + /* select EXTI4/EXTI5/EXTI6/EXTI7 */ + AFIO_EXTISS1 &= ~source; + AFIO_EXTISS1 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else if(GPIO_PIN_SOURCE_12 > output_pin){ + /* select EXTI8/EXTI9/EXTI10/EXTI11 */ + AFIO_EXTISS2 &= ~source; + AFIO_EXTISS2 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + }else{ + /* select EXTI12/EXTI13/EXTI14/EXTI15 */ + AFIO_EXTISS3 &= ~source; + AFIO_EXTISS3 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK))); + } +} + +/*! + \brief configure GPIO pin event output + \param[in] output_port: gpio event output port + only one parameter can be selected which are shown as below: + \arg GPIO_EVENT_PORT_GPIOA: event output port A + \arg GPIO_EVENT_PORT_GPIOB: event output port B + \arg GPIO_EVENT_PORT_GPIOC: event output port C + \arg GPIO_EVENT_PORT_GPIOD: event output port D + \arg GPIO_EVENT_PORT_GPIOE: event output port E + \arg GPIO_EVENT_PORT_GPIOE: event output port F + \arg GPIO_EVENT_PORT_GPIOE: event output port G + \param[in] output_pin: + only one parameter can be selected which are shown as below: + \arg GPIO_EVENT_PIN_x(x=0..15) + \param[out] none + \retval none +*/ +void gpio_event_output_config(uint8_t output_port, uint8_t output_pin) +{ + uint32_t reg = 0U; + reg = AFIO_EC; + + /* clear AFIO_EC_PORT and AFIO_EC_PIN bits */ + reg &= (uint32_t)(~(AFIO_EC_PORT|AFIO_EC_PIN)); + + reg |= (uint32_t)((uint32_t)output_port << GPIO_OUTPUT_PORT_OFFSET); + reg |= (uint32_t)output_pin; + + AFIO_EC = reg; +} + +/*! + \brief enable GPIO pin event output + \param[in] none + \param[out] none + \retval none +*/ +void gpio_event_output_enable(void) +{ + AFIO_EC |= AFIO_EC_EOE; +} + +/*! + \brief disable GPIO pin event output + \param[in] none + \param[out] none + \retval none +*/ +void gpio_event_output_disable(void) +{ + AFIO_EC &= (uint32_t)(~AFIO_EC_EOE); +} + +/*! + \brief lock GPIO pin + \param[in] gpio_periph: GPIOx(x = A,B,C,D,E,F,G) + \param[in] pin: GPIO pin + one or more parameters can be selected which are shown as below: + \arg GPIO_PIN_x(x=0..15), GPIO_PIN_ALL + \param[out] none + \retval none +*/ +void gpio_pin_lock(uint32_t gpio_periph, uint32_t pin) +{ + uint32_t lock = 0x00010000U; + lock |= pin; + + /* lock key writing sequence: write 1 -> write 0 -> write 1 -> read 0 -> read 1 */ + GPIO_LOCK(gpio_periph) = (uint32_t)lock; + GPIO_LOCK(gpio_periph) = (uint32_t)pin; + GPIO_LOCK(gpio_periph) = (uint32_t)lock; + lock = GPIO_LOCK(gpio_periph); + lock = GPIO_LOCK(gpio_periph); +} + +#ifdef GD32F10X_CL +/*! + \brief select ethernet MII or RMII PHY + \param[in] gpio_enetsel: ethernet MII or RMII PHY selection + \arg GPIO_ENET_PHY_MII: configure ethernet MAC for connection with an MII PHY + \arg GPIO_ENET_PHY_RMII: configure ethernet MAC for connection with an RMII PHY + \param[out] none + \retval none +*/ +void gpio_ethernet_phy_select(uint32_t gpio_enetsel) +{ + /* clear AFIO_PCF0_ENET_PHY_SEL bit */ + AFIO_PCF0 &= (uint32_t)(~AFIO_PCF0_ENET_PHY_SEL); + + /* select MII or RMII PHY */ + AFIO_PCF0 |= (uint32_t)gpio_enetsel; +} +#endif diff --git a/RTE/Device/GD32F107VC/gd32f10x_misc.c b/RTE/Device/GD32F107VC/gd32f10x_misc.c new file mode 100644 index 0000000..6978689 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_misc.c @@ -0,0 +1,186 @@ +/*! + \file gd32f10x_misc.c + \brief MISC driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_misc.h" + +/*! + \brief set the priority group + \param[in] nvic_prigroup: the NVIC priority group + \arg NVIC_PRIGROUP_PRE0_SUB4:0 bits for pre-emption priority 4 bits for subpriority + \arg NVIC_PRIGROUP_PRE1_SUB3:1 bits for pre-emption priority 3 bits for subpriority + \arg NVIC_PRIGROUP_PRE2_SUB2:2 bits for pre-emption priority 2 bits for subpriority + \arg NVIC_PRIGROUP_PRE3_SUB1:3 bits for pre-emption priority 1 bits for subpriority + \arg NVIC_PRIGROUP_PRE4_SUB0:4 bits for pre-emption priority 0 bits for subpriority + \param[out] none + \retval none +*/ +void nvic_priority_group_set(uint32_t nvic_prigroup) +{ + /* set the priority group value */ + SCB->AIRCR = NVIC_AIRCR_VECTKEY_MASK | nvic_prigroup; +} + +/*! + \brief enable NVIC request + \param[in] nvic_irq: the NVIC interrupt request, detailed in IRQn_Type + \param[in] nvic_irq_pre_priority: the pre-emption priority needed to set + \param[in] nvic_irq_sub_priority: the subpriority needed to set + \param[out] none + \retval none +*/ +void nvic_irq_enable(uint8_t nvic_irq, + uint8_t nvic_irq_pre_priority, + uint8_t nvic_irq_sub_priority) +{ + uint32_t temp_priority = 0x00U, temp_pre = 0x00U, temp_sub = 0x00U; + + /* use the priority group value to get the temp_pre and the temp_sub */ + switch ((SCB->AIRCR) & (uint32_t)0x700U) { + case NVIC_PRIGROUP_PRE0_SUB4: + temp_pre = 0U; + temp_sub = 0x4U; + break; + case NVIC_PRIGROUP_PRE1_SUB3: + temp_pre = 1U; + temp_sub = 0x3U; + break; + case NVIC_PRIGROUP_PRE2_SUB2: + temp_pre = 2U; + temp_sub = 0x2U; + break; + case NVIC_PRIGROUP_PRE3_SUB1: + temp_pre = 3U; + temp_sub = 0x1U; + break; + case NVIC_PRIGROUP_PRE4_SUB0: + temp_pre = 4U; + temp_sub = 0x0U; + break; + default: + nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); + temp_pre = 2U; + temp_sub = 0x2U; + break; + } + + /* get the temp_priority to fill the NVIC->IP register */ + temp_priority = (uint32_t)nvic_irq_pre_priority << (0x4U - temp_pre); + temp_priority |= nvic_irq_sub_priority &(0x0FU >> (0x4U - temp_sub)); + temp_priority = temp_priority << 0x04U; + NVIC->IP[nvic_irq] = (uint8_t)temp_priority; + + /* enable the selected IRQ */ + NVIC->ISER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU); +} + +/*! + \brief disable NVIC request + \param[in] nvic_irq: the NVIC interrupt request, detailed in IRQn_Type + \param[out] none + \retval none +*/ +void nvic_irq_disable(uint8_t nvic_irq) +{ + /* disable the selected IRQ.*/ + NVIC->ICER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU); +} + +/*! + \brief set the NVIC vector table base address + \param[in] nvic_vict_tab: the RAM or FLASH base address + \arg NVIC_VECTTAB_RAM: RAM base address + \are NVIC_VECTTAB_FLASH: Flash base address + \param[in] offset: Vector Table offset + \param[out] none + \retval none +*/ +void nvic_vector_table_set(uint32_t nvic_vict_tab, uint32_t offset) +{ + SCB->VTOR = nvic_vict_tab | (offset & NVIC_VECTTAB_OFFSET_MASK); +} + +/*! + \brief set the state of the low power mode + \param[in] lowpower_mode: the low power mode state + \arg SCB_LPM_SLEEP_EXIT_ISR: if chose this para, the system always enter low power + mode by exiting from ISR + \arg SCB_LPM_DEEPSLEEP: if chose this para, the system will enter the DEEPSLEEP mode + \arg SCB_LPM_WAKE_BY_ALL_INT: if chose this para, the lowpower mode can be woke up + by all the enable and disable interrupts + \param[out] none + \retval none +*/ +void system_lowpower_set(uint8_t lowpower_mode) +{ + SCB->SCR |= (uint32_t)lowpower_mode; +} + +/*! + \brief reset the state of the low power mode + \param[in] lowpower_mode: the low power mode state + \arg SCB_LPM_SLEEP_EXIT_ISR: if chose this para, the system will exit low power + mode by exiting from ISR + \arg SCB_LPM_DEEPSLEEP: if chose this para, the system will enter the SLEEP mode + \arg SCB_LPM_WAKE_BY_ALL_INT: if chose this para, the lowpower mode only can be + woke up by the enable interrupts + \param[out] none + \retval none +*/ +void system_lowpower_reset(uint8_t lowpower_mode) +{ + SCB->SCR &= (~(uint32_t)lowpower_mode); +} + +/*! + \brief set the systick clock source + \param[in] systick_clksource: the systick clock source needed to choose + \arg SYSTICK_CLKSOURCE_HCLK: systick clock source is from HCLK + \arg SYSTICK_CLKSOURCE_HCLK_DIV8: systick clock source is from HCLK/8 + \param[out] none + \retval none +*/ + +void systick_clksource_set(uint32_t systick_clksource) +{ + if(SYSTICK_CLKSOURCE_HCLK == systick_clksource ){ + /* set the systick clock source from HCLK */ + SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK; + }else{ + /* set the systick clock source from HCLK/8 */ + SysTick->CTRL &= SYSTICK_CLKSOURCE_HCLK_DIV8; + } +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_misc.c.base@2.0.2 b/RTE/Device/GD32F107VC/gd32f10x_misc.c.base@2.0.2 new file mode 100644 index 0000000..6978689 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_misc.c.base@2.0.2 @@ -0,0 +1,186 @@ +/*! + \file gd32f10x_misc.c + \brief MISC driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_misc.h" + +/*! + \brief set the priority group + \param[in] nvic_prigroup: the NVIC priority group + \arg NVIC_PRIGROUP_PRE0_SUB4:0 bits for pre-emption priority 4 bits for subpriority + \arg NVIC_PRIGROUP_PRE1_SUB3:1 bits for pre-emption priority 3 bits for subpriority + \arg NVIC_PRIGROUP_PRE2_SUB2:2 bits for pre-emption priority 2 bits for subpriority + \arg NVIC_PRIGROUP_PRE3_SUB1:3 bits for pre-emption priority 1 bits for subpriority + \arg NVIC_PRIGROUP_PRE4_SUB0:4 bits for pre-emption priority 0 bits for subpriority + \param[out] none + \retval none +*/ +void nvic_priority_group_set(uint32_t nvic_prigroup) +{ + /* set the priority group value */ + SCB->AIRCR = NVIC_AIRCR_VECTKEY_MASK | nvic_prigroup; +} + +/*! + \brief enable NVIC request + \param[in] nvic_irq: the NVIC interrupt request, detailed in IRQn_Type + \param[in] nvic_irq_pre_priority: the pre-emption priority needed to set + \param[in] nvic_irq_sub_priority: the subpriority needed to set + \param[out] none + \retval none +*/ +void nvic_irq_enable(uint8_t nvic_irq, + uint8_t nvic_irq_pre_priority, + uint8_t nvic_irq_sub_priority) +{ + uint32_t temp_priority = 0x00U, temp_pre = 0x00U, temp_sub = 0x00U; + + /* use the priority group value to get the temp_pre and the temp_sub */ + switch ((SCB->AIRCR) & (uint32_t)0x700U) { + case NVIC_PRIGROUP_PRE0_SUB4: + temp_pre = 0U; + temp_sub = 0x4U; + break; + case NVIC_PRIGROUP_PRE1_SUB3: + temp_pre = 1U; + temp_sub = 0x3U; + break; + case NVIC_PRIGROUP_PRE2_SUB2: + temp_pre = 2U; + temp_sub = 0x2U; + break; + case NVIC_PRIGROUP_PRE3_SUB1: + temp_pre = 3U; + temp_sub = 0x1U; + break; + case NVIC_PRIGROUP_PRE4_SUB0: + temp_pre = 4U; + temp_sub = 0x0U; + break; + default: + nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); + temp_pre = 2U; + temp_sub = 0x2U; + break; + } + + /* get the temp_priority to fill the NVIC->IP register */ + temp_priority = (uint32_t)nvic_irq_pre_priority << (0x4U - temp_pre); + temp_priority |= nvic_irq_sub_priority &(0x0FU >> (0x4U - temp_sub)); + temp_priority = temp_priority << 0x04U; + NVIC->IP[nvic_irq] = (uint8_t)temp_priority; + + /* enable the selected IRQ */ + NVIC->ISER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU); +} + +/*! + \brief disable NVIC request + \param[in] nvic_irq: the NVIC interrupt request, detailed in IRQn_Type + \param[out] none + \retval none +*/ +void nvic_irq_disable(uint8_t nvic_irq) +{ + /* disable the selected IRQ.*/ + NVIC->ICER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU); +} + +/*! + \brief set the NVIC vector table base address + \param[in] nvic_vict_tab: the RAM or FLASH base address + \arg NVIC_VECTTAB_RAM: RAM base address + \are NVIC_VECTTAB_FLASH: Flash base address + \param[in] offset: Vector Table offset + \param[out] none + \retval none +*/ +void nvic_vector_table_set(uint32_t nvic_vict_tab, uint32_t offset) +{ + SCB->VTOR = nvic_vict_tab | (offset & NVIC_VECTTAB_OFFSET_MASK); +} + +/*! + \brief set the state of the low power mode + \param[in] lowpower_mode: the low power mode state + \arg SCB_LPM_SLEEP_EXIT_ISR: if chose this para, the system always enter low power + mode by exiting from ISR + \arg SCB_LPM_DEEPSLEEP: if chose this para, the system will enter the DEEPSLEEP mode + \arg SCB_LPM_WAKE_BY_ALL_INT: if chose this para, the lowpower mode can be woke up + by all the enable and disable interrupts + \param[out] none + \retval none +*/ +void system_lowpower_set(uint8_t lowpower_mode) +{ + SCB->SCR |= (uint32_t)lowpower_mode; +} + +/*! + \brief reset the state of the low power mode + \param[in] lowpower_mode: the low power mode state + \arg SCB_LPM_SLEEP_EXIT_ISR: if chose this para, the system will exit low power + mode by exiting from ISR + \arg SCB_LPM_DEEPSLEEP: if chose this para, the system will enter the SLEEP mode + \arg SCB_LPM_WAKE_BY_ALL_INT: if chose this para, the lowpower mode only can be + woke up by the enable interrupts + \param[out] none + \retval none +*/ +void system_lowpower_reset(uint8_t lowpower_mode) +{ + SCB->SCR &= (~(uint32_t)lowpower_mode); +} + +/*! + \brief set the systick clock source + \param[in] systick_clksource: the systick clock source needed to choose + \arg SYSTICK_CLKSOURCE_HCLK: systick clock source is from HCLK + \arg SYSTICK_CLKSOURCE_HCLK_DIV8: systick clock source is from HCLK/8 + \param[out] none + \retval none +*/ + +void systick_clksource_set(uint32_t systick_clksource) +{ + if(SYSTICK_CLKSOURCE_HCLK == systick_clksource ){ + /* set the systick clock source from HCLK */ + SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK; + }else{ + /* set the systick clock source from HCLK/8 */ + SysTick->CTRL &= SYSTICK_CLKSOURCE_HCLK_DIV8; + } +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_pmu.c b/RTE/Device/GD32F107VC/gd32f10x_pmu.c new file mode 100644 index 0000000..341335c --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_pmu.c @@ -0,0 +1,282 @@ +/*! + \file gd32f10x_pmu.c + \brief PMU driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2019-11-26, V2.1.1, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_pmu.h" + +/*! + \brief reset PMU register + \param[in] none + \param[out] none + \retval none +*/ +void pmu_deinit(void) +{ + /* reset PMU */ + rcu_periph_reset_enable(RCU_PMURST); + rcu_periph_reset_disable(RCU_PMURST); +} + +/*! + \brief select low voltage detector threshold + \param[in] lvdt_n: + only one parameter can be selected which is shown as below: + \arg PMU_LVDT_0: voltage threshold is 2.2V + \arg PMU_LVDT_1: voltage threshold is 2.3V + \arg PMU_LVDT_2: voltage threshold is 2.4V + \arg PMU_LVDT_3: voltage threshold is 2.5V + \arg PMU_LVDT_4: voltage threshold is 2.6V + \arg PMU_LVDT_5: voltage threshold is 2.7V + \arg PMU_LVDT_6: voltage threshold is 2.8V + \arg PMU_LVDT_7: voltage threshold is 2.9V + \param[out] none + \retval none +*/ +void pmu_lvd_select(uint32_t lvdt_n) +{ + /* disable LVD */ + PMU_CTL &= ~PMU_CTL_LVDEN; + /* clear LVDT bits */ + PMU_CTL &= ~PMU_CTL_LVDT; + /* set LVDT bits according to lvdt_n */ + PMU_CTL |= lvdt_n; + /* enable LVD */ + PMU_CTL |= PMU_CTL_LVDEN; +} + +/*! + \brief disable PMU lvd + \param[in] none + \param[out] none + \retval none +*/ +void pmu_lvd_disable(void) +{ + /* disable LVD */ + PMU_CTL &= ~PMU_CTL_LVDEN; +} + +/*! + \brief PMU work at sleep mode + \param[in] sleepmodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_sleepmode(uint8_t sleepmodecmd) +{ + /* clear sleepdeep bit of Cortex-M3 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + + /* select WFI or WFE command to enter sleep mode */ + if(WFI_CMD == sleepmodecmd){ + __WFI(); + }else{ + __WFE(); + } +} + +/*! + \brief PMU work at deepsleep mode + \param[in] ldo: + only one parameter can be selected which is shown as below: + \arg PMU_LDO_NORMAL: LDO work at normal power mode when pmu enter deepsleep mode + \arg PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode + \param[in] deepsleepmodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_deepsleepmode(uint32_t ldo,uint8_t deepsleepmodecmd) +{ + static uint32_t reg_snap[ 4 ]; + /* clear stbmod and ldolp bits */ + PMU_CTL &= ~((uint32_t)(PMU_CTL_STBMOD | PMU_CTL_LDOLP)); + + /* set ldolp bit according to pmu_ldo */ + PMU_CTL |= ldo; + + /* set sleepdeep bit of Cortex-M3 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + reg_snap[0] = REG32(0xE000E010U); + reg_snap[1] = REG32(0xE000E100U); + reg_snap[2] = REG32(0xE000E104U); + reg_snap[3] = REG32(0xE000E108U); + + REG32( 0xE000E010U ) &= 0x00010004U; + REG32( 0xE000E180U ) = 0XFF7FF83DU; + REG32( 0xE000E184U ) = 0XBFFFF8FFU; + REG32( 0xE000E188U ) = 0xFFFFFFFFU; + + /* select WFI or WFE command to enter deepsleep mode */ + if(WFI_CMD == deepsleepmodecmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } + + REG32(0xE000E010U) = reg_snap[0] ; + REG32(0xE000E100U) = reg_snap[1] ; + REG32(0xE000E104U) = reg_snap[2] ; + REG32(0xE000E108U) = reg_snap[3] ; + + /* reset sleepdeep bit of Cortex-M3 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); +} + +/*! + \brief pmu work at standby mode + \param[in] standbymodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_standbymode(uint8_t standbymodecmd) +{ + /* set sleepdeep bit of Cortex-M3 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* set stbmod bit */ + PMU_CTL |= PMU_CTL_STBMOD; + + /* reset wakeup flag */ + PMU_CTL |= PMU_CTL_WURST; + + /* select WFI or WFE command to enter standby mode */ + if(WFI_CMD == standbymodecmd){ + __WFI(); + }else{ + __WFE(); + } +} + +/*! + \brief enable wakeup pin + \param[in] none + \param[out] none + \retval none +*/ +void pmu_wakeup_pin_enable(void) +{ + PMU_CS |= PMU_CS_WUPEN; +} + +/*! + \brief disable wakeup pin + \param[in] none + \param[out] none + \retval none +*/ +void pmu_wakeup_pin_disable(void) +{ + PMU_CS &= ~PMU_CS_WUPEN; +} + +/*! + \brief enable write access to the registers in backup domain + \param[in] none + \param[out] none + \retval none +*/ +void pmu_backup_write_enable(void) +{ + PMU_CTL |= PMU_CTL_BKPWEN; +} + +/*! + \brief disable write access to the registers in backup domain + \param[in] none + \param[out] none + \retval none +*/ +void pmu_backup_write_disable(void) +{ + PMU_CTL &= ~PMU_CTL_BKPWEN; +} + +/*! + \brief get flag state + \param[in] flag: + only one parameter can be selected which is shown as below: + \arg PMU_FLAG_WAKEUP: wakeup flag + \arg PMU_FLAG_STANDBY: standby flag + \arg PMU_FLAG_LVD: lvd flag + \param[out] none + \retval FlagStatus SET or RESET +*/ +FlagStatus pmu_flag_get(uint32_t flag) +{ + if(PMU_CS & flag){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear flag bit + \param[in] flag_reset: + only one parameter can be selected which is shown as below: + \arg PMU_FLAG_RESET_WAKEUP: reset wakeup flag + \arg PMU_FLAG_RESET_STANDBY: reset standby flag + \param[out] none + \retval none +*/ +void pmu_flag_clear(uint32_t flag_reset) +{ + switch(flag_reset){ + case PMU_FLAG_RESET_WAKEUP: + /* reset wakeup flag */ + PMU_CTL |= PMU_CTL_WURST; + break; + case PMU_FLAG_RESET_STANDBY: + /* reset standby flag */ + PMU_CTL |= PMU_CTL_STBRST; + break; + default : + break; + } +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_pmu.c.base@2.0.2 b/RTE/Device/GD32F107VC/gd32f10x_pmu.c.base@2.0.2 new file mode 100644 index 0000000..341335c --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_pmu.c.base@2.0.2 @@ -0,0 +1,282 @@ +/*! + \file gd32f10x_pmu.c + \brief PMU driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2019-11-26, V2.1.1, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_pmu.h" + +/*! + \brief reset PMU register + \param[in] none + \param[out] none + \retval none +*/ +void pmu_deinit(void) +{ + /* reset PMU */ + rcu_periph_reset_enable(RCU_PMURST); + rcu_periph_reset_disable(RCU_PMURST); +} + +/*! + \brief select low voltage detector threshold + \param[in] lvdt_n: + only one parameter can be selected which is shown as below: + \arg PMU_LVDT_0: voltage threshold is 2.2V + \arg PMU_LVDT_1: voltage threshold is 2.3V + \arg PMU_LVDT_2: voltage threshold is 2.4V + \arg PMU_LVDT_3: voltage threshold is 2.5V + \arg PMU_LVDT_4: voltage threshold is 2.6V + \arg PMU_LVDT_5: voltage threshold is 2.7V + \arg PMU_LVDT_6: voltage threshold is 2.8V + \arg PMU_LVDT_7: voltage threshold is 2.9V + \param[out] none + \retval none +*/ +void pmu_lvd_select(uint32_t lvdt_n) +{ + /* disable LVD */ + PMU_CTL &= ~PMU_CTL_LVDEN; + /* clear LVDT bits */ + PMU_CTL &= ~PMU_CTL_LVDT; + /* set LVDT bits according to lvdt_n */ + PMU_CTL |= lvdt_n; + /* enable LVD */ + PMU_CTL |= PMU_CTL_LVDEN; +} + +/*! + \brief disable PMU lvd + \param[in] none + \param[out] none + \retval none +*/ +void pmu_lvd_disable(void) +{ + /* disable LVD */ + PMU_CTL &= ~PMU_CTL_LVDEN; +} + +/*! + \brief PMU work at sleep mode + \param[in] sleepmodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_sleepmode(uint8_t sleepmodecmd) +{ + /* clear sleepdeep bit of Cortex-M3 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + + /* select WFI or WFE command to enter sleep mode */ + if(WFI_CMD == sleepmodecmd){ + __WFI(); + }else{ + __WFE(); + } +} + +/*! + \brief PMU work at deepsleep mode + \param[in] ldo: + only one parameter can be selected which is shown as below: + \arg PMU_LDO_NORMAL: LDO work at normal power mode when pmu enter deepsleep mode + \arg PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode + \param[in] deepsleepmodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_deepsleepmode(uint32_t ldo,uint8_t deepsleepmodecmd) +{ + static uint32_t reg_snap[ 4 ]; + /* clear stbmod and ldolp bits */ + PMU_CTL &= ~((uint32_t)(PMU_CTL_STBMOD | PMU_CTL_LDOLP)); + + /* set ldolp bit according to pmu_ldo */ + PMU_CTL |= ldo; + + /* set sleepdeep bit of Cortex-M3 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + reg_snap[0] = REG32(0xE000E010U); + reg_snap[1] = REG32(0xE000E100U); + reg_snap[2] = REG32(0xE000E104U); + reg_snap[3] = REG32(0xE000E108U); + + REG32( 0xE000E010U ) &= 0x00010004U; + REG32( 0xE000E180U ) = 0XFF7FF83DU; + REG32( 0xE000E184U ) = 0XBFFFF8FFU; + REG32( 0xE000E188U ) = 0xFFFFFFFFU; + + /* select WFI or WFE command to enter deepsleep mode */ + if(WFI_CMD == deepsleepmodecmd){ + __WFI(); + }else{ + __SEV(); + __WFE(); + __WFE(); + } + + REG32(0xE000E010U) = reg_snap[0] ; + REG32(0xE000E100U) = reg_snap[1] ; + REG32(0xE000E104U) = reg_snap[2] ; + REG32(0xE000E108U) = reg_snap[3] ; + + /* reset sleepdeep bit of Cortex-M3 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); +} + +/*! + \brief pmu work at standby mode + \param[in] standbymodecmd: + only one parameter can be selected which is shown as below: + \arg WFI_CMD: use WFI command + \arg WFE_CMD: use WFE command + \param[out] none + \retval none +*/ +void pmu_to_standbymode(uint8_t standbymodecmd) +{ + /* set sleepdeep bit of Cortex-M3 system control register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* set stbmod bit */ + PMU_CTL |= PMU_CTL_STBMOD; + + /* reset wakeup flag */ + PMU_CTL |= PMU_CTL_WURST; + + /* select WFI or WFE command to enter standby mode */ + if(WFI_CMD == standbymodecmd){ + __WFI(); + }else{ + __WFE(); + } +} + +/*! + \brief enable wakeup pin + \param[in] none + \param[out] none + \retval none +*/ +void pmu_wakeup_pin_enable(void) +{ + PMU_CS |= PMU_CS_WUPEN; +} + +/*! + \brief disable wakeup pin + \param[in] none + \param[out] none + \retval none +*/ +void pmu_wakeup_pin_disable(void) +{ + PMU_CS &= ~PMU_CS_WUPEN; +} + +/*! + \brief enable write access to the registers in backup domain + \param[in] none + \param[out] none + \retval none +*/ +void pmu_backup_write_enable(void) +{ + PMU_CTL |= PMU_CTL_BKPWEN; +} + +/*! + \brief disable write access to the registers in backup domain + \param[in] none + \param[out] none + \retval none +*/ +void pmu_backup_write_disable(void) +{ + PMU_CTL &= ~PMU_CTL_BKPWEN; +} + +/*! + \brief get flag state + \param[in] flag: + only one parameter can be selected which is shown as below: + \arg PMU_FLAG_WAKEUP: wakeup flag + \arg PMU_FLAG_STANDBY: standby flag + \arg PMU_FLAG_LVD: lvd flag + \param[out] none + \retval FlagStatus SET or RESET +*/ +FlagStatus pmu_flag_get(uint32_t flag) +{ + if(PMU_CS & flag){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear flag bit + \param[in] flag_reset: + only one parameter can be selected which is shown as below: + \arg PMU_FLAG_RESET_WAKEUP: reset wakeup flag + \arg PMU_FLAG_RESET_STANDBY: reset standby flag + \param[out] none + \retval none +*/ +void pmu_flag_clear(uint32_t flag_reset) +{ + switch(flag_reset){ + case PMU_FLAG_RESET_WAKEUP: + /* reset wakeup flag */ + PMU_CTL |= PMU_CTL_WURST; + break; + case PMU_FLAG_RESET_STANDBY: + /* reset standby flag */ + PMU_CTL |= PMU_CTL_STBRST; + break; + default : + break; + } +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_rcu.c b/RTE/Device/GD32F107VC/gd32f10x_rcu.c new file mode 100644 index 0000000..20ea2ca --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_rcu.c @@ -0,0 +1,1195 @@ +/*! + \file gd32f10x_rcu.c + \brief RCU driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_rcu.h" + +/* define clock source */ +#define SEL_IRC8M ((uint16_t)0U) +#define SEL_HXTAL ((uint16_t)1U) +#define SEL_PLL ((uint16_t)2U) + +/* define startup timeout count */ +#define OSC_STARTUP_TIMEOUT ((uint32_t)0xFFFFFU) +#define LXTAL_STARTUP_TIMEOUT ((uint32_t)0x3FFFFFFU) + +/*! + \brief deinitialize the RCU + \param[in] none + \param[out] none + \retval none +*/ +void rcu_deinit(void) +{ + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + rcu_osci_stab_wait(RCU_IRC8M); + + RCU_CFG0 &= ~RCU_CFG0_SCS; + + /* reset CTL register */ + RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN); + RCU_CTL &= ~RCU_CTL_HXTALBPS; +#ifdef GD32F10X_CL + RCU_CTL &= ~(RCU_CTL_PLL1EN | RCU_CTL_PLL2EN); +#endif /* GD32F10X_CL */ + + /* reset CFG0 register */ +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0 | RCU_CFG0_PLLMF | + RCU_CFG0_USBDPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_PLLMF_4 | RCU_CFG0_ADCPSC_2); +#elif defined(GD32F10X_CL) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF | + RCU_CFG0_USBFSPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_ADCPSC_2 | RCU_CFG0_PLLMF_4); +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* reset INT and CFG1 register */ +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + RCU_INT = 0x009f0000U; +#elif defined(GD32F10X_CL) + RCU_INT = 0x00ff0000U; + RCU_CFG1 &= ~(RCU_CFG1_PREDV0 | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PLL2MF | + RCU_CFG1_PREDV0SEL | RCU_CFG1_I2S1SEL | RCU_CFG1_I2S2SEL); +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ +} + +/*! + \brief enable the peripherals clock + \param[in] periph: RCU peripherals, refer to rcu_periph_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOx (x=A,B,C,D,E,F,G): GPIO ports clock + \arg RCU_AF : alternate function clock + \arg RCU_CRC: CRC clock + \arg RCU_DMAx (x=0,1): DMA clock + \arg RCU_ENET: ENET clock(CL series available) + \arg RCU_ENETTX: ENETTX clock(CL series available) + \arg RCU_ENETRX: ENETRX clock(CL series available) + \arg RCU_USBD: USBD clock(HD,XD series available) + \arg RCU_USBFS: USBFS clock(CL series available) + \arg RCU_EXMC: EXMC clock + \arg RCU_TIMERx (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD series): TIMER clock + \arg RCU_WWDGT: WWDGT clock + \arg RCU_SPIx (x=0,1,2): SPI clock + \arg RCU_USARTx (x=0,1,2): USART clock + \arg RCU_UARTx (x=3,4): UART clock + \arg RCU_I2Cx (x=0,1): I2C clock + \arg RCU_CANx (x=0,1,CAN1 is only available for CL series): CAN clock + \arg RCU_PMU: PMU clock + \arg RCU_DAC: DAC clock + \arg RCU_RTC: RTC clock + \arg RCU_ADCx (x=0,1,2,ADC2 is not available for CL series): ADC clock + \arg RCU_SDIO: SDIO clock(not available for CL series) + \arg RCU_BKPI: BKP interface clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_enable(rcu_periph_enum periph) +{ + RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief disable the peripherals clock + \param[in] periph: RCU peripherals, refer to rcu_periph_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOx (x=A,B,C,D,E,F,G): GPIO ports clock + \arg RCU_AF: alternate function clock + \arg RCU_CRC: CRC clock + \arg RCU_DMAx (x=0,1): DMA clock + \arg RCU_ENET: ENET clock(CL series available) + \arg RCU_ENETTX: ENETTX clock(CL series available) + \arg RCU_ENETRX: ENETRX clock(CL series available) + \arg RCU_USBD: USBD clock(HD,XD series available) + \arg RCU_USBFS: USBFS clock(CL series available) + \arg RCU_EXMC: EXMC clock + \arg RCU_TIMERx (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD series): TIMER clock + \arg RCU_WWDGT: WWDGT clock + \arg RCU_SPIx (x=0,1,2): SPI clock + \arg RCU_USARTx (x=0,1,2): USART clock + \arg RCU_UARTx (x=3,4): UART clock + \arg RCU_I2Cx (x=0,1): I2C clock + \arg RCU_CANx (x=0,1,CAN1 is only available for CL series): CAN clock + \arg RCU_PMU: PMU clock + \arg RCU_DAC: DAC clock + \arg RCU_RTC: RTC clock + \arg RCU_ADCx (x=0,1,2,ADC2 is not available for CL series): ADC clock + \arg RCU_SDIO: SDIO clock(not available for CL series) + \arg RCU_BKPI: BKP interface clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_disable(rcu_periph_enum periph) +{ + RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief enable the peripherals clock when sleep mode + \param[in] periph: RCU peripherals, refer to rcu_periph_sleep_enum + only one parameter can be selected which is shown as below: + \arg RCU_FMC_SLP: FMC clock + \arg RCU_SRAM_SLP: SRAM clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_sleep_enable(rcu_periph_sleep_enum periph) +{ + RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief disable the peripherals clock when sleep mode + \param[in] periph: RCU peripherals, refer to rcu_periph_sleep_enum + only one parameter can be selected which is shown as below: + \arg RCU_FMC_SLP: FMC clock + \arg RCU_SRAM_SLP: SRAM clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_sleep_disable(rcu_periph_sleep_enum periph) +{ + RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief reset the peripherals + \param[in] periph_reset: RCU peripherals reset, refer to rcu_periph_reset_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOxRST (x=A,B,C,D,E,F,G): reset GPIO ports + \arg RCU_AFRST : reset alternate function clock + \arg RCU_ENETRST: reset ENET(CL series available) + \arg RCU_USBDRST: reset USBD(HD,XD series available) + \arg RCU_USBFSRST: reset USBFS(CL series available) + \arg RCU_TIMERxRST (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD series): reset TIMER + \arg RCU_WWDGTRST: reset WWDGT + \arg RCU_SPIxRST (x=0,1,2): reset SPI + \arg RCU_USARTxRST (x=0,1,2): reset USART + \arg RCU_UARTxRST (x=3,4): reset UART + \arg RCU_I2CxRST (x=0,1): reset I2C + \arg RCU_CANxRST (x=0,1,CAN1 is only available for CL series): reset CAN + \arg RCU_PMURST: reset PMU + \arg RCU_DACRST: reset DAC + \arg RCU_ADCxRST (x=0,1,2, ADC2 is not available for CL series): reset ADC + \arg RCU_BKPIRST: reset BKPI + \param[out] none + \retval none +*/ +void rcu_periph_reset_enable(rcu_periph_reset_enum periph_reset) +{ + RCU_REG_VAL(periph_reset) |= BIT(RCU_BIT_POS(periph_reset)); +} + +/*! + \brief disable reset the peripheral + \param[in] periph_reset: RCU peripherals reset, refer to rcu_periph_reset_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOxRST (x=A,B,C,D,E,F,G): reset GPIO ports + \arg RCU_AFRST : reset alternate function clock + \arg RCU_ENETRST: reset ENET(CL series available) + \arg RCU_USBDRST: reset USBD(HD,XD series available) + \arg RCU_USBFSRST: reset USBFS(CL series available) + \arg RCU_TIMERxRST (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD series): reset TIMER + \arg RCU_WWDGTRST: reset WWDGT + \arg RCU_SPIxRST (x=0,1,2): reset SPI + \arg RCU_USARTxRST (x=0,1,2): reset USART + \arg RCU_UARTxRST (x=3,4): reset UART + \arg RCU_I2CxRST (x=0,1): reset I2C + \arg RCU_CANxRST (x=0,1,CAN1 is only available for CL series): reset CAN + \arg RCU_PMURST: reset PMU + \arg RCU_DACRST: reset DAC + \arg RCU_ADCxRST (x=0,1,2, ADC2 is not available for CL series): reset ADC + \arg RCU_BKPIRST: reset BKPI + \param[out] none + \retval none +*/ +void rcu_periph_reset_disable(rcu_periph_reset_enum periph_reset) +{ + RCU_REG_VAL(periph_reset) &= ~BIT(RCU_BIT_POS(periph_reset)); +} + +/*! + \brief reset the BKP domain + \param[in] none + \param[out] none + \retval none +*/ +void rcu_bkp_reset_enable(void) +{ + RCU_BDCTL |= RCU_BDCTL_BKPRST; +} + +/*! + \brief disable the BKP domain reset + \param[in] none + \param[out] none + \retval none +*/ +void rcu_bkp_reset_disable(void) +{ + RCU_BDCTL &= ~RCU_BDCTL_BKPRST; +} + +/*! + \brief configure the system clock source + \param[in] ck_sys: system clock source select + only one parameter can be selected which is shown as below: + \arg RCU_CKSYSSRC_IRC8M: select CK_IRC8M as the CK_SYS source + \arg RCU_CKSYSSRC_HXTAL: select CK_HXTAL as the CK_SYS source + \arg RCU_CKSYSSRC_PLL: select CK_PLL as the CK_SYS source + \param[out] none + \retval none +*/ +void rcu_system_clock_source_config(uint32_t ck_sys) +{ + uint32_t reg; + + reg = RCU_CFG0; + /* reset the SCS bits and set according to ck_sys */ + reg &= ~RCU_CFG0_SCS; + RCU_CFG0 = (reg | ck_sys); +} + +/*! + \brief get the system clock source + \param[in] none + \param[out] none + \retval which clock is selected as CK_SYS source + \arg RCU_SCSS_IRC8M: CK_IRC8M is selected as the CK_SYS source + \arg RCU_SCSS_HXTAL: CK_HXTAL is selected as the CK_SYS source + \arg RCU_SCSS_PLL: CK_PLL is selected as the CK_SYS source +*/ +uint32_t rcu_system_clock_source_get(void) +{ + return (RCU_CFG0 & RCU_CFG0_SCSS); +} + +/*! + \brief configure the AHB clock prescaler selection + \param[in] ck_ahb: AHB clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_AHB_CKSYS_DIVx, x=1, 2, 4, 8, 16, 64, 128, 256, 512 + \param[out] none + \retval none +*/ +void rcu_ahb_clock_config(uint32_t ck_ahb) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the AHBPSC bits and set according to ck_ahb */ + reg &= ~RCU_CFG0_AHBPSC; + RCU_CFG0 = (reg | ck_ahb); +} + +/*! + \brief configure the APB1 clock prescaler selection + \param[in] ck_apb1: APB1 clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_APB1_CKAHB_DIV1: select CK_AHB as CK_APB1 + \arg RCU_APB1_CKAHB_DIV2: select CK_AHB/2 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV4: select CK_AHB/4 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV8: select CK_AHB/8 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV16: select CK_AHB/16 as CK_APB1 + \param[out] none + \retval none +*/ +void rcu_apb1_clock_config(uint32_t ck_apb1) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the APB1PSC and set according to ck_apb1 */ + reg &= ~RCU_CFG0_APB1PSC; + RCU_CFG0 = (reg | ck_apb1); +} + +/*! + \brief configure the APB2 clock prescaler selection + \param[in] ck_apb2: APB2 clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_APB2_CKAHB_DIV1: select CK_AHB as CK_APB2 + \arg RCU_APB2_CKAHB_DIV2: select CK_AHB/2 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV4: select CK_AHB/4 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV8: select CK_AHB/8 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV16: select CK_AHB/16 as CK_APB2 + \param[out] none + \retval none +*/ +void rcu_apb2_clock_config(uint32_t ck_apb2) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the APB2PSC and set according to ck_apb2 */ + reg &= ~RCU_CFG0_APB2PSC; + RCU_CFG0 = (reg | ck_apb2); +} + +/*! + \brief configure the CK_OUT0 clock source + \param[in] ckout0_src: CK_OUT0 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_CKOUT0SRC_NONE: no clock selected + \arg RCU_CKOUT0SRC_CKSYS: system clock selected + \arg RCU_CKOUT0SRC_IRC8M: high speed 8M internal oscillator clock selected + \arg RCU_CKOUT0SRC_HXTAL: HXTAL selected + \arg RCU_CKOUT0SRC_CKPLL_DIV2: CK_PLL/2 selected + \arg RCU_CKOUT0SRC_CKPLL1: CK_PLL1 selected + \arg RCU_CKOUT0SRC_CKPLL2_DIV2: CK_PLL2/2 selected + \arg RCU_CKOUT0SRC_EXT1: EXT1 selected + \arg RCU_CKOUT0SRC_CKPLL2: PLL2 selected + \param[out] none + \retval none +*/ +void rcu_ckout0_config(uint32_t ckout0_src) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the CKOUT0SRC, set according to ckout0_src */ + reg &= ~RCU_CFG0_CKOUT0SEL; + RCU_CFG0 = (reg | ckout0_src); +} + +/*! + \brief configure the main PLL clock + \param[in] pll_src: PLL clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PLLSRC_IRC8M_DIV2: IRC8M/2 clock selected as source clock of PLL + \arg RCU_PLLSRC_HXTAL: HXTAL selected as source clock of PLL + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL_MULx (XD series x = 2..32, CL series x = 2..14, 6.5, 16..32) + \param[out] none + \retval none +*/ +void rcu_pll_config(uint32_t pll_src, uint32_t pll_mul) +{ + uint32_t reg = 0U; + + reg = RCU_CFG0; + + /* PLL clock source and multiplication factor configuration */ + reg &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + reg |= (pll_src | pll_mul); + + RCU_CFG0 = reg; +} + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) +/*! + \brief configure the PREDV0 division factor + \param[in] predv0_div: PREDV0 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0_DIVx, x = 1,2 + \param[out] none + \retval none +*/ +void rcu_predv0_config(uint32_t predv0_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG0; + /* reset PREDV0 bit */ + reg &= ~RCU_CFG0_PREDV0; + if(RCU_PREDV0_DIV2 == predv0_div){ + /* set the PREDV0 bit */ + reg |= RCU_CFG0_PREDV0; + } + + RCU_CFG0 = reg; +} +#elif defined(GD32F10X_CL) +/*! + \brief configure the PREDV0 division factor and clock source + \param[in] predv0_source: PREDV0 input clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0SRC_HXTAL: HXTAL selected as PREDV0 input source clock + \arg RCU_PREDV0SRC_CKPLL1: CK_PLL1 selected as PREDV0 input source clock + \param[in] predv0_div: PREDV0 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0_DIVx, x = 1..16 + \param[out] none + \retval none +*/ +void rcu_predv0_config(uint32_t predv0_source, uint32_t predv0_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + /* reset PREDV0SEL and PREDV0 bits */ + reg &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV0); + /* set the PREDV0SEL and PREDV0 division factor */ + reg |= (predv0_source | predv0_div); + + RCU_CFG1 = reg; +} + +/*! + \brief configure the PREDV1 division factor + \param[in] predv1_div: PREDV1 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV1_DIVx, x = 1..16 + \param[out] none + \retval none +*/ +void rcu_predv1_config(uint32_t predv1_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + /* reset the PREDV1 bits */ + reg &= ~RCU_CFG1_PREDV1; + /* set the PREDV1 division factor */ + reg |= predv1_div; + + RCU_CFG1 = reg; +} + +/*! + \brief configure the PLL1 clock + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL1_MULx (x = 8..16, 20) + \param[out] none + \retval none +*/ +void rcu_pll1_config(uint32_t pll_mul) +{ + RCU_CFG1 &= ~RCU_CFG1_PLL1MF; + RCU_CFG1 |= pll_mul; +} + +/*! + \brief configure the PLL2 clock + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL2_MULx (x = 8..16, 20) + \param[out] none + \retval none +*/ +void rcu_pll2_config(uint32_t pll_mul) +{ + RCU_CFG1 &= ~RCU_CFG1_PLL2MF; + RCU_CFG1 |= pll_mul; +} +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + +/*! + \brief configure the ADC prescaler factor + \param[in] adc_psc: ADC prescaler factor + only one parameter can be selected which is shown as below: + \arg RCU_CKADC_CKAPB2_DIV2: ADC prescaler select CK_APB2/2 + \arg RCU_CKADC_CKAPB2_DIV4: ADC prescaler select CK_APB2/4 + \arg RCU_CKADC_CKAPB2_DIV6: ADC prescaler select CK_APB2/6 + \arg RCU_CKADC_CKAPB2_DIV8: ADC prescaler select CK_APB2/8 + \arg RCU_CKADC_CKAPB2_DIV12: ADC prescaler select CK_APB2/12 + \arg RCU_CKADC_CKAPB2_DIV16: ADC prescaler select CK_APB2/16 + \param[out] none + \retval none +*/ +void rcu_adc_clock_config(uint32_t adc_psc) +{ + uint32_t reg0; + + /* reset the ADCPSC bits */ + reg0 = RCU_CFG0; + reg0 &= ~(RCU_CFG0_ADCPSC_2 | RCU_CFG0_ADCPSC); + + /* set the ADC prescaler factor */ + switch(adc_psc){ + case RCU_CKADC_CKAPB2_DIV2: + case RCU_CKADC_CKAPB2_DIV4: + case RCU_CKADC_CKAPB2_DIV6: + case RCU_CKADC_CKAPB2_DIV8: + reg0 |= (adc_psc << 14); + break; + + case RCU_CKADC_CKAPB2_DIV12: + case RCU_CKADC_CKAPB2_DIV16: + adc_psc &= ~BIT(2); + reg0 |= (adc_psc << 14 | RCU_CFG0_ADCPSC_2); + break; + + default: + break; + } + + /* set the register */ + RCU_CFG0 = reg0; +} + +/*! + \brief configure the USBD/USBFS prescaler factor + \param[in] usb_psc: USB prescaler factor + only one parameter can be selected which is shown as below: + \arg RCU_CKUSB_CKPLL_DIV1_5: USBD/USBFS prescaler select CK_PLL/1.5 + \arg RCU_CKUSB_CKPLL_DIV1: USBD/USBFS prescaler select CK_PLL/1 + \arg RCU_CKUSB_CKPLL_DIV2_5: USBD/USBFS prescaler select CK_PLL/2.5 + \arg RCU_CKUSB_CKPLL_DIV2: USBD/USBFS prescaler select CK_PLL/2 + \param[out] none + \retval none +*/ +void rcu_usb_clock_config(uint32_t usb_psc) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* configure the USBD/USBFS prescaler factor */ +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + reg &= ~RCU_CFG0_USBDPSC; +#elif defined(GD32F10X_CL) + reg &= ~RCU_CFG0_USBFSPSC; +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + RCU_CFG0 = (reg | usb_psc); +} + +/*! + \brief configure the RTC clock source selection + \param[in] rtc_clock_source: RTC clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_RTCSRC_NONE: no clock selected + \arg RCU_RTCSRC_LXTAL: CK_LXTAL selected as RTC source clock + \arg RCU_RTCSRC_IRC40K: CK_IRC40K selected as RTC source clock + \arg RCU_RTCSRC_HXTAL_DIV_128: CK_HXTAL/128 selected as RTC source clock + \param[out] none + \retval none +*/ +void rcu_rtc_clock_config(uint32_t rtc_clock_source) +{ + uint32_t reg; + + reg = RCU_BDCTL; + /* reset the RTCSRC bits and set according to rtc_clock_source */ + reg &= ~RCU_BDCTL_RTCSRC; + RCU_BDCTL = (reg | rtc_clock_source); +} + +#ifdef GD32F10X_CL +/*! + \brief configure the I2S1 clock source selection + \param[in] i2s_clock_source: I2S1 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2S1SRC_CKSYS: System clock selected as I2S1 source clock + \arg RCU_I2S1SRC_CKPLL2_MUL2: CK_PLL2x2 selected as I2S1 source clock + \param[out] none + \retval none +*/ +void rcu_i2s1_clock_config(uint32_t i2s_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S1SEL bit and set according to i2s_clock_source */ + reg &= ~RCU_CFG1_I2S1SEL; + RCU_CFG1 = (reg | i2s_clock_source); +} + +/*! + \brief configure the I2S2 clock source selection + \param[in] i2s_clock_source: I2S2 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2S2SRC_CKSYS: system clock selected as I2S2 source clock + \arg RCU_I2S2SRC_CKPLL2_MUL2: CK_PLL2x2 selected as I2S2 source clock + \param[out] none + \retval none +*/ +void rcu_i2s2_clock_config(uint32_t i2s_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S2SEL bit and set according to i2s_clock_source */ + reg &= ~RCU_CFG1_I2S2SEL; + RCU_CFG1 = (reg | i2s_clock_source); +} +#endif /* GD32F10X_CL */ + +/*! + \brief get the clock stabilization and periphral reset flags + \param[in] flag: the clock stabilization and periphral reset flags, refer to rcu_flag_enum + only one parameter can be selected which is shown as below: + \arg RCU_FLAG_IRC8MSTB: IRC8M stabilization flag + \arg RCU_FLAG_HXTALSTB: HXTAL stabilization flag + \arg RCU_FLAG_PLLSTB: PLL stabilization flag + \arg RCU_FLAG_PLL1STB: PLL1 stabilization flag(CL series only) + \arg RCU_FLAG_PLL2STB: PLL2 stabilization flag(CL series only) + \arg RCU_FLAG_LXTALSTB: LXTAL stabilization flag + \arg RCU_FLAG_IRC40KSTB: IRC40K stabilization flag + \arg RCU_FLAG_EPRST: external PIN reset flag + \arg RCU_FLAG_PORRST: power reset flag + \arg RCU_FLAG_SWRST: software reset flag + \arg RCU_FLAG_FWDGTRST: free watchdog timer reset flag + \arg RCU_FLAG_WWDGTRST: window watchdog timer reset flag + \arg RCU_FLAG_LPRST: low-power reset flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus rcu_flag_get(rcu_flag_enum flag) +{ + /* get the rcu flag */ + if(RESET != (RCU_REG_VAL(flag) & BIT(RCU_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear all the reset flag + \param[in] none + \param[out] none + \retval none +*/ +void rcu_all_reset_flag_clear(void) +{ + RCU_RSTSCK |= RCU_RSTSCK_RSTFC; +} + +/*! + \brief get the clock stabilization interrupt and ckm flags + \param[in] int_flag: interrupt and ckm flags, refer to rcu_int_flag_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_FLAG_IRC40KSTB: IRC40K stabilization interrupt flag + \arg RCU_INT_FLAG_LXTALSTB: LXTAL stabilization interrupt flag + \arg RCU_INT_FLAG_IRC8MSTB: IRC8M stabilization interrupt flag + \arg RCU_INT_FLAG_HXTALSTB: HXTAL stabilization interrupt flag + \arg RCU_INT_FLAG_PLLSTB: PLL stabilization interrupt flag + \arg RCU_INT_FLAG_PLL1STB: PLL1 stabilization interrupt flag(CL series only) + \arg RCU_INT_FLAG_PLL2STB: PLL2 stabilization interrupt flag(CL series only) + \arg RCU_INT_FLAG_CKM: HXTAL clock stuck interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus rcu_interrupt_flag_get(rcu_int_flag_enum int_flag) +{ + /* get the rcu interrupt flag */ + if(RESET != (RCU_REG_VAL(int_flag) & BIT(RCU_BIT_POS(int_flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear the interrupt flags + \param[in] int_flag_clear: clock stabilization and stuck interrupt flags clear, refer to rcu_int_flag_clear_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_FLAG_IRC40KSTB_CLR: IRC40K stabilization interrupt flag clear + \arg RCU_INT_FLAG_LXTALSTB_CLR: LXTAL stabilization interrupt flag clear + \arg RCU_INT_FLAG_IRC8MSTB_CLR: IRC8M stabilization interrupt flag clear + \arg RCU_INT_FLAG_HXTALSTB_CLR: HXTAL stabilization interrupt flag clear + \arg RCU_INT_FLAG_PLLSTB_CLR: PLL stabilization interrupt flag clear + \arg RCU_INT_FLAG_PLL1STB_CLR: PLL1 stabilization interrupt flag clear(CL series only) + \arg RCU_INT_FLAG_PLL2STB_CLR: PLL2 stabilization interrupt flag clear(CL series only) + \arg RCU_INT_FLAG_CKM_CLR: clock stuck interrupt flag clear + \param[out] none + \retval none +*/ +void rcu_interrupt_flag_clear(rcu_int_flag_clear_enum int_flag_clear) +{ + RCU_REG_VAL(int_flag_clear) |= BIT(RCU_BIT_POS(int_flag_clear)); +} + +/*! + \brief enable the stabilization interrupt + \param[in] stab_int: clock stabilization interrupt, refer to rcu_int_enum + Only one parameter can be selected which is shown as below: + \arg RCU_INT_IRC40KSTB: IRC40K stabilization interrupt enable + \arg RCU_INT_LXTALSTB: LXTAL stabilization interrupt enable + \arg RCU_INT_IRC8MSTB: IRC8M stabilization interrupt enable + \arg RCU_INT_HXTALSTB: HXTAL stabilization interrupt enable + \arg RCU_INT_PLLSTB: PLL stabilization interrupt enable + \arg RCU_INT_PLL1STB: PLL1 stabilization interrupt enable(CL series only) + \arg RCU_INT_PLL2STB: PLL2 stabilization interrupt enable(CL series only) + \param[out] none + \retval none +*/ +void rcu_interrupt_enable(rcu_int_enum stab_int) +{ + RCU_REG_VAL(stab_int) |= BIT(RCU_BIT_POS(stab_int)); +} + +/*! + \brief disable the stabilization interrupt + \param[in] stab_int: clock stabilization interrupt, refer to rcu_int_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_IRC40KSTB: IRC40K stabilization interrupt enable + \arg RCU_INT_LXTALSTB: LXTAL stabilization interrupt enable + \arg RCU_INT_IRC8MSTB: IRC8M stabilization interrupt enable + \arg RCU_INT_HXTALSTB: HXTAL stabilization interrupt enable + \arg RCU_INT_PLLSTB: PLL stabilization interrupt enable + \arg RCU_INT_PLL1STB: PLL1 stabilization interrupt enable(CL series only) + \arg RCU_INT_PLL2STB: PLL2 stabilization interrupt enable(CL series only) + \param[out] none + \retval none +*/ +void rcu_interrupt_disable(rcu_int_enum stab_int) +{ + RCU_REG_VAL(stab_int) &= ~BIT(RCU_BIT_POS(stab_int)); +} + +/*! + \brief wait for oscillator stabilization flags is SET or oscillator startup is timeout + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL series only) + \arg RCU_PLL2_CK: phase locked loop 2(CL series only) + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus rcu_osci_stab_wait(rcu_osci_type_enum osci) +{ + uint32_t stb_cnt = 0U; + ErrStatus reval = ERROR; + FlagStatus osci_stat = RESET; + + switch(osci){ + /* wait HXTAL stable */ + case RCU_HXTAL: + while((RESET == osci_stat) && (HXTAL_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_HXTALSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_HXTALSTB)){ + reval = SUCCESS; + } + break; + + /* wait LXTAL stable */ + case RCU_LXTAL: + while((RESET == osci_stat) && (LXTAL_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_LXTALSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_LXTALSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC8M stable */ + case RCU_IRC8M: + while((RESET == osci_stat) && (IRC8M_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC8MSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_IRC8MSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC40K stable */ + case RCU_IRC40K: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC40KSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_IRC40KSTB)){ + reval = SUCCESS; + } + break; + + /* wait PLL stable */ + case RCU_PLL_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLLSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLLSTB)){ + reval = SUCCESS; + } + break; + +#ifdef GD32F10X_CL + /* wait PLL1 stable */ + case RCU_PLL1_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLL1STB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLL1STB)){ + reval = SUCCESS; + } + break; + /* wait PLL2 stable */ + case RCU_PLL2_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLL2STB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLL2STB)){ + reval = SUCCESS; + } + break; +#endif /* GD32F10X_CL */ + + default: + break; + } + + /* return value */ + return reval; +} + +/*! + \brief turn on the oscillator + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL series only) + \arg RCU_PLL2_CK: phase locked loop 2(CL series only) + \param[out] none + \retval none +*/ +void rcu_osci_on(rcu_osci_type_enum osci) +{ + RCU_REG_VAL(osci) |= BIT(RCU_BIT_POS(osci)); +} + +/*! + \brief turn off the oscillator + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL series only) + \arg RCU_PLL2_CK: phase locked loop 2(CL series only) + \param[out] none + \retval none +*/ +void rcu_osci_off(rcu_osci_type_enum osci) +{ + RCU_REG_VAL(osci) &= ~BIT(RCU_BIT_POS(osci)); +} + +/*! + \brief enable the oscillator bypass mode, HXTALEN or LXTALEN must be reset before it + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \param[out] none + \retval none +*/ +void rcu_osci_bypass_mode_enable(rcu_osci_type_enum osci) +{ + uint32_t reg; + + switch(osci){ + /* enable HXTAL to bypass mode */ + case RCU_HXTAL: + reg = RCU_CTL; + RCU_CTL &= ~RCU_CTL_HXTALEN; + RCU_CTL = (reg | RCU_CTL_HXTALBPS); + break; + /* enable LXTAL to bypass mode */ + case RCU_LXTAL: + reg = RCU_BDCTL; + RCU_BDCTL &= ~RCU_BDCTL_LXTALEN; + RCU_BDCTL = (reg | RCU_BDCTL_LXTALBPS); + break; + case RCU_IRC8M: + case RCU_IRC40K: + case RCU_PLL_CK: +#ifdef GD32F10X_CL + case RCU_PLL1_CK: + case RCU_PLL2_CK: +#endif /* GD32F10X_CL */ + break; + default: + break; + } +} + +/*! + \brief disable the oscillator bypass mode, HXTALEN or LXTALEN must be reset before it + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \param[out] none + \retval none +*/ +void rcu_osci_bypass_mode_disable(rcu_osci_type_enum osci) +{ + uint32_t reg; + + switch(osci){ + /* disable HXTAL to bypass mode */ + case RCU_HXTAL: + reg = RCU_CTL; + RCU_CTL &= ~RCU_CTL_HXTALEN; + RCU_CTL = (reg & ~RCU_CTL_HXTALBPS); + break; + /* disable LXTAL to bypass mode */ + case RCU_LXTAL: + reg = RCU_BDCTL; + RCU_BDCTL &= ~RCU_BDCTL_LXTALEN; + RCU_BDCTL = (reg & ~RCU_BDCTL_LXTALBPS); + break; + case RCU_IRC8M: + case RCU_IRC40K: + case RCU_PLL_CK: +#ifdef GD32F10X_CL + case RCU_PLL1_CK: + case RCU_PLL2_CK: +#endif /* GD32F10X_CL */ + break; + default: + break; + } +} + +/*! + \brief enable the HXTAL clock monitor + \param[in] none + \param[out] none + \retval none +*/ + +void rcu_hxtal_clock_monitor_enable(void) +{ + RCU_CTL |= RCU_CTL_CKMEN; +} + +/*! + \brief disable the HXTAL clock monitor + \param[in] none + \param[out] none + \retval none +*/ +void rcu_hxtal_clock_monitor_disable(void) +{ + RCU_CTL &= ~RCU_CTL_CKMEN; +} + +/*! + \brief set the IRC8M adjust value + \param[in] irc8m_adjval: IRC8M adjust value, must be between 0 and 0x1F + \param[out] none + \retval none +*/ +void rcu_irc8m_adjust_value_set(uint8_t irc8m_adjval) +{ + uint32_t reg; + + reg = RCU_CTL; + /* reset the IRC8MADJ bits and set according to irc8m_adjval */ + reg &= ~RCU_CTL_IRC8MADJ; + RCU_CTL = (reg | ((irc8m_adjval & 0x1FU) << 3)); +} + +/*! + \brief deep-sleep mode voltage select + \param[in] dsvol: deep sleep mode voltage + only one parameter can be selected which is shown as below: + \arg RCU_DEEPSLEEP_V_1_2: the core voltage is 1.2V + \arg RCU_DEEPSLEEP_V_1_1: the core voltage is 1.1V + \arg RCU_DEEPSLEEP_V_1_0: the core voltage is 1.0V + \arg RCU_DEEPSLEEP_V_0_9: the core voltage is 0.9V + \param[out] none + \retval none +*/ +void rcu_deepsleep_voltage_set(uint32_t dsvol) +{ + dsvol &= RCU_DSV_DSLPVS; + RCU_DSV = dsvol; +} + +/*! + \brief get the system clock, bus and peripheral clock frequency + \param[in] clock: the clock frequency which to get + only one parameter can be selected which is shown as below: + \arg CK_SYS: system clock frequency + \arg CK_AHB: AHB clock frequency + \arg CK_APB1: APB1 clock frequency + \arg CK_APB2: APB2 clock frequency + \param[out] none + \retval clock frequency of system, AHB, APB1, APB2 +*/ +uint32_t rcu_clock_freq_get(rcu_clock_freq_enum clock) +{ + uint32_t sws, ck_freq = 0U; + uint32_t cksys_freq, ahb_freq, apb1_freq, apb2_freq; + uint32_t pllsel, predv0sel, pllmf,ck_src, idx, clk_exp; +#ifdef GD32F10X_CL + uint32_t predv0, predv1, pll1mf; +#endif /* GD32F10X_CL */ + + /* exponent of AHB, APB1 and APB2 clock divider */ + uint8_t ahb_exp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + uint8_t apb1_exp[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + uint8_t apb2_exp[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + + sws = GET_BITS(RCU_CFG0, 2, 3); + switch(sws){ + /* IRC8M is selected as CK_SYS */ + case SEL_IRC8M: + cksys_freq = IRC8M_VALUE; + break; + /* HXTAL is selected as CK_SYS */ + case SEL_HXTAL: + cksys_freq = HXTAL_VALUE; + break; + /* PLL is selected as CK_SYS */ + case SEL_PLL: + /* PLL clock source selection, HXTAL or IRC8M/2 */ + pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL); + + if(RCU_PLLSRC_HXTAL == pllsel) { + /* PLL clock source is HXTAL */ + ck_src = HXTAL_VALUE; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + predv0sel = (RCU_CFG0 & RCU_CFG0_PREDV0); + /* PREDV0 input source clock divided by 2 */ + if(RCU_CFG0_PREDV0 == predv0sel){ + ck_src = HXTAL_VALUE/2U; + } +#elif defined(GD32F10X_CL) + predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL); + /* source clock use PLL1 */ + if(RCU_PREDV0SRC_CKPLL1 == predv0sel){ + predv1 = (uint32_t)((RCU_CFG1 & RCU_CFG1_PREDV1) >> 4) + 1U; + pll1mf = (uint32_t)((RCU_CFG1 & RCU_CFG1_PLL1MF) >> 8) + 2U; + if(17U == pll1mf){ + pll1mf = 20U; + } + ck_src = (ck_src / predv1) * pll1mf; + } + predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U; + ck_src /= predv0; +#endif /* GD32F10X_HD and GD32F10X_XD */ + }else{ + /* PLL clock source is IRC8M/2 */ + ck_src = IRC8M_VALUE/2U; + } + + /* PLL multiplication factor */ + pllmf = GET_BITS(RCU_CFG0, 18, 21); + if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){ + pllmf |= 0x10U; + } + if(pllmf < 15U){ + pllmf += 2U; + }else{ + pllmf += 1U; + } + + cksys_freq = ck_src * pllmf; + + #ifdef GD32F10X_CL + if(15U == pllmf){ + /* PLL source clock multiply by 6.5 */ + cksys_freq = ck_src * 6U + ck_src / 2U; + } + #endif /* GD32F10X_CL */ + + break; + /* IRC8M is selected as CK_SYS */ + default: + cksys_freq = IRC8M_VALUE; + break; + } + + /* calculate AHB clock frequency */ + idx = GET_BITS(RCU_CFG0, 4, 7); + clk_exp = ahb_exp[idx]; + ahb_freq = cksys_freq >> clk_exp; + + /* calculate APB1 clock frequency */ + idx = GET_BITS(RCU_CFG0, 8, 10); + clk_exp = apb1_exp[idx]; + apb1_freq = ahb_freq >> clk_exp; + + /* calculate APB2 clock frequency */ + idx = GET_BITS(RCU_CFG0, 11, 13); + clk_exp = apb2_exp[idx]; + apb2_freq = ahb_freq >> clk_exp; + + /* return the clocks frequency */ + switch(clock){ + case CK_SYS: + ck_freq = cksys_freq; + break; + case CK_AHB: + ck_freq = ahb_freq; + break; + case CK_APB1: + ck_freq = apb1_freq; + break; + case CK_APB2: + ck_freq = apb2_freq; + break; + default: + break; + } + return ck_freq; +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_rcu.c.base@2.0.2 b/RTE/Device/GD32F107VC/gd32f10x_rcu.c.base@2.0.2 new file mode 100644 index 0000000..20ea2ca --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_rcu.c.base@2.0.2 @@ -0,0 +1,1195 @@ +/*! + \file gd32f10x_rcu.c + \brief RCU driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.0, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_rcu.h" + +/* define clock source */ +#define SEL_IRC8M ((uint16_t)0U) +#define SEL_HXTAL ((uint16_t)1U) +#define SEL_PLL ((uint16_t)2U) + +/* define startup timeout count */ +#define OSC_STARTUP_TIMEOUT ((uint32_t)0xFFFFFU) +#define LXTAL_STARTUP_TIMEOUT ((uint32_t)0x3FFFFFFU) + +/*! + \brief deinitialize the RCU + \param[in] none + \param[out] none + \retval none +*/ +void rcu_deinit(void) +{ + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + rcu_osci_stab_wait(RCU_IRC8M); + + RCU_CFG0 &= ~RCU_CFG0_SCS; + + /* reset CTL register */ + RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN); + RCU_CTL &= ~RCU_CTL_HXTALBPS; +#ifdef GD32F10X_CL + RCU_CTL &= ~(RCU_CTL_PLL1EN | RCU_CTL_PLL2EN); +#endif /* GD32F10X_CL */ + + /* reset CFG0 register */ +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0 | RCU_CFG0_PLLMF | + RCU_CFG0_USBDPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_PLLMF_4 | RCU_CFG0_ADCPSC_2); +#elif defined(GD32F10X_CL) + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF | + RCU_CFG0_USBFSPSC | RCU_CFG0_CKOUT0SEL | RCU_CFG0_ADCPSC_2 | RCU_CFG0_PLLMF_4); +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* reset INT and CFG1 register */ +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + RCU_INT = 0x009f0000U; +#elif defined(GD32F10X_CL) + RCU_INT = 0x00ff0000U; + RCU_CFG1 &= ~(RCU_CFG1_PREDV0 | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PLL2MF | + RCU_CFG1_PREDV0SEL | RCU_CFG1_I2S1SEL | RCU_CFG1_I2S2SEL); +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ +} + +/*! + \brief enable the peripherals clock + \param[in] periph: RCU peripherals, refer to rcu_periph_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOx (x=A,B,C,D,E,F,G): GPIO ports clock + \arg RCU_AF : alternate function clock + \arg RCU_CRC: CRC clock + \arg RCU_DMAx (x=0,1): DMA clock + \arg RCU_ENET: ENET clock(CL series available) + \arg RCU_ENETTX: ENETTX clock(CL series available) + \arg RCU_ENETRX: ENETRX clock(CL series available) + \arg RCU_USBD: USBD clock(HD,XD series available) + \arg RCU_USBFS: USBFS clock(CL series available) + \arg RCU_EXMC: EXMC clock + \arg RCU_TIMERx (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD series): TIMER clock + \arg RCU_WWDGT: WWDGT clock + \arg RCU_SPIx (x=0,1,2): SPI clock + \arg RCU_USARTx (x=0,1,2): USART clock + \arg RCU_UARTx (x=3,4): UART clock + \arg RCU_I2Cx (x=0,1): I2C clock + \arg RCU_CANx (x=0,1,CAN1 is only available for CL series): CAN clock + \arg RCU_PMU: PMU clock + \arg RCU_DAC: DAC clock + \arg RCU_RTC: RTC clock + \arg RCU_ADCx (x=0,1,2,ADC2 is not available for CL series): ADC clock + \arg RCU_SDIO: SDIO clock(not available for CL series) + \arg RCU_BKPI: BKP interface clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_enable(rcu_periph_enum periph) +{ + RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief disable the peripherals clock + \param[in] periph: RCU peripherals, refer to rcu_periph_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOx (x=A,B,C,D,E,F,G): GPIO ports clock + \arg RCU_AF: alternate function clock + \arg RCU_CRC: CRC clock + \arg RCU_DMAx (x=0,1): DMA clock + \arg RCU_ENET: ENET clock(CL series available) + \arg RCU_ENETTX: ENETTX clock(CL series available) + \arg RCU_ENETRX: ENETRX clock(CL series available) + \arg RCU_USBD: USBD clock(HD,XD series available) + \arg RCU_USBFS: USBFS clock(CL series available) + \arg RCU_EXMC: EXMC clock + \arg RCU_TIMERx (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD series): TIMER clock + \arg RCU_WWDGT: WWDGT clock + \arg RCU_SPIx (x=0,1,2): SPI clock + \arg RCU_USARTx (x=0,1,2): USART clock + \arg RCU_UARTx (x=3,4): UART clock + \arg RCU_I2Cx (x=0,1): I2C clock + \arg RCU_CANx (x=0,1,CAN1 is only available for CL series): CAN clock + \arg RCU_PMU: PMU clock + \arg RCU_DAC: DAC clock + \arg RCU_RTC: RTC clock + \arg RCU_ADCx (x=0,1,2,ADC2 is not available for CL series): ADC clock + \arg RCU_SDIO: SDIO clock(not available for CL series) + \arg RCU_BKPI: BKP interface clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_disable(rcu_periph_enum periph) +{ + RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief enable the peripherals clock when sleep mode + \param[in] periph: RCU peripherals, refer to rcu_periph_sleep_enum + only one parameter can be selected which is shown as below: + \arg RCU_FMC_SLP: FMC clock + \arg RCU_SRAM_SLP: SRAM clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_sleep_enable(rcu_periph_sleep_enum periph) +{ + RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief disable the peripherals clock when sleep mode + \param[in] periph: RCU peripherals, refer to rcu_periph_sleep_enum + only one parameter can be selected which is shown as below: + \arg RCU_FMC_SLP: FMC clock + \arg RCU_SRAM_SLP: SRAM clock + \param[out] none + \retval none +*/ +void rcu_periph_clock_sleep_disable(rcu_periph_sleep_enum periph) +{ + RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph)); +} + +/*! + \brief reset the peripherals + \param[in] periph_reset: RCU peripherals reset, refer to rcu_periph_reset_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOxRST (x=A,B,C,D,E,F,G): reset GPIO ports + \arg RCU_AFRST : reset alternate function clock + \arg RCU_ENETRST: reset ENET(CL series available) + \arg RCU_USBDRST: reset USBD(HD,XD series available) + \arg RCU_USBFSRST: reset USBFS(CL series available) + \arg RCU_TIMERxRST (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD series): reset TIMER + \arg RCU_WWDGTRST: reset WWDGT + \arg RCU_SPIxRST (x=0,1,2): reset SPI + \arg RCU_USARTxRST (x=0,1,2): reset USART + \arg RCU_UARTxRST (x=3,4): reset UART + \arg RCU_I2CxRST (x=0,1): reset I2C + \arg RCU_CANxRST (x=0,1,CAN1 is only available for CL series): reset CAN + \arg RCU_PMURST: reset PMU + \arg RCU_DACRST: reset DAC + \arg RCU_ADCxRST (x=0,1,2, ADC2 is not available for CL series): reset ADC + \arg RCU_BKPIRST: reset BKPI + \param[out] none + \retval none +*/ +void rcu_periph_reset_enable(rcu_periph_reset_enum periph_reset) +{ + RCU_REG_VAL(periph_reset) |= BIT(RCU_BIT_POS(periph_reset)); +} + +/*! + \brief disable reset the peripheral + \param[in] periph_reset: RCU peripherals reset, refer to rcu_periph_reset_enum + only one parameter can be selected which is shown as below: + \arg RCU_GPIOxRST (x=A,B,C,D,E,F,G): reset GPIO ports + \arg RCU_AFRST : reset alternate function clock + \arg RCU_ENETRST: reset ENET(CL series available) + \arg RCU_USBDRST: reset USBD(HD,XD series available) + \arg RCU_USBFSRST: reset USBFS(CL series available) + \arg RCU_TIMERxRST (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are only available for XD series): reset TIMER + \arg RCU_WWDGTRST: reset WWDGT + \arg RCU_SPIxRST (x=0,1,2): reset SPI + \arg RCU_USARTxRST (x=0,1,2): reset USART + \arg RCU_UARTxRST (x=3,4): reset UART + \arg RCU_I2CxRST (x=0,1): reset I2C + \arg RCU_CANxRST (x=0,1,CAN1 is only available for CL series): reset CAN + \arg RCU_PMURST: reset PMU + \arg RCU_DACRST: reset DAC + \arg RCU_ADCxRST (x=0,1,2, ADC2 is not available for CL series): reset ADC + \arg RCU_BKPIRST: reset BKPI + \param[out] none + \retval none +*/ +void rcu_periph_reset_disable(rcu_periph_reset_enum periph_reset) +{ + RCU_REG_VAL(periph_reset) &= ~BIT(RCU_BIT_POS(periph_reset)); +} + +/*! + \brief reset the BKP domain + \param[in] none + \param[out] none + \retval none +*/ +void rcu_bkp_reset_enable(void) +{ + RCU_BDCTL |= RCU_BDCTL_BKPRST; +} + +/*! + \brief disable the BKP domain reset + \param[in] none + \param[out] none + \retval none +*/ +void rcu_bkp_reset_disable(void) +{ + RCU_BDCTL &= ~RCU_BDCTL_BKPRST; +} + +/*! + \brief configure the system clock source + \param[in] ck_sys: system clock source select + only one parameter can be selected which is shown as below: + \arg RCU_CKSYSSRC_IRC8M: select CK_IRC8M as the CK_SYS source + \arg RCU_CKSYSSRC_HXTAL: select CK_HXTAL as the CK_SYS source + \arg RCU_CKSYSSRC_PLL: select CK_PLL as the CK_SYS source + \param[out] none + \retval none +*/ +void rcu_system_clock_source_config(uint32_t ck_sys) +{ + uint32_t reg; + + reg = RCU_CFG0; + /* reset the SCS bits and set according to ck_sys */ + reg &= ~RCU_CFG0_SCS; + RCU_CFG0 = (reg | ck_sys); +} + +/*! + \brief get the system clock source + \param[in] none + \param[out] none + \retval which clock is selected as CK_SYS source + \arg RCU_SCSS_IRC8M: CK_IRC8M is selected as the CK_SYS source + \arg RCU_SCSS_HXTAL: CK_HXTAL is selected as the CK_SYS source + \arg RCU_SCSS_PLL: CK_PLL is selected as the CK_SYS source +*/ +uint32_t rcu_system_clock_source_get(void) +{ + return (RCU_CFG0 & RCU_CFG0_SCSS); +} + +/*! + \brief configure the AHB clock prescaler selection + \param[in] ck_ahb: AHB clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_AHB_CKSYS_DIVx, x=1, 2, 4, 8, 16, 64, 128, 256, 512 + \param[out] none + \retval none +*/ +void rcu_ahb_clock_config(uint32_t ck_ahb) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the AHBPSC bits and set according to ck_ahb */ + reg &= ~RCU_CFG0_AHBPSC; + RCU_CFG0 = (reg | ck_ahb); +} + +/*! + \brief configure the APB1 clock prescaler selection + \param[in] ck_apb1: APB1 clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_APB1_CKAHB_DIV1: select CK_AHB as CK_APB1 + \arg RCU_APB1_CKAHB_DIV2: select CK_AHB/2 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV4: select CK_AHB/4 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV8: select CK_AHB/8 as CK_APB1 + \arg RCU_APB1_CKAHB_DIV16: select CK_AHB/16 as CK_APB1 + \param[out] none + \retval none +*/ +void rcu_apb1_clock_config(uint32_t ck_apb1) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the APB1PSC and set according to ck_apb1 */ + reg &= ~RCU_CFG0_APB1PSC; + RCU_CFG0 = (reg | ck_apb1); +} + +/*! + \brief configure the APB2 clock prescaler selection + \param[in] ck_apb2: APB2 clock prescaler selection + only one parameter can be selected which is shown as below: + \arg RCU_APB2_CKAHB_DIV1: select CK_AHB as CK_APB2 + \arg RCU_APB2_CKAHB_DIV2: select CK_AHB/2 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV4: select CK_AHB/4 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV8: select CK_AHB/8 as CK_APB2 + \arg RCU_APB2_CKAHB_DIV16: select CK_AHB/16 as CK_APB2 + \param[out] none + \retval none +*/ +void rcu_apb2_clock_config(uint32_t ck_apb2) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the APB2PSC and set according to ck_apb2 */ + reg &= ~RCU_CFG0_APB2PSC; + RCU_CFG0 = (reg | ck_apb2); +} + +/*! + \brief configure the CK_OUT0 clock source + \param[in] ckout0_src: CK_OUT0 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_CKOUT0SRC_NONE: no clock selected + \arg RCU_CKOUT0SRC_CKSYS: system clock selected + \arg RCU_CKOUT0SRC_IRC8M: high speed 8M internal oscillator clock selected + \arg RCU_CKOUT0SRC_HXTAL: HXTAL selected + \arg RCU_CKOUT0SRC_CKPLL_DIV2: CK_PLL/2 selected + \arg RCU_CKOUT0SRC_CKPLL1: CK_PLL1 selected + \arg RCU_CKOUT0SRC_CKPLL2_DIV2: CK_PLL2/2 selected + \arg RCU_CKOUT0SRC_EXT1: EXT1 selected + \arg RCU_CKOUT0SRC_CKPLL2: PLL2 selected + \param[out] none + \retval none +*/ +void rcu_ckout0_config(uint32_t ckout0_src) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* reset the CKOUT0SRC, set according to ckout0_src */ + reg &= ~RCU_CFG0_CKOUT0SEL; + RCU_CFG0 = (reg | ckout0_src); +} + +/*! + \brief configure the main PLL clock + \param[in] pll_src: PLL clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PLLSRC_IRC8M_DIV2: IRC8M/2 clock selected as source clock of PLL + \arg RCU_PLLSRC_HXTAL: HXTAL selected as source clock of PLL + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL_MULx (XD series x = 2..32, CL series x = 2..14, 6.5, 16..32) + \param[out] none + \retval none +*/ +void rcu_pll_config(uint32_t pll_src, uint32_t pll_mul) +{ + uint32_t reg = 0U; + + reg = RCU_CFG0; + + /* PLL clock source and multiplication factor configuration */ + reg &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + reg |= (pll_src | pll_mul); + + RCU_CFG0 = reg; +} + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) +/*! + \brief configure the PREDV0 division factor + \param[in] predv0_div: PREDV0 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0_DIVx, x = 1,2 + \param[out] none + \retval none +*/ +void rcu_predv0_config(uint32_t predv0_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG0; + /* reset PREDV0 bit */ + reg &= ~RCU_CFG0_PREDV0; + if(RCU_PREDV0_DIV2 == predv0_div){ + /* set the PREDV0 bit */ + reg |= RCU_CFG0_PREDV0; + } + + RCU_CFG0 = reg; +} +#elif defined(GD32F10X_CL) +/*! + \brief configure the PREDV0 division factor and clock source + \param[in] predv0_source: PREDV0 input clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0SRC_HXTAL: HXTAL selected as PREDV0 input source clock + \arg RCU_PREDV0SRC_CKPLL1: CK_PLL1 selected as PREDV0 input source clock + \param[in] predv0_div: PREDV0 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV0_DIVx, x = 1..16 + \param[out] none + \retval none +*/ +void rcu_predv0_config(uint32_t predv0_source, uint32_t predv0_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + /* reset PREDV0SEL and PREDV0 bits */ + reg &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV0); + /* set the PREDV0SEL and PREDV0 division factor */ + reg |= (predv0_source | predv0_div); + + RCU_CFG1 = reg; +} + +/*! + \brief configure the PREDV1 division factor + \param[in] predv1_div: PREDV1 division factor + only one parameter can be selected which is shown as below: + \arg RCU_PREDV1_DIVx, x = 1..16 + \param[out] none + \retval none +*/ +void rcu_predv1_config(uint32_t predv1_div) +{ + uint32_t reg = 0U; + + reg = RCU_CFG1; + /* reset the PREDV1 bits */ + reg &= ~RCU_CFG1_PREDV1; + /* set the PREDV1 division factor */ + reg |= predv1_div; + + RCU_CFG1 = reg; +} + +/*! + \brief configure the PLL1 clock + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL1_MULx (x = 8..16, 20) + \param[out] none + \retval none +*/ +void rcu_pll1_config(uint32_t pll_mul) +{ + RCU_CFG1 &= ~RCU_CFG1_PLL1MF; + RCU_CFG1 |= pll_mul; +} + +/*! + \brief configure the PLL2 clock + \param[in] pll_mul: PLL clock multiplication factor + only one parameter can be selected which is shown as below: + \arg RCU_PLL2_MULx (x = 8..16, 20) + \param[out] none + \retval none +*/ +void rcu_pll2_config(uint32_t pll_mul) +{ + RCU_CFG1 &= ~RCU_CFG1_PLL2MF; + RCU_CFG1 |= pll_mul; +} +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + +/*! + \brief configure the ADC prescaler factor + \param[in] adc_psc: ADC prescaler factor + only one parameter can be selected which is shown as below: + \arg RCU_CKADC_CKAPB2_DIV2: ADC prescaler select CK_APB2/2 + \arg RCU_CKADC_CKAPB2_DIV4: ADC prescaler select CK_APB2/4 + \arg RCU_CKADC_CKAPB2_DIV6: ADC prescaler select CK_APB2/6 + \arg RCU_CKADC_CKAPB2_DIV8: ADC prescaler select CK_APB2/8 + \arg RCU_CKADC_CKAPB2_DIV12: ADC prescaler select CK_APB2/12 + \arg RCU_CKADC_CKAPB2_DIV16: ADC prescaler select CK_APB2/16 + \param[out] none + \retval none +*/ +void rcu_adc_clock_config(uint32_t adc_psc) +{ + uint32_t reg0; + + /* reset the ADCPSC bits */ + reg0 = RCU_CFG0; + reg0 &= ~(RCU_CFG0_ADCPSC_2 | RCU_CFG0_ADCPSC); + + /* set the ADC prescaler factor */ + switch(adc_psc){ + case RCU_CKADC_CKAPB2_DIV2: + case RCU_CKADC_CKAPB2_DIV4: + case RCU_CKADC_CKAPB2_DIV6: + case RCU_CKADC_CKAPB2_DIV8: + reg0 |= (adc_psc << 14); + break; + + case RCU_CKADC_CKAPB2_DIV12: + case RCU_CKADC_CKAPB2_DIV16: + adc_psc &= ~BIT(2); + reg0 |= (adc_psc << 14 | RCU_CFG0_ADCPSC_2); + break; + + default: + break; + } + + /* set the register */ + RCU_CFG0 = reg0; +} + +/*! + \brief configure the USBD/USBFS prescaler factor + \param[in] usb_psc: USB prescaler factor + only one parameter can be selected which is shown as below: + \arg RCU_CKUSB_CKPLL_DIV1_5: USBD/USBFS prescaler select CK_PLL/1.5 + \arg RCU_CKUSB_CKPLL_DIV1: USBD/USBFS prescaler select CK_PLL/1 + \arg RCU_CKUSB_CKPLL_DIV2_5: USBD/USBFS prescaler select CK_PLL/2.5 + \arg RCU_CKUSB_CKPLL_DIV2: USBD/USBFS prescaler select CK_PLL/2 + \param[out] none + \retval none +*/ +void rcu_usb_clock_config(uint32_t usb_psc) +{ + uint32_t reg; + + reg = RCU_CFG0; + + /* configure the USBD/USBFS prescaler factor */ +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + reg &= ~RCU_CFG0_USBDPSC; +#elif defined(GD32F10X_CL) + reg &= ~RCU_CFG0_USBFSPSC; +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + RCU_CFG0 = (reg | usb_psc); +} + +/*! + \brief configure the RTC clock source selection + \param[in] rtc_clock_source: RTC clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_RTCSRC_NONE: no clock selected + \arg RCU_RTCSRC_LXTAL: CK_LXTAL selected as RTC source clock + \arg RCU_RTCSRC_IRC40K: CK_IRC40K selected as RTC source clock + \arg RCU_RTCSRC_HXTAL_DIV_128: CK_HXTAL/128 selected as RTC source clock + \param[out] none + \retval none +*/ +void rcu_rtc_clock_config(uint32_t rtc_clock_source) +{ + uint32_t reg; + + reg = RCU_BDCTL; + /* reset the RTCSRC bits and set according to rtc_clock_source */ + reg &= ~RCU_BDCTL_RTCSRC; + RCU_BDCTL = (reg | rtc_clock_source); +} + +#ifdef GD32F10X_CL +/*! + \brief configure the I2S1 clock source selection + \param[in] i2s_clock_source: I2S1 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2S1SRC_CKSYS: System clock selected as I2S1 source clock + \arg RCU_I2S1SRC_CKPLL2_MUL2: CK_PLL2x2 selected as I2S1 source clock + \param[out] none + \retval none +*/ +void rcu_i2s1_clock_config(uint32_t i2s_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S1SEL bit and set according to i2s_clock_source */ + reg &= ~RCU_CFG1_I2S1SEL; + RCU_CFG1 = (reg | i2s_clock_source); +} + +/*! + \brief configure the I2S2 clock source selection + \param[in] i2s_clock_source: I2S2 clock source selection + only one parameter can be selected which is shown as below: + \arg RCU_I2S2SRC_CKSYS: system clock selected as I2S2 source clock + \arg RCU_I2S2SRC_CKPLL2_MUL2: CK_PLL2x2 selected as I2S2 source clock + \param[out] none + \retval none +*/ +void rcu_i2s2_clock_config(uint32_t i2s_clock_source) +{ + uint32_t reg; + + reg = RCU_CFG1; + /* reset the I2S2SEL bit and set according to i2s_clock_source */ + reg &= ~RCU_CFG1_I2S2SEL; + RCU_CFG1 = (reg | i2s_clock_source); +} +#endif /* GD32F10X_CL */ + +/*! + \brief get the clock stabilization and periphral reset flags + \param[in] flag: the clock stabilization and periphral reset flags, refer to rcu_flag_enum + only one parameter can be selected which is shown as below: + \arg RCU_FLAG_IRC8MSTB: IRC8M stabilization flag + \arg RCU_FLAG_HXTALSTB: HXTAL stabilization flag + \arg RCU_FLAG_PLLSTB: PLL stabilization flag + \arg RCU_FLAG_PLL1STB: PLL1 stabilization flag(CL series only) + \arg RCU_FLAG_PLL2STB: PLL2 stabilization flag(CL series only) + \arg RCU_FLAG_LXTALSTB: LXTAL stabilization flag + \arg RCU_FLAG_IRC40KSTB: IRC40K stabilization flag + \arg RCU_FLAG_EPRST: external PIN reset flag + \arg RCU_FLAG_PORRST: power reset flag + \arg RCU_FLAG_SWRST: software reset flag + \arg RCU_FLAG_FWDGTRST: free watchdog timer reset flag + \arg RCU_FLAG_WWDGTRST: window watchdog timer reset flag + \arg RCU_FLAG_LPRST: low-power reset flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus rcu_flag_get(rcu_flag_enum flag) +{ + /* get the rcu flag */ + if(RESET != (RCU_REG_VAL(flag) & BIT(RCU_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear all the reset flag + \param[in] none + \param[out] none + \retval none +*/ +void rcu_all_reset_flag_clear(void) +{ + RCU_RSTSCK |= RCU_RSTSCK_RSTFC; +} + +/*! + \brief get the clock stabilization interrupt and ckm flags + \param[in] int_flag: interrupt and ckm flags, refer to rcu_int_flag_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_FLAG_IRC40KSTB: IRC40K stabilization interrupt flag + \arg RCU_INT_FLAG_LXTALSTB: LXTAL stabilization interrupt flag + \arg RCU_INT_FLAG_IRC8MSTB: IRC8M stabilization interrupt flag + \arg RCU_INT_FLAG_HXTALSTB: HXTAL stabilization interrupt flag + \arg RCU_INT_FLAG_PLLSTB: PLL stabilization interrupt flag + \arg RCU_INT_FLAG_PLL1STB: PLL1 stabilization interrupt flag(CL series only) + \arg RCU_INT_FLAG_PLL2STB: PLL2 stabilization interrupt flag(CL series only) + \arg RCU_INT_FLAG_CKM: HXTAL clock stuck interrupt flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus rcu_interrupt_flag_get(rcu_int_flag_enum int_flag) +{ + /* get the rcu interrupt flag */ + if(RESET != (RCU_REG_VAL(int_flag) & BIT(RCU_BIT_POS(int_flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear the interrupt flags + \param[in] int_flag_clear: clock stabilization and stuck interrupt flags clear, refer to rcu_int_flag_clear_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_FLAG_IRC40KSTB_CLR: IRC40K stabilization interrupt flag clear + \arg RCU_INT_FLAG_LXTALSTB_CLR: LXTAL stabilization interrupt flag clear + \arg RCU_INT_FLAG_IRC8MSTB_CLR: IRC8M stabilization interrupt flag clear + \arg RCU_INT_FLAG_HXTALSTB_CLR: HXTAL stabilization interrupt flag clear + \arg RCU_INT_FLAG_PLLSTB_CLR: PLL stabilization interrupt flag clear + \arg RCU_INT_FLAG_PLL1STB_CLR: PLL1 stabilization interrupt flag clear(CL series only) + \arg RCU_INT_FLAG_PLL2STB_CLR: PLL2 stabilization interrupt flag clear(CL series only) + \arg RCU_INT_FLAG_CKM_CLR: clock stuck interrupt flag clear + \param[out] none + \retval none +*/ +void rcu_interrupt_flag_clear(rcu_int_flag_clear_enum int_flag_clear) +{ + RCU_REG_VAL(int_flag_clear) |= BIT(RCU_BIT_POS(int_flag_clear)); +} + +/*! + \brief enable the stabilization interrupt + \param[in] stab_int: clock stabilization interrupt, refer to rcu_int_enum + Only one parameter can be selected which is shown as below: + \arg RCU_INT_IRC40KSTB: IRC40K stabilization interrupt enable + \arg RCU_INT_LXTALSTB: LXTAL stabilization interrupt enable + \arg RCU_INT_IRC8MSTB: IRC8M stabilization interrupt enable + \arg RCU_INT_HXTALSTB: HXTAL stabilization interrupt enable + \arg RCU_INT_PLLSTB: PLL stabilization interrupt enable + \arg RCU_INT_PLL1STB: PLL1 stabilization interrupt enable(CL series only) + \arg RCU_INT_PLL2STB: PLL2 stabilization interrupt enable(CL series only) + \param[out] none + \retval none +*/ +void rcu_interrupt_enable(rcu_int_enum stab_int) +{ + RCU_REG_VAL(stab_int) |= BIT(RCU_BIT_POS(stab_int)); +} + +/*! + \brief disable the stabilization interrupt + \param[in] stab_int: clock stabilization interrupt, refer to rcu_int_enum + only one parameter can be selected which is shown as below: + \arg RCU_INT_IRC40KSTB: IRC40K stabilization interrupt enable + \arg RCU_INT_LXTALSTB: LXTAL stabilization interrupt enable + \arg RCU_INT_IRC8MSTB: IRC8M stabilization interrupt enable + \arg RCU_INT_HXTALSTB: HXTAL stabilization interrupt enable + \arg RCU_INT_PLLSTB: PLL stabilization interrupt enable + \arg RCU_INT_PLL1STB: PLL1 stabilization interrupt enable(CL series only) + \arg RCU_INT_PLL2STB: PLL2 stabilization interrupt enable(CL series only) + \param[out] none + \retval none +*/ +void rcu_interrupt_disable(rcu_int_enum stab_int) +{ + RCU_REG_VAL(stab_int) &= ~BIT(RCU_BIT_POS(stab_int)); +} + +/*! + \brief wait for oscillator stabilization flags is SET or oscillator startup is timeout + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL series only) + \arg RCU_PLL2_CK: phase locked loop 2(CL series only) + \param[out] none + \retval ErrStatus: SUCCESS or ERROR +*/ +ErrStatus rcu_osci_stab_wait(rcu_osci_type_enum osci) +{ + uint32_t stb_cnt = 0U; + ErrStatus reval = ERROR; + FlagStatus osci_stat = RESET; + + switch(osci){ + /* wait HXTAL stable */ + case RCU_HXTAL: + while((RESET == osci_stat) && (HXTAL_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_HXTALSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_HXTALSTB)){ + reval = SUCCESS; + } + break; + + /* wait LXTAL stable */ + case RCU_LXTAL: + while((RESET == osci_stat) && (LXTAL_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_LXTALSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_LXTALSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC8M stable */ + case RCU_IRC8M: + while((RESET == osci_stat) && (IRC8M_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC8MSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_IRC8MSTB)){ + reval = SUCCESS; + } + break; + + /* wait IRC40K stable */ + case RCU_IRC40K: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_IRC40KSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_IRC40KSTB)){ + reval = SUCCESS; + } + break; + + /* wait PLL stable */ + case RCU_PLL_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLLSTB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLLSTB)){ + reval = SUCCESS; + } + break; + +#ifdef GD32F10X_CL + /* wait PLL1 stable */ + case RCU_PLL1_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLL1STB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLL1STB)){ + reval = SUCCESS; + } + break; + /* wait PLL2 stable */ + case RCU_PLL2_CK: + while((RESET == osci_stat) && (OSC_STARTUP_TIMEOUT != stb_cnt)){ + osci_stat = rcu_flag_get(RCU_FLAG_PLL2STB); + stb_cnt++; + } + + /* check whether flag is set or not */ + if(RESET != rcu_flag_get(RCU_FLAG_PLL2STB)){ + reval = SUCCESS; + } + break; +#endif /* GD32F10X_CL */ + + default: + break; + } + + /* return value */ + return reval; +} + +/*! + \brief turn on the oscillator + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL series only) + \arg RCU_PLL2_CK: phase locked loop 2(CL series only) + \param[out] none + \retval none +*/ +void rcu_osci_on(rcu_osci_type_enum osci) +{ + RCU_REG_VAL(osci) |= BIT(RCU_BIT_POS(osci)); +} + +/*! + \brief turn off the oscillator + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \arg RCU_IRC8M: internal 8M RC oscillators(IRC8M) + \arg RCU_IRC40K: internal 40K RC oscillator(IRC40K) + \arg RCU_PLL_CK: phase locked loop(PLL) + \arg RCU_PLL1_CK: phase locked loop 1(CL series only) + \arg RCU_PLL2_CK: phase locked loop 2(CL series only) + \param[out] none + \retval none +*/ +void rcu_osci_off(rcu_osci_type_enum osci) +{ + RCU_REG_VAL(osci) &= ~BIT(RCU_BIT_POS(osci)); +} + +/*! + \brief enable the oscillator bypass mode, HXTALEN or LXTALEN must be reset before it + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \param[out] none + \retval none +*/ +void rcu_osci_bypass_mode_enable(rcu_osci_type_enum osci) +{ + uint32_t reg; + + switch(osci){ + /* enable HXTAL to bypass mode */ + case RCU_HXTAL: + reg = RCU_CTL; + RCU_CTL &= ~RCU_CTL_HXTALEN; + RCU_CTL = (reg | RCU_CTL_HXTALBPS); + break; + /* enable LXTAL to bypass mode */ + case RCU_LXTAL: + reg = RCU_BDCTL; + RCU_BDCTL &= ~RCU_BDCTL_LXTALEN; + RCU_BDCTL = (reg | RCU_BDCTL_LXTALBPS); + break; + case RCU_IRC8M: + case RCU_IRC40K: + case RCU_PLL_CK: +#ifdef GD32F10X_CL + case RCU_PLL1_CK: + case RCU_PLL2_CK: +#endif /* GD32F10X_CL */ + break; + default: + break; + } +} + +/*! + \brief disable the oscillator bypass mode, HXTALEN or LXTALEN must be reset before it + \param[in] osci: oscillator types, refer to rcu_osci_type_enum + only one parameter can be selected which is shown as below: + \arg RCU_HXTAL: high speed crystal oscillator(HXTAL) + \arg RCU_LXTAL: low speed crystal oscillator(LXTAL) + \param[out] none + \retval none +*/ +void rcu_osci_bypass_mode_disable(rcu_osci_type_enum osci) +{ + uint32_t reg; + + switch(osci){ + /* disable HXTAL to bypass mode */ + case RCU_HXTAL: + reg = RCU_CTL; + RCU_CTL &= ~RCU_CTL_HXTALEN; + RCU_CTL = (reg & ~RCU_CTL_HXTALBPS); + break; + /* disable LXTAL to bypass mode */ + case RCU_LXTAL: + reg = RCU_BDCTL; + RCU_BDCTL &= ~RCU_BDCTL_LXTALEN; + RCU_BDCTL = (reg & ~RCU_BDCTL_LXTALBPS); + break; + case RCU_IRC8M: + case RCU_IRC40K: + case RCU_PLL_CK: +#ifdef GD32F10X_CL + case RCU_PLL1_CK: + case RCU_PLL2_CK: +#endif /* GD32F10X_CL */ + break; + default: + break; + } +} + +/*! + \brief enable the HXTAL clock monitor + \param[in] none + \param[out] none + \retval none +*/ + +void rcu_hxtal_clock_monitor_enable(void) +{ + RCU_CTL |= RCU_CTL_CKMEN; +} + +/*! + \brief disable the HXTAL clock monitor + \param[in] none + \param[out] none + \retval none +*/ +void rcu_hxtal_clock_monitor_disable(void) +{ + RCU_CTL &= ~RCU_CTL_CKMEN; +} + +/*! + \brief set the IRC8M adjust value + \param[in] irc8m_adjval: IRC8M adjust value, must be between 0 and 0x1F + \param[out] none + \retval none +*/ +void rcu_irc8m_adjust_value_set(uint8_t irc8m_adjval) +{ + uint32_t reg; + + reg = RCU_CTL; + /* reset the IRC8MADJ bits and set according to irc8m_adjval */ + reg &= ~RCU_CTL_IRC8MADJ; + RCU_CTL = (reg | ((irc8m_adjval & 0x1FU) << 3)); +} + +/*! + \brief deep-sleep mode voltage select + \param[in] dsvol: deep sleep mode voltage + only one parameter can be selected which is shown as below: + \arg RCU_DEEPSLEEP_V_1_2: the core voltage is 1.2V + \arg RCU_DEEPSLEEP_V_1_1: the core voltage is 1.1V + \arg RCU_DEEPSLEEP_V_1_0: the core voltage is 1.0V + \arg RCU_DEEPSLEEP_V_0_9: the core voltage is 0.9V + \param[out] none + \retval none +*/ +void rcu_deepsleep_voltage_set(uint32_t dsvol) +{ + dsvol &= RCU_DSV_DSLPVS; + RCU_DSV = dsvol; +} + +/*! + \brief get the system clock, bus and peripheral clock frequency + \param[in] clock: the clock frequency which to get + only one parameter can be selected which is shown as below: + \arg CK_SYS: system clock frequency + \arg CK_AHB: AHB clock frequency + \arg CK_APB1: APB1 clock frequency + \arg CK_APB2: APB2 clock frequency + \param[out] none + \retval clock frequency of system, AHB, APB1, APB2 +*/ +uint32_t rcu_clock_freq_get(rcu_clock_freq_enum clock) +{ + uint32_t sws, ck_freq = 0U; + uint32_t cksys_freq, ahb_freq, apb1_freq, apb2_freq; + uint32_t pllsel, predv0sel, pllmf,ck_src, idx, clk_exp; +#ifdef GD32F10X_CL + uint32_t predv0, predv1, pll1mf; +#endif /* GD32F10X_CL */ + + /* exponent of AHB, APB1 and APB2 clock divider */ + uint8_t ahb_exp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + uint8_t apb1_exp[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + uint8_t apb2_exp[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + + sws = GET_BITS(RCU_CFG0, 2, 3); + switch(sws){ + /* IRC8M is selected as CK_SYS */ + case SEL_IRC8M: + cksys_freq = IRC8M_VALUE; + break; + /* HXTAL is selected as CK_SYS */ + case SEL_HXTAL: + cksys_freq = HXTAL_VALUE; + break; + /* PLL is selected as CK_SYS */ + case SEL_PLL: + /* PLL clock source selection, HXTAL or IRC8M/2 */ + pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL); + + if(RCU_PLLSRC_HXTAL == pllsel) { + /* PLL clock source is HXTAL */ + ck_src = HXTAL_VALUE; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + predv0sel = (RCU_CFG0 & RCU_CFG0_PREDV0); + /* PREDV0 input source clock divided by 2 */ + if(RCU_CFG0_PREDV0 == predv0sel){ + ck_src = HXTAL_VALUE/2U; + } +#elif defined(GD32F10X_CL) + predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL); + /* source clock use PLL1 */ + if(RCU_PREDV0SRC_CKPLL1 == predv0sel){ + predv1 = (uint32_t)((RCU_CFG1 & RCU_CFG1_PREDV1) >> 4) + 1U; + pll1mf = (uint32_t)((RCU_CFG1 & RCU_CFG1_PLL1MF) >> 8) + 2U; + if(17U == pll1mf){ + pll1mf = 20U; + } + ck_src = (ck_src / predv1) * pll1mf; + } + predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U; + ck_src /= predv0; +#endif /* GD32F10X_HD and GD32F10X_XD */ + }else{ + /* PLL clock source is IRC8M/2 */ + ck_src = IRC8M_VALUE/2U; + } + + /* PLL multiplication factor */ + pllmf = GET_BITS(RCU_CFG0, 18, 21); + if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){ + pllmf |= 0x10U; + } + if(pllmf < 15U){ + pllmf += 2U; + }else{ + pllmf += 1U; + } + + cksys_freq = ck_src * pllmf; + + #ifdef GD32F10X_CL + if(15U == pllmf){ + /* PLL source clock multiply by 6.5 */ + cksys_freq = ck_src * 6U + ck_src / 2U; + } + #endif /* GD32F10X_CL */ + + break; + /* IRC8M is selected as CK_SYS */ + default: + cksys_freq = IRC8M_VALUE; + break; + } + + /* calculate AHB clock frequency */ + idx = GET_BITS(RCU_CFG0, 4, 7); + clk_exp = ahb_exp[idx]; + ahb_freq = cksys_freq >> clk_exp; + + /* calculate APB1 clock frequency */ + idx = GET_BITS(RCU_CFG0, 8, 10); + clk_exp = apb1_exp[idx]; + apb1_freq = ahb_freq >> clk_exp; + + /* calculate APB2 clock frequency */ + idx = GET_BITS(RCU_CFG0, 11, 13); + clk_exp = apb2_exp[idx]; + apb2_freq = ahb_freq >> clk_exp; + + /* return the clocks frequency */ + switch(clock){ + case CK_SYS: + ck_freq = cksys_freq; + break; + case CK_AHB: + ck_freq = ahb_freq; + break; + case CK_APB1: + ck_freq = apb1_freq; + break; + case CK_APB2: + ck_freq = apb2_freq; + break; + default: + break; + } + return ck_freq; +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_usart.c b/RTE/Device/GD32F107VC/gd32f10x_usart.c new file mode 100644 index 0000000..78530e8 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_usart.c @@ -0,0 +1,766 @@ +/*! + \file gd32f10x_usart.c + \brief USART driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.1, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_usart.h" + +/*! + \brief reset USART/UART + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_deinit(uint32_t usart_periph) +{ + switch(usart_periph){ + case USART0: + /* reset USART0 */ + rcu_periph_reset_enable(RCU_USART0RST); + rcu_periph_reset_disable(RCU_USART0RST); + break; + case USART1: + /* reset USART1 */ + rcu_periph_reset_enable(RCU_USART1RST); + rcu_periph_reset_disable(RCU_USART1RST); + break; + case USART2: + /* reset USART2 */ + rcu_periph_reset_enable(RCU_USART2RST); + rcu_periph_reset_disable(RCU_USART2RST); + break; + case UART3: + /* reset UART3 */ + rcu_periph_reset_enable(RCU_UART3RST); + rcu_periph_reset_disable(RCU_UART3RST); + break; + case UART4: + /* reset UART4 */ + rcu_periph_reset_enable(RCU_UART4RST); + rcu_periph_reset_disable(RCU_UART4RST); + break; + default: + break; + } +} + +/*! + \brief configure USART baud rate value + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] baudval: baud rate value + \param[out] none + \retval none +*/ +void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval) +{ + uint32_t uclk=0U, intdiv=0U, fradiv=0U, udiv=0U; + switch(usart_periph){ + /* get clock frequency */ + case USART0: + /* get USART0 clock */ + uclk=rcu_clock_freq_get(CK_APB2); + break; + case USART1: + /* get USART1 clock */ + uclk=rcu_clock_freq_get(CK_APB1); + break; + case USART2: + /* get USART2 clock */ + uclk=rcu_clock_freq_get(CK_APB1); + break; + case UART3: + /* get UART3 clock */ + uclk=rcu_clock_freq_get(CK_APB1); + break; + case UART4: + /* get UART4 clock */ + uclk=rcu_clock_freq_get(CK_APB1); + break; + default: + break; + } + /* oversampling by 16, configure the value of USART_BAUD */ + udiv = (uclk+baudval/2U)/baudval; + intdiv = udiv & (0x0000fff0U); + fradiv = udiv & (0x0000000fU); + USART_BAUD(usart_periph) = ((USART_BAUD_FRADIV | USART_BAUD_INTDIV) & (intdiv | fradiv)); +} + +/*! + \brief configure USART parity + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] paritycfg: configure USART parity + only one parameter can be selected which is shown as below: + \arg USART_PM_NONE: no parity + \arg USART_PM_ODD: odd parity + \arg USART_PM_EVEN: even parity + \param[out] none + \retval none +*/ +void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg) +{ + /* clear USART_CTL0 PM,PCEN bits */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_PM | USART_CTL0_PCEN); + /* configure USART parity mode */ + USART_CTL0(usart_periph) |= paritycfg ; +} + +/*! + \brief configure USART word length + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] wlen: USART word length configure + only one parameter can be selected which is shown as below: + \arg USART_WL_8BIT: 8 bits + \arg USART_WL_9BIT: 9 bits + \param[out] none + \retval none +*/ +void usart_word_length_set(uint32_t usart_periph, uint32_t wlen) +{ + /* clear USART_CTL0 WL bit */ + USART_CTL0(usart_periph) &= ~USART_CTL0_WL; + /* configure USART word length */ + USART_CTL0(usart_periph) |= wlen; +} + +/*! + \brief configure USART stop bit length + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] stblen: USART stop bit configure + only one parameter can be selected which is shown as below: + \arg USART_STB_1BIT: 1 bit + \arg USART_STB_0_5BIT: 0.5 bit, not available for UARTx(x=3,4) + \arg USART_STB_2BIT: 2 bits + \arg USART_STB_1_5BIT: 1.5 bits, not available for UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_stop_bit_set(uint32_t usart_periph, uint32_t stblen) +{ + /* clear USART_CTL1 STB bits */ + USART_CTL1(usart_periph) &= ~USART_CTL1_STB; + /* configure USART stop bits */ + USART_CTL1(usart_periph) |= stblen; +} +/*! + \brief enable USART + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_enable(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) |= USART_CTL0_UEN; +} + +/*! + \brief disable USART + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_disable(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) &= ~(USART_CTL0_UEN); +} + +/*! + \brief configure USART transmitter + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] txconfig: enable or disable USART transmitter + only one parameter can be selected which is shown as below: + \arg USART_TRANSMIT_ENABLE: enable USART transmission + \arg USART_TRANSMIT_DISABLE: disable USART transmission + \param[out] none + \retval none +*/ +void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL0(usart_periph); + ctl &= ~USART_CTL0_TEN; + ctl |= txconfig; + /* configure transfer mode */ + USART_CTL0(usart_periph) = ctl; +} + +/*! + \brief configure USART receiver + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] rxconfig: enable or disable USART receiver + only one parameter can be selected which is shown as below: + \arg USART_RECEIVE_ENABLE: enable USART reception + \arg USART_RECEIVE_DISABLE: disable USART reception + \param[out] none + \retval none +*/ +void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL0(usart_periph); + ctl &= ~USART_CTL0_REN; + ctl |= rxconfig; + /* configure receiver mode */ + USART_CTL0(usart_periph) = ctl; +} + +/*! + \brief USART transmit data function + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] data: data of transmission + \param[out] none + \retval none +*/ +void usart_data_transmit(uint32_t usart_periph, uint16_t data) +{ + USART_DATA(usart_periph) = USART_DATA_DATA & data; +} + +/*! + \brief USART receive data function + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval data of received +*/ +uint16_t usart_data_receive(uint32_t usart_periph) +{ + return (uint16_t)(GET_BITS(USART_DATA(usart_periph), 0U, 8U)); +} + +/*! + \brief configure the address of the USART in wake up by address match mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] addr: address of USART/UART + \param[out] none + \retval none +*/ +void usart_address_config(uint32_t usart_periph, uint8_t addr) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_ADDR); + USART_CTL1(usart_periph) |= (USART_CTL1_ADDR & addr); +} + +/*! + \brief receiver in mute mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_mute_mode_enable(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) |= USART_CTL0_RWU; +} + +/*! + \brief receiver in active mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_mute_mode_disable(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) &= ~(USART_CTL0_RWU); +} + +/*! + \brief configure wakeup method in mute mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] wmethod: two methods be used to enter or exit the mute mode + only one parameter can be selected which is shown as below: + \arg USART_WM_IDLE: idle line + \arg USART_WM_ADDR: address mask + \param[out] none + \retval none +*/ +void usart_mute_mode_wakeup_config(uint32_t usart_periph, uint32_t wmethod) +{ + USART_CTL0(usart_periph) &= ~(USART_CTL0_WM); + USART_CTL0(usart_periph) |= wmethod; +} + +/*! + \brief enable LIN mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_lin_mode_enable(uint32_t usart_periph) +{ + USART_CTL1(usart_periph) |= USART_CTL1_LMEN; +} + +/*! + \brief disable LIN mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_lin_mode_disable(uint32_t usart_periph) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_LMEN); +} + +/*! + \brief configure lin break frame length + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] lblen: lin break frame length + only one parameter can be selected which is shown as below: + \arg USART_LBLEN_10B: 10 bits + \arg USART_LBLEN_11B: 11 bits + \param[out] none + \retval none +*/ +void usart_lin_break_detection_length_config(uint32_t usart_periph, uint32_t lblen) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_LBLEN); + USART_CTL1(usart_periph) |= (USART_CTL1_LBLEN & lblen); +} + +/*! + \brief send break frame + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_send_break(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) |= USART_CTL0_SBKCMD; +} + +/*! + \brief enable half duplex mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_halfduplex_enable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) |= USART_CTL2_HDEN; +} + +/*! + \brief disable half duplex mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_halfduplex_disable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_HDEN); +} + +/*! + \brief enable CK pin in synchronous mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_synchronous_clock_enable(uint32_t usart_periph) +{ + USART_CTL1(usart_periph) |= USART_CTL1_CKEN; +} + +/*! + \brief disable CK pin in synchronous mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_synchronous_clock_disable(uint32_t usart_periph) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_CKEN); +} + +/*! + \brief configure USART synchronous mode parameters + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] clen: CK length + only one parameter can be selected which is shown as below: + \arg USART_CLEN_NONE: there are 7 CK pulses for an 8 bit frame and 8 CK pulses for a 9 bit frame + \arg USART_CLEN_EN: there are 8 CK pulses for an 8 bit frame and 9 CK pulses for a 9 bit frame + \param[in] cph: clock phase + only one parameter can be selected which is shown as below: + \arg USART_CPH_1CK: first clock transition is the first data capture edge + \arg USART_CPH_2CK: second clock transition is the first data capture edge + \param[in] cpl: clock polarity + only one parameter can be selected which is shown as below: + \arg USART_CPL_LOW: steady low value on CK pin + \arg USART_CPL_HIGH: steady high value on CK pin + \param[out] none + \retval none +*/ +void usart_synchronous_clock_config(uint32_t usart_periph, uint32_t clen, uint32_t cph, uint32_t cpl) +{ + uint32_t ctl = 0U; + + /* read USART_CTL1 register */ + ctl = USART_CTL1(usart_periph); + ctl &= ~(USART_CTL1_CLEN | USART_CTL1_CPH | USART_CTL1_CPL); + /* set CK length, CK phase, CK polarity */ + ctl |= (USART_CTL1_CLEN & clen) | (USART_CTL1_CPH & cph) | (USART_CTL1_CPL & cpl); + + USART_CTL1(usart_periph) = ctl; +} + +/*! + \brief configure guard time value in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] gaut: guard time value + \param[out] none + \retval none +*/ +void usart_guard_time_config(uint32_t usart_periph,uint32_t gaut) +{ + USART_GP(usart_periph) &= ~(USART_GP_GUAT); + USART_GP(usart_periph) |= (USART_GP_GUAT & ((gaut)<<8)); +} + +/*! + \brief enable smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_enable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) |= USART_CTL2_SCEN; +} + +/*! + \brief disable smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_disable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_SCEN); +} + +/*! + \brief enable NACK in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_nack_enable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) |= USART_CTL2_NKEN; +} + +/*! + \brief disable NACK in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_nack_disable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_NKEN); +} + +/*! + \brief enable IrDA mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_irda_mode_enable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) |= USART_CTL2_IREN; +} + +/*! + \brief disable IrDA mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_irda_mode_disable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_IREN); +} + +/*! + \brief configure the peripheral clock prescaler in USART IrDA low-power mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] psc: 0x00-0xFF + \param[out] none + \retval none +*/ +void usart_prescaler_config(uint32_t usart_periph, uint8_t psc) +{ + USART_GP(usart_periph) &= ~(USART_GP_PSC); + USART_GP(usart_periph) |= psc; +} + +/*! + \brief configure IrDA low-power + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] irlp: IrDA low-power or normal + only one parameter can be selected which is shown as below: + \arg USART_IRLP_LOW: low-power + \arg USART_IRLP_NORMAL: normal + \param[out] none + \retval none +*/ +void usart_irda_lowpower_config(uint32_t usart_periph, uint32_t irlp) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_IRLP); + USART_CTL2(usart_periph) |= (USART_CTL2_IRLP & irlp); +} + +/*! + \brief configure hardware flow control RTS + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] rtsconfig: enable or disable RTS + only one parameter can be selected which is shown as below: + \arg USART_RTS_ENABLE: enable RTS + \arg USART_RTS_DISABLE: disable RTS + \param[out] none + \retval none +*/ +void usart_hardware_flow_rts_config(uint32_t usart_periph, uint32_t rtsconfig) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_RTSEN; + ctl |= rtsconfig; + /* configure RTS */ + USART_CTL2(usart_periph) = ctl; +} + +/*! + \brief configure hardware flow control CTS + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] ctsconfig: enable or disable CTS + only one parameter can be selected which is shown as below: + \arg USART_CTS_ENABLE: enable CTS + \arg USART_CTS_DISABLE: disable CTS + \param[out] none + \retval none +*/ +void usart_hardware_flow_cts_config(uint32_t usart_periph, uint32_t ctsconfig) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_CTSEN; + ctl |= ctsconfig; + /* configure CTS */ + USART_CTL2(usart_periph) = ctl; +} + +/*! + \brief configure USART DMA reception + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3) + \param[in] dmacmd: enable or disable DMA for reception + only one parameter can be selected which is shown as below: + \arg USART_DENR_ENABLE: DMA enable for reception + \arg USART_DENR_DISABLE: DMA disable for reception + \param[out] none + \retval none +*/ +void usart_dma_receive_config(uint32_t usart_periph, uint32_t dmacmd) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_DENR; + ctl |= dmacmd; + /* configure DMA reception */ + USART_CTL2(usart_periph) = ctl; +} + +/*! + \brief configure USART DMA transmission + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3) + \param[in] dmacmd: enable or disable DMA for transmission + only one parameter can be selected which is shown as below: + \arg USART_DENT_ENABLE: DMA enable for transmission + \arg USART_DENT_DISABLE: DMA disable for transmission + \param[out] none + \retval none +*/ +void usart_dma_transmit_config(uint32_t usart_periph, uint32_t dmacmd) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_DENT; + ctl |= dmacmd; + /* configure DMA transmission */ + USART_CTL2(usart_periph) = ctl; +} + +/*! + \brief get flag in STAT register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART flags, refer to usart_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_FLAG_CTSF: CTS change flag + \arg USART_FLAG_LBDF: LIN break detected flag + \arg USART_FLAG_TBE: transmit data buffer empty + \arg USART_FLAG_TC: transmission complete + \arg USART_FLAG_RBNE: read data buffer not empty + \arg USART_FLAG_IDLEF: IDLE frame detected flag + \arg USART_FLAG_ORERR: overrun error + \arg USART_FLAG_NERR: noise error flag + \arg USART_FLAG_FERR: frame error flag + \arg USART_FLAG_PERR: parity error flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag) +{ + if(RESET != (USART_REG_VAL(usart_periph, flag) & BIT(USART_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear flag in STAT register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART flags, refer to usart_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_FLAG_CTSF: CTS change flag + \arg USART_FLAG_LBDF: LIN break detected flag + \arg USART_FLAG_TC: transmission complete + \arg USART_FLAG_RBNE: read data buffer not empty + \param[out] none + \retval none +*/ +void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag) +{ + USART_REG_VAL(usart_periph, flag) &= ~BIT(USART_BIT_POS(flag)); +} + +/*! + \brief enable USART interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag + only one parameter can be selected which is shown as below: + \arg USART_INT_PERR: parity error interrupt + \arg USART_INT_TBE: transmitter buffer empty interrupt + \arg USART_INT_TC: transmission complete interrupt + \arg USART_INT_RBNE: read data buffer not empty interrupt and overrun error interrupt + \arg USART_INT_IDLE: IDLE line detected interrupt + \arg USART_INT_LBD: LIN break detected interrupt + \arg USART_INT_ERR: error interrupt + \arg USART_INT_CTS: CTS interrupt + \param[out] none + \retval none +*/ +void usart_interrupt_enable(uint32_t usart_periph, uint32_t int_flag) +{ + USART_REG_VAL(usart_periph, int_flag) |= BIT(USART_BIT_POS(int_flag)); +} + +/*! + \brief disable USART interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag + only one parameter can be selected which is shown as below: + \arg USART_INT_PERR: parity error interrupt + \arg USART_INT_TBE: transmitter buffer empty interrupt + \arg USART_INT_TC: transmission complete interrupt + \arg USART_INT_RBNE: read data buffer not empty interrupt and overrun error interrupt + \arg USART_INT_IDLE: IDLE line detected interrupt + \arg USART_INT_LBD: LIN break detected interrupt + \arg USART_INT_ERR: error interrupt + \arg USART_INT_CTS: CTS interrupt + \param[out] none + \retval none +*/ +void usart_interrupt_disable(uint32_t usart_periph, uint32_t int_flag) +{ + USART_REG_VAL(usart_periph, int_flag) &= ~BIT(USART_BIT_POS(int_flag)); +} + +/*! + \brief get USART interrupt and flag status + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag + only one parameter can be selected which is shown as below: + \arg USART_INT_FLAG_PERR: parity error interrupt and flag + \arg USART_INT_FLAG_TBE: transmitter buffer empty interrupt and flag + \arg USART_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART_INT_FLAG_RBNE: read data buffer not empty interrupt and flag + \arg USART_INT_FLAG_RBNE_ORERR: read data buffer not empty interrupt and overrun error flag + \arg USART_INT_FLAG_IDLE: IDLE line detected interrupt and flag + \arg USART_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART_INT_FLAG_CTS: CTS interrupt and flag + \arg USART_INT_FLAG_ERR_ORERR: error interrupt and overrun error + \arg USART_INT_FLAG_ERR_NERR: error interrupt and noise error flag + \arg USART_INT_FLAG_ERR_FERR: error interrupt and frame error flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, uint32_t int_flag) +{ + uint32_t intenable = 0U, flagstatus = 0U; + /* get the interrupt enable bit status */ + intenable = (USART_REG_VAL(usart_periph, int_flag) & BIT(USART_BIT_POS(int_flag))); + /* get the corresponding flag bit status */ + flagstatus = (USART_REG_VAL2(usart_periph, int_flag) & BIT(USART_BIT_POS2(int_flag))); + + if(flagstatus && intenable){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear USART interrupt flag in STAT register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART interrupt flag + only one parameter can be selected which is shown as below: + \arg USART_INT_FLAG_CTS: CTS change flag + \arg USART_INT_FLAG_LBD: LIN break detected flag + \arg USART_INT_FLAG_TC: transmission complete + \arg USART_INT_FLAG_RBNE: read data buffer not empty + \param[out] none + \retval none +*/ +void usart_interrupt_flag_clear(uint32_t usart_periph, uint32_t flag) +{ + USART_REG_VAL2(usart_periph, flag) &= ~BIT(USART_BIT_POS2(flag)); +} diff --git a/RTE/Device/GD32F107VC/gd32f10x_usart.c.base@2.0.2 b/RTE/Device/GD32F107VC/gd32f10x_usart.c.base@2.0.2 new file mode 100644 index 0000000..78530e8 --- /dev/null +++ b/RTE/Device/GD32F107VC/gd32f10x_usart.c.base@2.0.2 @@ -0,0 +1,766 @@ +/*! + \file gd32f10x_usart.c + \brief USART driver + + \version 2014-12-26, V1.0.0, firmware for GD32F10x + \version 2017-06-20, V2.0.1, firmware for GD32F10x + \version 2018-07-31, V2.1.0, firmware for GD32F10x + \version 2020-09-30, V2.2.0, firmware for GD32F10x +*/ + +/* + Copyright (c) 2020, GigaDevice Semiconductor Inc. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +#include "gd32f10x_usart.h" + +/*! + \brief reset USART/UART + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_deinit(uint32_t usart_periph) +{ + switch(usart_periph){ + case USART0: + /* reset USART0 */ + rcu_periph_reset_enable(RCU_USART0RST); + rcu_periph_reset_disable(RCU_USART0RST); + break; + case USART1: + /* reset USART1 */ + rcu_periph_reset_enable(RCU_USART1RST); + rcu_periph_reset_disable(RCU_USART1RST); + break; + case USART2: + /* reset USART2 */ + rcu_periph_reset_enable(RCU_USART2RST); + rcu_periph_reset_disable(RCU_USART2RST); + break; + case UART3: + /* reset UART3 */ + rcu_periph_reset_enable(RCU_UART3RST); + rcu_periph_reset_disable(RCU_UART3RST); + break; + case UART4: + /* reset UART4 */ + rcu_periph_reset_enable(RCU_UART4RST); + rcu_periph_reset_disable(RCU_UART4RST); + break; + default: + break; + } +} + +/*! + \brief configure USART baud rate value + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] baudval: baud rate value + \param[out] none + \retval none +*/ +void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval) +{ + uint32_t uclk=0U, intdiv=0U, fradiv=0U, udiv=0U; + switch(usart_periph){ + /* get clock frequency */ + case USART0: + /* get USART0 clock */ + uclk=rcu_clock_freq_get(CK_APB2); + break; + case USART1: + /* get USART1 clock */ + uclk=rcu_clock_freq_get(CK_APB1); + break; + case USART2: + /* get USART2 clock */ + uclk=rcu_clock_freq_get(CK_APB1); + break; + case UART3: + /* get UART3 clock */ + uclk=rcu_clock_freq_get(CK_APB1); + break; + case UART4: + /* get UART4 clock */ + uclk=rcu_clock_freq_get(CK_APB1); + break; + default: + break; + } + /* oversampling by 16, configure the value of USART_BAUD */ + udiv = (uclk+baudval/2U)/baudval; + intdiv = udiv & (0x0000fff0U); + fradiv = udiv & (0x0000000fU); + USART_BAUD(usart_periph) = ((USART_BAUD_FRADIV | USART_BAUD_INTDIV) & (intdiv | fradiv)); +} + +/*! + \brief configure USART parity + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] paritycfg: configure USART parity + only one parameter can be selected which is shown as below: + \arg USART_PM_NONE: no parity + \arg USART_PM_ODD: odd parity + \arg USART_PM_EVEN: even parity + \param[out] none + \retval none +*/ +void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg) +{ + /* clear USART_CTL0 PM,PCEN bits */ + USART_CTL0(usart_periph) &= ~(USART_CTL0_PM | USART_CTL0_PCEN); + /* configure USART parity mode */ + USART_CTL0(usart_periph) |= paritycfg ; +} + +/*! + \brief configure USART word length + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] wlen: USART word length configure + only one parameter can be selected which is shown as below: + \arg USART_WL_8BIT: 8 bits + \arg USART_WL_9BIT: 9 bits + \param[out] none + \retval none +*/ +void usart_word_length_set(uint32_t usart_periph, uint32_t wlen) +{ + /* clear USART_CTL0 WL bit */ + USART_CTL0(usart_periph) &= ~USART_CTL0_WL; + /* configure USART word length */ + USART_CTL0(usart_periph) |= wlen; +} + +/*! + \brief configure USART stop bit length + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] stblen: USART stop bit configure + only one parameter can be selected which is shown as below: + \arg USART_STB_1BIT: 1 bit + \arg USART_STB_0_5BIT: 0.5 bit, not available for UARTx(x=3,4) + \arg USART_STB_2BIT: 2 bits + \arg USART_STB_1_5BIT: 1.5 bits, not available for UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_stop_bit_set(uint32_t usart_periph, uint32_t stblen) +{ + /* clear USART_CTL1 STB bits */ + USART_CTL1(usart_periph) &= ~USART_CTL1_STB; + /* configure USART stop bits */ + USART_CTL1(usart_periph) |= stblen; +} +/*! + \brief enable USART + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_enable(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) |= USART_CTL0_UEN; +} + +/*! + \brief disable USART + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_disable(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) &= ~(USART_CTL0_UEN); +} + +/*! + \brief configure USART transmitter + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] txconfig: enable or disable USART transmitter + only one parameter can be selected which is shown as below: + \arg USART_TRANSMIT_ENABLE: enable USART transmission + \arg USART_TRANSMIT_DISABLE: disable USART transmission + \param[out] none + \retval none +*/ +void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL0(usart_periph); + ctl &= ~USART_CTL0_TEN; + ctl |= txconfig; + /* configure transfer mode */ + USART_CTL0(usart_periph) = ctl; +} + +/*! + \brief configure USART receiver + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] rxconfig: enable or disable USART receiver + only one parameter can be selected which is shown as below: + \arg USART_RECEIVE_ENABLE: enable USART reception + \arg USART_RECEIVE_DISABLE: disable USART reception + \param[out] none + \retval none +*/ +void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL0(usart_periph); + ctl &= ~USART_CTL0_REN; + ctl |= rxconfig; + /* configure receiver mode */ + USART_CTL0(usart_periph) = ctl; +} + +/*! + \brief USART transmit data function + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] data: data of transmission + \param[out] none + \retval none +*/ +void usart_data_transmit(uint32_t usart_periph, uint16_t data) +{ + USART_DATA(usart_periph) = USART_DATA_DATA & data; +} + +/*! + \brief USART receive data function + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval data of received +*/ +uint16_t usart_data_receive(uint32_t usart_periph) +{ + return (uint16_t)(GET_BITS(USART_DATA(usart_periph), 0U, 8U)); +} + +/*! + \brief configure the address of the USART in wake up by address match mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] addr: address of USART/UART + \param[out] none + \retval none +*/ +void usart_address_config(uint32_t usart_periph, uint8_t addr) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_ADDR); + USART_CTL1(usart_periph) |= (USART_CTL1_ADDR & addr); +} + +/*! + \brief receiver in mute mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_mute_mode_enable(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) |= USART_CTL0_RWU; +} + +/*! + \brief receiver in active mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_mute_mode_disable(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) &= ~(USART_CTL0_RWU); +} + +/*! + \brief configure wakeup method in mute mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] wmethod: two methods be used to enter or exit the mute mode + only one parameter can be selected which is shown as below: + \arg USART_WM_IDLE: idle line + \arg USART_WM_ADDR: address mask + \param[out] none + \retval none +*/ +void usart_mute_mode_wakeup_config(uint32_t usart_periph, uint32_t wmethod) +{ + USART_CTL0(usart_periph) &= ~(USART_CTL0_WM); + USART_CTL0(usart_periph) |= wmethod; +} + +/*! + \brief enable LIN mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_lin_mode_enable(uint32_t usart_periph) +{ + USART_CTL1(usart_periph) |= USART_CTL1_LMEN; +} + +/*! + \brief disable LIN mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_lin_mode_disable(uint32_t usart_periph) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_LMEN); +} + +/*! + \brief configure lin break frame length + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] lblen: lin break frame length + only one parameter can be selected which is shown as below: + \arg USART_LBLEN_10B: 10 bits + \arg USART_LBLEN_11B: 11 bits + \param[out] none + \retval none +*/ +void usart_lin_break_detection_length_config(uint32_t usart_periph, uint32_t lblen) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_LBLEN); + USART_CTL1(usart_periph) |= (USART_CTL1_LBLEN & lblen); +} + +/*! + \brief send break frame + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_send_break(uint32_t usart_periph) +{ + USART_CTL0(usart_periph) |= USART_CTL0_SBKCMD; +} + +/*! + \brief enable half duplex mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_halfduplex_enable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) |= USART_CTL2_HDEN; +} + +/*! + \brief disable half duplex mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_halfduplex_disable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_HDEN); +} + +/*! + \brief enable CK pin in synchronous mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_synchronous_clock_enable(uint32_t usart_periph) +{ + USART_CTL1(usart_periph) |= USART_CTL1_CKEN; +} + +/*! + \brief disable CK pin in synchronous mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_synchronous_clock_disable(uint32_t usart_periph) +{ + USART_CTL1(usart_periph) &= ~(USART_CTL1_CKEN); +} + +/*! + \brief configure USART synchronous mode parameters + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] clen: CK length + only one parameter can be selected which is shown as below: + \arg USART_CLEN_NONE: there are 7 CK pulses for an 8 bit frame and 8 CK pulses for a 9 bit frame + \arg USART_CLEN_EN: there are 8 CK pulses for an 8 bit frame and 9 CK pulses for a 9 bit frame + \param[in] cph: clock phase + only one parameter can be selected which is shown as below: + \arg USART_CPH_1CK: first clock transition is the first data capture edge + \arg USART_CPH_2CK: second clock transition is the first data capture edge + \param[in] cpl: clock polarity + only one parameter can be selected which is shown as below: + \arg USART_CPL_LOW: steady low value on CK pin + \arg USART_CPL_HIGH: steady high value on CK pin + \param[out] none + \retval none +*/ +void usart_synchronous_clock_config(uint32_t usart_periph, uint32_t clen, uint32_t cph, uint32_t cpl) +{ + uint32_t ctl = 0U; + + /* read USART_CTL1 register */ + ctl = USART_CTL1(usart_periph); + ctl &= ~(USART_CTL1_CLEN | USART_CTL1_CPH | USART_CTL1_CPL); + /* set CK length, CK phase, CK polarity */ + ctl |= (USART_CTL1_CLEN & clen) | (USART_CTL1_CPH & cph) | (USART_CTL1_CPL & cpl); + + USART_CTL1(usart_periph) = ctl; +} + +/*! + \brief configure guard time value in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] gaut: guard time value + \param[out] none + \retval none +*/ +void usart_guard_time_config(uint32_t usart_periph,uint32_t gaut) +{ + USART_GP(usart_periph) &= ~(USART_GP_GUAT); + USART_GP(usart_periph) |= (USART_GP_GUAT & ((gaut)<<8)); +} + +/*! + \brief enable smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_enable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) |= USART_CTL2_SCEN; +} + +/*! + \brief disable smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_disable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_SCEN); +} + +/*! + \brief enable NACK in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_nack_enable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) |= USART_CTL2_NKEN; +} + +/*! + \brief disable NACK in smartcard mode + \param[in] usart_periph: USARTx(x=0,1,2) + \param[out] none + \retval none +*/ +void usart_smartcard_mode_nack_disable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_NKEN); +} + +/*! + \brief enable IrDA mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_irda_mode_enable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) |= USART_CTL2_IREN; +} + +/*! + \brief disable IrDA mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[out] none + \retval none +*/ +void usart_irda_mode_disable(uint32_t usart_periph) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_IREN); +} + +/*! + \brief configure the peripheral clock prescaler in USART IrDA low-power mode + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] psc: 0x00-0xFF + \param[out] none + \retval none +*/ +void usart_prescaler_config(uint32_t usart_periph, uint8_t psc) +{ + USART_GP(usart_periph) &= ~(USART_GP_PSC); + USART_GP(usart_periph) |= psc; +} + +/*! + \brief configure IrDA low-power + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] irlp: IrDA low-power or normal + only one parameter can be selected which is shown as below: + \arg USART_IRLP_LOW: low-power + \arg USART_IRLP_NORMAL: normal + \param[out] none + \retval none +*/ +void usart_irda_lowpower_config(uint32_t usart_periph, uint32_t irlp) +{ + USART_CTL2(usart_periph) &= ~(USART_CTL2_IRLP); + USART_CTL2(usart_periph) |= (USART_CTL2_IRLP & irlp); +} + +/*! + \brief configure hardware flow control RTS + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] rtsconfig: enable or disable RTS + only one parameter can be selected which is shown as below: + \arg USART_RTS_ENABLE: enable RTS + \arg USART_RTS_DISABLE: disable RTS + \param[out] none + \retval none +*/ +void usart_hardware_flow_rts_config(uint32_t usart_periph, uint32_t rtsconfig) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_RTSEN; + ctl |= rtsconfig; + /* configure RTS */ + USART_CTL2(usart_periph) = ctl; +} + +/*! + \brief configure hardware flow control CTS + \param[in] usart_periph: USARTx(x=0,1,2) + \param[in] ctsconfig: enable or disable CTS + only one parameter can be selected which is shown as below: + \arg USART_CTS_ENABLE: enable CTS + \arg USART_CTS_DISABLE: disable CTS + \param[out] none + \retval none +*/ +void usart_hardware_flow_cts_config(uint32_t usart_periph, uint32_t ctsconfig) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_CTSEN; + ctl |= ctsconfig; + /* configure CTS */ + USART_CTL2(usart_periph) = ctl; +} + +/*! + \brief configure USART DMA reception + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3) + \param[in] dmacmd: enable or disable DMA for reception + only one parameter can be selected which is shown as below: + \arg USART_DENR_ENABLE: DMA enable for reception + \arg USART_DENR_DISABLE: DMA disable for reception + \param[out] none + \retval none +*/ +void usart_dma_receive_config(uint32_t usart_periph, uint32_t dmacmd) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_DENR; + ctl |= dmacmd; + /* configure DMA reception */ + USART_CTL2(usart_periph) = ctl; +} + +/*! + \brief configure USART DMA transmission + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3) + \param[in] dmacmd: enable or disable DMA for transmission + only one parameter can be selected which is shown as below: + \arg USART_DENT_ENABLE: DMA enable for transmission + \arg USART_DENT_DISABLE: DMA disable for transmission + \param[out] none + \retval none +*/ +void usart_dma_transmit_config(uint32_t usart_periph, uint32_t dmacmd) +{ + uint32_t ctl = 0U; + + ctl = USART_CTL2(usart_periph); + ctl &= ~USART_CTL2_DENT; + ctl |= dmacmd; + /* configure DMA transmission */ + USART_CTL2(usart_periph) = ctl; +} + +/*! + \brief get flag in STAT register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART flags, refer to usart_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_FLAG_CTSF: CTS change flag + \arg USART_FLAG_LBDF: LIN break detected flag + \arg USART_FLAG_TBE: transmit data buffer empty + \arg USART_FLAG_TC: transmission complete + \arg USART_FLAG_RBNE: read data buffer not empty + \arg USART_FLAG_IDLEF: IDLE frame detected flag + \arg USART_FLAG_ORERR: overrun error + \arg USART_FLAG_NERR: noise error flag + \arg USART_FLAG_FERR: frame error flag + \arg USART_FLAG_PERR: parity error flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag) +{ + if(RESET != (USART_REG_VAL(usart_periph, flag) & BIT(USART_BIT_POS(flag)))){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear flag in STAT register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART flags, refer to usart_flag_enum + only one parameter can be selected which is shown as below: + \arg USART_FLAG_CTSF: CTS change flag + \arg USART_FLAG_LBDF: LIN break detected flag + \arg USART_FLAG_TC: transmission complete + \arg USART_FLAG_RBNE: read data buffer not empty + \param[out] none + \retval none +*/ +void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag) +{ + USART_REG_VAL(usart_periph, flag) &= ~BIT(USART_BIT_POS(flag)); +} + +/*! + \brief enable USART interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag + only one parameter can be selected which is shown as below: + \arg USART_INT_PERR: parity error interrupt + \arg USART_INT_TBE: transmitter buffer empty interrupt + \arg USART_INT_TC: transmission complete interrupt + \arg USART_INT_RBNE: read data buffer not empty interrupt and overrun error interrupt + \arg USART_INT_IDLE: IDLE line detected interrupt + \arg USART_INT_LBD: LIN break detected interrupt + \arg USART_INT_ERR: error interrupt + \arg USART_INT_CTS: CTS interrupt + \param[out] none + \retval none +*/ +void usart_interrupt_enable(uint32_t usart_periph, uint32_t int_flag) +{ + USART_REG_VAL(usart_periph, int_flag) |= BIT(USART_BIT_POS(int_flag)); +} + +/*! + \brief disable USART interrupt + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag + only one parameter can be selected which is shown as below: + \arg USART_INT_PERR: parity error interrupt + \arg USART_INT_TBE: transmitter buffer empty interrupt + \arg USART_INT_TC: transmission complete interrupt + \arg USART_INT_RBNE: read data buffer not empty interrupt and overrun error interrupt + \arg USART_INT_IDLE: IDLE line detected interrupt + \arg USART_INT_LBD: LIN break detected interrupt + \arg USART_INT_ERR: error interrupt + \arg USART_INT_CTS: CTS interrupt + \param[out] none + \retval none +*/ +void usart_interrupt_disable(uint32_t usart_periph, uint32_t int_flag) +{ + USART_REG_VAL(usart_periph, int_flag) &= ~BIT(USART_BIT_POS(int_flag)); +} + +/*! + \brief get USART interrupt and flag status + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] int_flag + only one parameter can be selected which is shown as below: + \arg USART_INT_FLAG_PERR: parity error interrupt and flag + \arg USART_INT_FLAG_TBE: transmitter buffer empty interrupt and flag + \arg USART_INT_FLAG_TC: transmission complete interrupt and flag + \arg USART_INT_FLAG_RBNE: read data buffer not empty interrupt and flag + \arg USART_INT_FLAG_RBNE_ORERR: read data buffer not empty interrupt and overrun error flag + \arg USART_INT_FLAG_IDLE: IDLE line detected interrupt and flag + \arg USART_INT_FLAG_LBD: LIN break detected interrupt and flag + \arg USART_INT_FLAG_CTS: CTS interrupt and flag + \arg USART_INT_FLAG_ERR_ORERR: error interrupt and overrun error + \arg USART_INT_FLAG_ERR_NERR: error interrupt and noise error flag + \arg USART_INT_FLAG_ERR_FERR: error interrupt and frame error flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, uint32_t int_flag) +{ + uint32_t intenable = 0U, flagstatus = 0U; + /* get the interrupt enable bit status */ + intenable = (USART_REG_VAL(usart_periph, int_flag) & BIT(USART_BIT_POS(int_flag))); + /* get the corresponding flag bit status */ + flagstatus = (USART_REG_VAL2(usart_periph, int_flag) & BIT(USART_BIT_POS2(int_flag))); + + if(flagstatus && intenable){ + return SET; + }else{ + return RESET; + } +} + +/*! + \brief clear USART interrupt flag in STAT register + \param[in] usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4) + \param[in] flag: USART interrupt flag + only one parameter can be selected which is shown as below: + \arg USART_INT_FLAG_CTS: CTS change flag + \arg USART_INT_FLAG_LBD: LIN break detected flag + \arg USART_INT_FLAG_TC: transmission complete + \arg USART_INT_FLAG_RBNE: read data buffer not empty + \param[out] none + \retval none +*/ +void usart_interrupt_flag_clear(uint32_t usart_periph, uint32_t flag) +{ + USART_REG_VAL2(usart_periph, flag) &= ~BIT(USART_BIT_POS2(flag)); +} diff --git a/RTE/Device/GD32F107VC/startup_gd32f10x_cl.s b/RTE/Device/GD32F107VC/startup_gd32f10x_cl.s new file mode 100644 index 0000000..a3ffa4c --- /dev/null +++ b/RTE/Device/GD32F107VC/startup_gd32f10x_cl.s @@ -0,0 +1,390 @@ +;/*! +; \file startup_gd32f10x_cl.s +; \brief start up file +; +; \version 2014-12-26, V1.0.0, firmware for GD32F10x +; \version 2017-06-20, V2.0.0, firmware for GD32F10x +; \version 2018-07-31, V2.1.0, firmware for GD32F10x +;*/ +; +;/* +; Copyright (c) 2018, GigaDevice Semiconductor Inc. +; +; All rights reserved. +; +; 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. Neither the name of the copyright holder nor the names of its contributors +; may be used to endorse or promote products derived from this software without +; specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +;AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +;IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +;*/ + +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00002000 + + AREA STACK, NOINIT, READWRITE, ALIGN = 3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00002000 + + AREA HEAP, NOINIT, READWRITE, ALIGN = 3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + + PRESERVE8 + THUMB + +; /* reset Vector Mapped to at Address 0 */ + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + +; /* external interrupts handler */ + DCD WWDGT_IRQHandler ; 16:Window Watchdog Timer + DCD LVD_IRQHandler ; 17:LVD through EXTI Line detect + DCD TAMPER_IRQHandler ; 18:Tamper Interrupt + DCD RTC_IRQHandler ; 19:RTC through EXTI Line + DCD FMC_IRQHandler ; 20:FMC + DCD RCU_IRQHandler ; 21:RCU + DCD EXTI0_IRQHandler ; 22:EXTI Line 0 + DCD EXTI1_IRQHandler ; 23:EXTI Line 1 + DCD EXTI2_IRQHandler ; 24:EXTI Line 2 + DCD EXTI3_IRQHandler ; 25:EXTI Line 3 + DCD EXTI4_IRQHandler ; 26:EXTI Line 4 + DCD DMA0_Channel0_IRQHandler ; 27:DMA0 Channel 0 + DCD DMA0_Channel1_IRQHandler ; 28:DMA0 Channel 1 + DCD DMA0_Channel2_IRQHandler ; 29:DMA0 Channel 2 + DCD DMA0_Channel3_IRQHandler ; 30:DMA0 Channel 3 + DCD DMA0_Channel4_IRQHandler ; 31:DMA0 Channel 4 + DCD DMA0_Channel5_IRQHandler ; 32:DMA0 Channel 5 + DCD DMA0_Channel6_IRQHandler ; 33:DMA0 Channel 6 + DCD ADC0_1_IRQHandler ; 34:ADC0 and ADC1 + DCD CAN0_TX_IRQHandler ; 35:CAN0 TX + DCD CAN0_RX0_IRQHandler ; 36:CAN0 RX0 + DCD CAN0_RX1_IRQHandler ; 37:CAN0 RX1 + DCD CAN0_EWMC_IRQHandler ; 38:CAN0 EWMC + DCD EXTI5_9_IRQHandler ; 39:EXTI Line 5 to EXTI Line 9 + DCD TIMER0_BRK_IRQHandler ; 40:TIMER0 Break + DCD TIMER0_UP_IRQHandler ; 41:TIMER0 Update + DCD TIMER0_TRG_CMT_IRQHandler ; 42:TIMER0 Trigger and Commutation + DCD TIMER0_Channel_IRQHandler ; 43:TIMER0 Channel Capture Compare + DCD TIMER1_IRQHandler ; 44:TIMER1 + DCD TIMER2_IRQHandler ; 45:TIMER2 + DCD TIMER3_IRQHandler ; 46:TIMER3 + DCD I2C0_EV_IRQHandler ; 47:I2C0 Event + DCD I2C0_ER_IRQHandler ; 48:I2C0 Error + DCD I2C1_EV_IRQHandler ; 49:I2C1 Event + DCD I2C1_ER_IRQHandler ; 50:I2C1 Error + DCD SPI0_IRQHandler ; 51:SPI0 + DCD SPI1_IRQHandler ; 52:SPI1 + DCD USART0_IRQHandler ; 53:USART0 + DCD USART1_IRQHandler ; 54:USART1 + DCD USART2_IRQHandler ; 55:USART2 + DCD EXTI10_15_IRQHandler ; 56:EXTI Line 10 to EXTI Line 15 + DCD RTC_Alarm_IRQHandler ; 57:RTC Alarm through EXTI Line + DCD USBFS_WKUP_IRQHandler ; 58:USBFS WakeUp from suspend through EXTI Line + DCD TIMER7_BRK_IRQHandler ; 59:TIMER7 Break Interrupt + DCD TIMER7_UP_IRQHandler ; 60:TIMER7 Update Interrupt + DCD TIMER7_TRG_CMT_IRQHandler ; 61:TIMER7 Trigger + DCD TIMER7_Channel_IRQHandler ; 62:TIMER7 Channel Capture Compare + DCD 0 ; Reserved + DCD EXMC_IRQHandler ; 64:EXMC + DCD 0 ; Reserved + DCD TIMER4_IRQHandler ; 66:TIMER4 + DCD SPI2_IRQHandler ; 67:SPI2 + DCD UART3_IRQHandler ; 68:UART3 + DCD UART4_IRQHandler ; 69:UART4 + DCD TIMER5_IRQHandler ; 70:TIMER5 + DCD TIMER6_IRQHandler ; 71:TIMER6 + DCD DMA1_Channel0_IRQHandler ; 72:DMA1 Channel0 + DCD DMA1_Channel1_IRQHandler ; 73:DMA1 Channel1 + DCD DMA1_Channel2_IRQHandler ; 74:DMA1 Channel2 + DCD DMA1_Channel3_IRQHandler ; 75:DMA1 Channel3 + DCD DMA1_Channel4_IRQHandler ; 76:DMA1 Channel4 + DCD ENET_IRQHandler ; 77:Ethernet + DCD ENET_WKUP_IRQHandler ; 78:Ethernet Wakeup through EXTI line + DCD CAN1_TX_IRQHandler ; 79:CAN1 TX + DCD CAN1_RX0_IRQHandler ; 80:CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; 81:CAN1 RX1 + DCD CAN1_EWMC_IRQHandler ; 82:CAN1 EWMC + DCD USBFS_IRQHandler ; 83:USBFS + +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +;/* reset Handler */ +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT __main + IMPORT SystemInit + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +;/* dummy Exception Handlers */ +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP + +HardFault_Handler PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP + +MemManage_Handler PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP + +BusFault_Handler PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP + +UsageFault_Handler PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP + +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP + +DebugMon_Handler PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP + +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP + +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC +; /* external interrupts handler */ + EXPORT WWDGT_IRQHandler [WEAK] + EXPORT LVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FMC_IRQHandler [WEAK] + EXPORT RCU_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA0_Channel0_IRQHandler [WEAK] + EXPORT DMA0_Channel1_IRQHandler [WEAK] + EXPORT DMA0_Channel2_IRQHandler [WEAK] + EXPORT DMA0_Channel3_IRQHandler [WEAK] + EXPORT DMA0_Channel4_IRQHandler [WEAK] + EXPORT DMA0_Channel5_IRQHandler [WEAK] + EXPORT DMA0_Channel6_IRQHandler [WEAK] + EXPORT ADC0_1_IRQHandler [WEAK] + EXPORT CAN0_TX_IRQHandler [WEAK] + EXPORT CAN0_RX0_IRQHandler [WEAK] + EXPORT CAN0_RX1_IRQHandler [WEAK] + EXPORT CAN0_EWMC_IRQHandler [WEAK] + EXPORT EXTI5_9_IRQHandler [WEAK] + EXPORT TIMER0_BRK_IRQHandler [WEAK] + EXPORT TIMER0_UP_IRQHandler [WEAK] + EXPORT TIMER0_TRG_CMT_IRQHandler [WEAK] + EXPORT TIMER0_Channel_IRQHandler [WEAK] + EXPORT TIMER1_IRQHandler [WEAK] + EXPORT TIMER2_IRQHandler [WEAK] + EXPORT TIMER3_IRQHandler [WEAK] + EXPORT I2C0_EV_IRQHandler [WEAK] + EXPORT I2C0_ER_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT SPI0_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT USART0_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT EXTI10_15_IRQHandler [WEAK] + EXPORT RTC_Alarm_IRQHandler [WEAK] + EXPORT USBFS_WKUP_IRQHandler [WEAK] + EXPORT TIMER7_BRK_IRQHandler [WEAK] + EXPORT TIMER7_UP_IRQHandler [WEAK] + EXPORT TIMER7_TRG_CMT_IRQHandler [WEAK] + EXPORT TIMER7_Channel_IRQHandler [WEAK] + EXPORT EXMC_IRQHandler [WEAK] + EXPORT TIMER4_IRQHandler [WEAK] + EXPORT SPI2_IRQHandler [WEAK] + EXPORT UART3_IRQHandler [WEAK] + EXPORT UART4_IRQHandler [WEAK] + EXPORT TIMER5_IRQHandler [WEAK] + EXPORT TIMER6_IRQHandler [WEAK] + EXPORT DMA1_Channel0_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT ENET_IRQHandler [WEAK] + EXPORT ENET_WKUP_IRQHandler [WEAK] + EXPORT CAN1_TX_IRQHandler [WEAK] + EXPORT CAN1_RX0_IRQHandler [WEAK] + EXPORT CAN1_RX1_IRQHandler [WEAK] + EXPORT CAN1_EWMC_IRQHandler [WEAK] + EXPORT USBFS_IRQHandler [WEAK] + + +;/* external interrupts handler */ +WWDGT_IRQHandler +LVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FMC_IRQHandler +RCU_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA0_Channel0_IRQHandler +DMA0_Channel1_IRQHandler +DMA0_Channel2_IRQHandler +DMA0_Channel3_IRQHandler +DMA0_Channel4_IRQHandler +DMA0_Channel5_IRQHandler +DMA0_Channel6_IRQHandler +ADC0_1_IRQHandler +CAN0_TX_IRQHandler +CAN0_RX0_IRQHandler +CAN0_RX1_IRQHandler +CAN0_EWMC_IRQHandler +EXTI5_9_IRQHandler +TIMER0_BRK_IRQHandler +TIMER0_UP_IRQHandler +TIMER0_TRG_CMT_IRQHandler +TIMER0_Channel_IRQHandler +TIMER1_IRQHandler +TIMER2_IRQHandler +TIMER3_IRQHandler +I2C0_EV_IRQHandler +I2C0_ER_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +SPI0_IRQHandler +SPI1_IRQHandler +USART0_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +EXTI10_15_IRQHandler +RTC_Alarm_IRQHandler +USBFS_WKUP_IRQHandler +TIMER7_BRK_IRQHandler +TIMER7_UP_IRQHandler +TIMER7_TRG_CMT_IRQHandler +TIMER7_Channel_IRQHandler +EXMC_IRQHandler +TIMER4_IRQHandler +SPI2_IRQHandler +UART3_IRQHandler +UART4_IRQHandler +TIMER5_IRQHandler +TIMER6_IRQHandler +DMA1_Channel0_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +ENET_IRQHandler +ENET_WKUP_IRQHandler +CAN1_TX_IRQHandler +CAN1_RX0_IRQHandler +CAN1_RX1_IRQHandler +CAN1_EWMC_IRQHandler +USBFS_IRQHandler + + + B . + ENDP + + ALIGN + +; user Initial Stack & Heap + + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap PROC + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + ENDP + + ALIGN + + ENDIF + + END diff --git a/RTE/Device/GD32F107VC/startup_gd32f10x_cl.s.base@2.0.2 b/RTE/Device/GD32F107VC/startup_gd32f10x_cl.s.base@2.0.2 new file mode 100644 index 0000000..a3ffa4c --- /dev/null +++ b/RTE/Device/GD32F107VC/startup_gd32f10x_cl.s.base@2.0.2 @@ -0,0 +1,390 @@ +;/*! +; \file startup_gd32f10x_cl.s +; \brief start up file +; +; \version 2014-12-26, V1.0.0, firmware for GD32F10x +; \version 2017-06-20, V2.0.0, firmware for GD32F10x +; \version 2018-07-31, V2.1.0, firmware for GD32F10x +;*/ +; +;/* +; Copyright (c) 2018, GigaDevice Semiconductor Inc. +; +; All rights reserved. +; +; 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. Neither the name of the copyright holder nor the names of its contributors +; may be used to endorse or promote products derived from this software without +; specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +;AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +;IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +;*/ + +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00002000 + + AREA STACK, NOINIT, READWRITE, ALIGN = 3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00002000 + + AREA HEAP, NOINIT, READWRITE, ALIGN = 3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + + PRESERVE8 + THUMB + +; /* reset Vector Mapped to at Address 0 */ + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + +; /* external interrupts handler */ + DCD WWDGT_IRQHandler ; 16:Window Watchdog Timer + DCD LVD_IRQHandler ; 17:LVD through EXTI Line detect + DCD TAMPER_IRQHandler ; 18:Tamper Interrupt + DCD RTC_IRQHandler ; 19:RTC through EXTI Line + DCD FMC_IRQHandler ; 20:FMC + DCD RCU_IRQHandler ; 21:RCU + DCD EXTI0_IRQHandler ; 22:EXTI Line 0 + DCD EXTI1_IRQHandler ; 23:EXTI Line 1 + DCD EXTI2_IRQHandler ; 24:EXTI Line 2 + DCD EXTI3_IRQHandler ; 25:EXTI Line 3 + DCD EXTI4_IRQHandler ; 26:EXTI Line 4 + DCD DMA0_Channel0_IRQHandler ; 27:DMA0 Channel 0 + DCD DMA0_Channel1_IRQHandler ; 28:DMA0 Channel 1 + DCD DMA0_Channel2_IRQHandler ; 29:DMA0 Channel 2 + DCD DMA0_Channel3_IRQHandler ; 30:DMA0 Channel 3 + DCD DMA0_Channel4_IRQHandler ; 31:DMA0 Channel 4 + DCD DMA0_Channel5_IRQHandler ; 32:DMA0 Channel 5 + DCD DMA0_Channel6_IRQHandler ; 33:DMA0 Channel 6 + DCD ADC0_1_IRQHandler ; 34:ADC0 and ADC1 + DCD CAN0_TX_IRQHandler ; 35:CAN0 TX + DCD CAN0_RX0_IRQHandler ; 36:CAN0 RX0 + DCD CAN0_RX1_IRQHandler ; 37:CAN0 RX1 + DCD CAN0_EWMC_IRQHandler ; 38:CAN0 EWMC + DCD EXTI5_9_IRQHandler ; 39:EXTI Line 5 to EXTI Line 9 + DCD TIMER0_BRK_IRQHandler ; 40:TIMER0 Break + DCD TIMER0_UP_IRQHandler ; 41:TIMER0 Update + DCD TIMER0_TRG_CMT_IRQHandler ; 42:TIMER0 Trigger and Commutation + DCD TIMER0_Channel_IRQHandler ; 43:TIMER0 Channel Capture Compare + DCD TIMER1_IRQHandler ; 44:TIMER1 + DCD TIMER2_IRQHandler ; 45:TIMER2 + DCD TIMER3_IRQHandler ; 46:TIMER3 + DCD I2C0_EV_IRQHandler ; 47:I2C0 Event + DCD I2C0_ER_IRQHandler ; 48:I2C0 Error + DCD I2C1_EV_IRQHandler ; 49:I2C1 Event + DCD I2C1_ER_IRQHandler ; 50:I2C1 Error + DCD SPI0_IRQHandler ; 51:SPI0 + DCD SPI1_IRQHandler ; 52:SPI1 + DCD USART0_IRQHandler ; 53:USART0 + DCD USART1_IRQHandler ; 54:USART1 + DCD USART2_IRQHandler ; 55:USART2 + DCD EXTI10_15_IRQHandler ; 56:EXTI Line 10 to EXTI Line 15 + DCD RTC_Alarm_IRQHandler ; 57:RTC Alarm through EXTI Line + DCD USBFS_WKUP_IRQHandler ; 58:USBFS WakeUp from suspend through EXTI Line + DCD TIMER7_BRK_IRQHandler ; 59:TIMER7 Break Interrupt + DCD TIMER7_UP_IRQHandler ; 60:TIMER7 Update Interrupt + DCD TIMER7_TRG_CMT_IRQHandler ; 61:TIMER7 Trigger + DCD TIMER7_Channel_IRQHandler ; 62:TIMER7 Channel Capture Compare + DCD 0 ; Reserved + DCD EXMC_IRQHandler ; 64:EXMC + DCD 0 ; Reserved + DCD TIMER4_IRQHandler ; 66:TIMER4 + DCD SPI2_IRQHandler ; 67:SPI2 + DCD UART3_IRQHandler ; 68:UART3 + DCD UART4_IRQHandler ; 69:UART4 + DCD TIMER5_IRQHandler ; 70:TIMER5 + DCD TIMER6_IRQHandler ; 71:TIMER6 + DCD DMA1_Channel0_IRQHandler ; 72:DMA1 Channel0 + DCD DMA1_Channel1_IRQHandler ; 73:DMA1 Channel1 + DCD DMA1_Channel2_IRQHandler ; 74:DMA1 Channel2 + DCD DMA1_Channel3_IRQHandler ; 75:DMA1 Channel3 + DCD DMA1_Channel4_IRQHandler ; 76:DMA1 Channel4 + DCD ENET_IRQHandler ; 77:Ethernet + DCD ENET_WKUP_IRQHandler ; 78:Ethernet Wakeup through EXTI line + DCD CAN1_TX_IRQHandler ; 79:CAN1 TX + DCD CAN1_RX0_IRQHandler ; 80:CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; 81:CAN1 RX1 + DCD CAN1_EWMC_IRQHandler ; 82:CAN1 EWMC + DCD USBFS_IRQHandler ; 83:USBFS + +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +;/* reset Handler */ +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT __main + IMPORT SystemInit + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +;/* dummy Exception Handlers */ +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP + +HardFault_Handler PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP + +MemManage_Handler PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP + +BusFault_Handler PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP + +UsageFault_Handler PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP + +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP + +DebugMon_Handler PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP + +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP + +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC +; /* external interrupts handler */ + EXPORT WWDGT_IRQHandler [WEAK] + EXPORT LVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FMC_IRQHandler [WEAK] + EXPORT RCU_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA0_Channel0_IRQHandler [WEAK] + EXPORT DMA0_Channel1_IRQHandler [WEAK] + EXPORT DMA0_Channel2_IRQHandler [WEAK] + EXPORT DMA0_Channel3_IRQHandler [WEAK] + EXPORT DMA0_Channel4_IRQHandler [WEAK] + EXPORT DMA0_Channel5_IRQHandler [WEAK] + EXPORT DMA0_Channel6_IRQHandler [WEAK] + EXPORT ADC0_1_IRQHandler [WEAK] + EXPORT CAN0_TX_IRQHandler [WEAK] + EXPORT CAN0_RX0_IRQHandler [WEAK] + EXPORT CAN0_RX1_IRQHandler [WEAK] + EXPORT CAN0_EWMC_IRQHandler [WEAK] + EXPORT EXTI5_9_IRQHandler [WEAK] + EXPORT TIMER0_BRK_IRQHandler [WEAK] + EXPORT TIMER0_UP_IRQHandler [WEAK] + EXPORT TIMER0_TRG_CMT_IRQHandler [WEAK] + EXPORT TIMER0_Channel_IRQHandler [WEAK] + EXPORT TIMER1_IRQHandler [WEAK] + EXPORT TIMER2_IRQHandler [WEAK] + EXPORT TIMER3_IRQHandler [WEAK] + EXPORT I2C0_EV_IRQHandler [WEAK] + EXPORT I2C0_ER_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT SPI0_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT USART0_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT EXTI10_15_IRQHandler [WEAK] + EXPORT RTC_Alarm_IRQHandler [WEAK] + EXPORT USBFS_WKUP_IRQHandler [WEAK] + EXPORT TIMER7_BRK_IRQHandler [WEAK] + EXPORT TIMER7_UP_IRQHandler [WEAK] + EXPORT TIMER7_TRG_CMT_IRQHandler [WEAK] + EXPORT TIMER7_Channel_IRQHandler [WEAK] + EXPORT EXMC_IRQHandler [WEAK] + EXPORT TIMER4_IRQHandler [WEAK] + EXPORT SPI2_IRQHandler [WEAK] + EXPORT UART3_IRQHandler [WEAK] + EXPORT UART4_IRQHandler [WEAK] + EXPORT TIMER5_IRQHandler [WEAK] + EXPORT TIMER6_IRQHandler [WEAK] + EXPORT DMA1_Channel0_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT ENET_IRQHandler [WEAK] + EXPORT ENET_WKUP_IRQHandler [WEAK] + EXPORT CAN1_TX_IRQHandler [WEAK] + EXPORT CAN1_RX0_IRQHandler [WEAK] + EXPORT CAN1_RX1_IRQHandler [WEAK] + EXPORT CAN1_EWMC_IRQHandler [WEAK] + EXPORT USBFS_IRQHandler [WEAK] + + +;/* external interrupts handler */ +WWDGT_IRQHandler +LVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FMC_IRQHandler +RCU_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA0_Channel0_IRQHandler +DMA0_Channel1_IRQHandler +DMA0_Channel2_IRQHandler +DMA0_Channel3_IRQHandler +DMA0_Channel4_IRQHandler +DMA0_Channel5_IRQHandler +DMA0_Channel6_IRQHandler +ADC0_1_IRQHandler +CAN0_TX_IRQHandler +CAN0_RX0_IRQHandler +CAN0_RX1_IRQHandler +CAN0_EWMC_IRQHandler +EXTI5_9_IRQHandler +TIMER0_BRK_IRQHandler +TIMER0_UP_IRQHandler +TIMER0_TRG_CMT_IRQHandler +TIMER0_Channel_IRQHandler +TIMER1_IRQHandler +TIMER2_IRQHandler +TIMER3_IRQHandler +I2C0_EV_IRQHandler +I2C0_ER_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +SPI0_IRQHandler +SPI1_IRQHandler +USART0_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +EXTI10_15_IRQHandler +RTC_Alarm_IRQHandler +USBFS_WKUP_IRQHandler +TIMER7_BRK_IRQHandler +TIMER7_UP_IRQHandler +TIMER7_TRG_CMT_IRQHandler +TIMER7_Channel_IRQHandler +EXMC_IRQHandler +TIMER4_IRQHandler +SPI2_IRQHandler +UART3_IRQHandler +UART4_IRQHandler +TIMER5_IRQHandler +TIMER6_IRQHandler +DMA1_Channel0_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +ENET_IRQHandler +ENET_WKUP_IRQHandler +CAN1_TX_IRQHandler +CAN1_RX0_IRQHandler +CAN1_RX1_IRQHandler +CAN1_EWMC_IRQHandler +USBFS_IRQHandler + + + B . + ENDP + + ALIGN + +; user Initial Stack & Heap + + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap PROC + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + ENDP + + ALIGN + + ENDIF + + END diff --git a/RTE/Device/GD32F107VC/system_gd32f10x.c b/RTE/Device/GD32F107VC/system_gd32f10x.c new file mode 100644 index 0000000..f51ebad --- /dev/null +++ b/RTE/Device/GD32F107VC/system_gd32f10x.c @@ -0,0 +1,1028 @@ +/*! + \file system_gd32f10x.c + \brief CMSIS Cortex-M3 Device Peripheral Access Layer Source File for + GD32F10x Device Series +*/ + +/* + Copyright (c) 2012 ARM LIMITED + + All rights reserved. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */ + +#include "gd32f10x.h" + +/* system frequency define */ +#define __IRC8M (IRC8M_VALUE) /* internal 8 MHz RC oscillator frequency */ +#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */ +#define __SYS_OSC_CLK (__IRC8M) /* main oscillator frequency */ + +/* select a system clock by uncommenting the following line */ +/* use IRC8M */ +//#define __SYSTEM_CLOCK_48M_PLL_IRC8M (uint32_t)(48000000) +//#define __SYSTEM_CLOCK_72M_PLL_IRC8M (uint32_t)(72000000) +//#define __SYSTEM_CLOCK_108M_PLL_IRC8M (uint32_t)(108000000) + +/* use HXTAL (XD series CK_HXTAL = 8M, CL series CK_HXTAL = 25M) */ +#define __SYSTEM_CLOCK_HXTAL (uint32_t)(__HXTAL) +//#define __SYSTEM_CLOCK_24M_PLL_HXTAL (uint32_t)(24000000) +//#define __SYSTEM_CLOCK_36M_PLL_HXTAL (uint32_t)(36000000) +//#define __SYSTEM_CLOCK_48M_PLL_HXTAL (uint32_t)(48000000) +//#define __SYSTEM_CLOCK_56M_PLL_HXTAL (uint32_t)(56000000) +//#define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000) +//#define __SYSTEM_CLOCK_96M_PLL_HXTAL (uint32_t)(96000000) +//#define __SYSTEM_CLOCK_108M_PLL_HXTAL (uint32_t)(108000000) + +#define SEL_IRC8M 0x00U +#define SEL_HXTAL 0x01U +#define SEL_PLL 0x02U + +/* set the system clock frequency and declare the system clock configuration function */ +#ifdef __SYSTEM_CLOCK_48M_PLL_IRC8M +uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_IRC8M; +static void system_clock_48m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_IRC8M; +static void system_clock_72m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_IRC8M; +static void system_clock_108m_irc8m(void); + +#elif defined (__SYSTEM_CLOCK_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_HXTAL; +static void system_clock_hxtal(void); +#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_24M_PLL_HXTAL; +static void system_clock_24m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_36M_PLL_HXTAL; +static void system_clock_36m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_HXTAL; +static void system_clock_48m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_56M_PLL_HXTAL; +static void system_clock_56m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL; +static void system_clock_72m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_96M_PLL_HXTAL; +static void system_clock_96m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_HXTAL; +static void system_clock_108m_hxtal(void); +#endif /* __SYSTEM_CLOCK_48M_PLL_IRC8M */ + +/* configure the system clock */ +static void system_clock_config(void); + +/*! + \brief configure the system clock + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_config(void) +{ +#ifdef __SYSTEM_CLOCK_HXTAL + system_clock_hxtal(); +#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL) + system_clock_24m_hxtal(); +#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL) + system_clock_36m_hxtal(); +#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL) + system_clock_48m_hxtal(); +#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL) + system_clock_56m_hxtal(); +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) + system_clock_72m_hxtal(); +#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL) + system_clock_96m_hxtal(); +#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL) + system_clock_108m_hxtal(); + +#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M) + system_clock_48m_irc8m(); +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) + system_clock_72m_irc8m(); +#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M) + system_clock_108m_irc8m(); +#endif /* __SYSTEM_CLOCK_HXTAL */ +} + +/*! + \brief setup the microcontroller system, initialize the system + \param[in] none + \param[out] none + \retval none +*/ +void SystemInit(void) +{ + /* reset the RCC clock configuration to the default reset state */ + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* reset SCS, AHBPSC, APB1PSC, APB2PSC, ADCPSC, CKOUT0SEL bits */ + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_ADCPSC_2 | RCU_CFG0_CKOUT0SEL); + + /* reset HXTALEN, CKMEN, PLLEN bits */ + RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN); + + /* Reset HXTALBPS bit */ + RCU_CTL &= ~(RCU_CTL_HXTALBPS); + + /* reset PLLSEL, PREDV0_LSB, PLLMF, USBFSPSC bits */ + +#ifdef GD32F10X_CL + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF | + RCU_CFG0_USBFSPSC | RCU_CFG0_PLLMF_4); + + RCU_CFG1 = 0x00000000U; +#else + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0 | RCU_CFG0_PLLMF | + RCU_CFG0_USBDPSC | RCU_CFG0_PLLMF_4); +#endif /* GD32F10X_CL */ + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* reset HXTALEN, CKMEN and PLLEN bits */ + RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN); + /* disable all interrupts */ + RCU_INT = 0x009F0000U; +#elif defined(GD32F10X_CL) + /* Reset HXTALEN, CKMEN, PLLEN, PLL1EN and PLL2EN bits */ + RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN); + /* disable all interrupts */ + RCU_INT = 0x00FF0000U; +#endif + + /* Configure the System clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */ + system_clock_config(); +} + +/*! + \brief update the SystemCoreClock with current core clock retrieved from cpu registers + \param[in] none + \param[out] none + \retval none +*/ +void SystemCoreClockUpdate(void) +{ + uint32_t scss; + uint32_t pllsel, predv0sel, pllmf, ck_src; +#ifdef GD32F10X_CL + uint32_t predv0, predv1, pll1mf; +#endif /* GD32F10X_CL */ + + scss = GET_BITS(RCU_CFG0, 2, 3); + + switch (scss) + { + /* IRC8M is selected as CK_SYS */ + case SEL_IRC8M: + SystemCoreClock = IRC8M_VALUE; + break; + + /* HXTAL is selected as CK_SYS */ + case SEL_HXTAL: + SystemCoreClock = HXTAL_VALUE; + break; + + /* PLL is selected as CK_SYS */ + case SEL_PLL: + /* PLL clock source selection, HXTAL or IRC8M/2 */ + pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL); + + + if(RCU_PLLSRC_IRC8M_DIV2 == pllsel){ + /* PLL clock source is IRC8M/2 */ + ck_src = IRC8M_VALUE / 2U; + }else{ + /* PLL clock source is HXTAL */ + ck_src = HXTAL_VALUE; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + predv0sel = (RCU_CFG0 & RCU_CFG0_PREDV0); + + /* PREDV0 input source clock divided by 2 */ + if(RCU_CFG0_PREDV0 == predv0sel){ + ck_src = HXTAL_VALUE / 2U; + } +#elif defined(GD32F10X_CL) + predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL); + + /* source clock use PLL1 */ + if(RCU_PREDV0SRC_CKPLL1 == predv0sel){ + predv1 = ((RCU_CFG1 & RCU_CFG1_PREDV1) >> 4) + 1U; + pll1mf = ((RCU_CFG1 & RCU_CFG1_PLL1MF) >> 8) + 2U; + if(17U == pll1mf){ + pll1mf = 20U; + } + ck_src = (ck_src / predv1) * pll1mf; + } + predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U; + ck_src /= predv0; +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + } + + /* PLL multiplication factor */ + pllmf = GET_BITS(RCU_CFG0, 18, 21); + + if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){ + pllmf |= 0x10U; + } + + if(pllmf >= 15U){ + pllmf += 1U; + }else{ + pllmf += 2U; + } + + SystemCoreClock = ck_src * pllmf; + +#ifdef GD32F10X_CL + if(15U == pllmf){ + /* PLL source clock multiply by 6.5 */ + SystemCoreClock = ck_src * 6U + ck_src / 2U; + } +#endif /* GD32F10X_CL */ + + break; + + /* IRC8M is selected as CK_SYS */ + default: + SystemCoreClock = IRC8M_VALUE; + break; + } +} + +#ifdef __SYSTEM_CLOCK_HXTAL +/*! + \brief configure the system clock to HXTAL + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* select HXTAL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_HXTAL; + + /* wait until HXTAL is selected as system clock */ + while(0 == (RCU_CFG0 & RCU_SCSS_HXTAL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL) +/*! + \brief configure the system clock to 24M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_24m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 6 = 24 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL6; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 6 = 24 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL6); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL) +/*! + \brief configure the system clock to 36M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_36m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 9 = 36 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL9; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 9 = 36 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL9); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL) +/*! + \brief configure the system clock to 48M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_48m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 12 = 48 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL12; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 12 = 48 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL12); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL) +/*! + \brief configure the system clock to 56M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_56m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 14 = 56 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL14; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 14 = 56 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL14); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) +/*! + \brief configure the system clock to 72M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_72m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL18; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL18); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL) +/*! + \brief configure the system clock to 96M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_96m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 24 = 96 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL24; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 24 = 96 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL24); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL) +/*! + \brief configure the system clock to 108M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_108m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 27 = 108 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL27; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 27 = 108 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL27); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while(0U == (RCU_CTL & RCU_CTL_PLL1STB)){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M) +/*! + \brief configure the system clock to 48M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_48m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + /* IRC8M is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 12 = 48 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL12; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) +/*! + \brief configure the system clock to 72M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_72m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + /* IRC8M is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL18; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M) +/*! + \brief configure the system clock to 108M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_108m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + /* IRC8M is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 27 = 108 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL27; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#endif diff --git a/RTE/Device/GD32F107VC/system_gd32f10x.c.base@2.0.2 b/RTE/Device/GD32F107VC/system_gd32f10x.c.base@2.0.2 new file mode 100644 index 0000000..43ff67c --- /dev/null +++ b/RTE/Device/GD32F107VC/system_gd32f10x.c.base@2.0.2 @@ -0,0 +1,1028 @@ +/*! + \file system_gd32f10x.c + \brief CMSIS Cortex-M3 Device Peripheral Access Layer Source File for + GD32F10x Device Series +*/ + +/* + Copyright (c) 2012 ARM LIMITED + + All rights reserved. + + 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. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. +*/ + +/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */ + +#include "gd32f10x.h" + +/* system frequency define */ +#define __IRC8M (IRC8M_VALUE) /* internal 8 MHz RC oscillator frequency */ +#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */ +#define __SYS_OSC_CLK (__IRC8M) /* main oscillator frequency */ + +/* select a system clock by uncommenting the following line */ +/* use IRC8M */ +//#define __SYSTEM_CLOCK_48M_PLL_IRC8M (uint32_t)(48000000) +//#define __SYSTEM_CLOCK_72M_PLL_IRC8M (uint32_t)(72000000) +//#define __SYSTEM_CLOCK_108M_PLL_IRC8M (uint32_t)(108000000) + +/* use HXTAL (XD series CK_HXTAL = 8M, CL series CK_HXTAL = 25M) */ +//#define __SYSTEM_CLOCK_HXTAL (uint32_t)(__HXTAL) +//#define __SYSTEM_CLOCK_24M_PLL_HXTAL (uint32_t)(24000000) +//#define __SYSTEM_CLOCK_36M_PLL_HXTAL (uint32_t)(36000000) +//#define __SYSTEM_CLOCK_48M_PLL_HXTAL (uint32_t)(48000000) +//#define __SYSTEM_CLOCK_56M_PLL_HXTAL (uint32_t)(56000000) +//#define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000) +//#define __SYSTEM_CLOCK_96M_PLL_HXTAL (uint32_t)(96000000) +#define __SYSTEM_CLOCK_108M_PLL_HXTAL (uint32_t)(108000000) + +#define SEL_IRC8M 0x00U +#define SEL_HXTAL 0x01U +#define SEL_PLL 0x02U + +/* set the system clock frequency and declare the system clock configuration function */ +#ifdef __SYSTEM_CLOCK_48M_PLL_IRC8M +uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_IRC8M; +static void system_clock_48m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_IRC8M; +static void system_clock_72m_irc8m(void); +#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_IRC8M; +static void system_clock_108m_irc8m(void); + +#elif defined (__SYSTEM_CLOCK_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_HXTAL; +static void system_clock_hxtal(void); +#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_24M_PLL_HXTAL; +static void system_clock_24m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_36M_PLL_HXTAL; +static void system_clock_36m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_HXTAL; +static void system_clock_48m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_56M_PLL_HXTAL; +static void system_clock_56m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL; +static void system_clock_72m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_96M_PLL_HXTAL; +static void system_clock_96m_hxtal(void); +#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL) +uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_HXTAL; +static void system_clock_108m_hxtal(void); +#endif /* __SYSTEM_CLOCK_48M_PLL_IRC8M */ + +/* configure the system clock */ +static void system_clock_config(void); + +/*! + \brief configure the system clock + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_config(void) +{ +#ifdef __SYSTEM_CLOCK_HXTAL + system_clock_hxtal(); +#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL) + system_clock_24m_hxtal(); +#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL) + system_clock_36m_hxtal(); +#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL) + system_clock_48m_hxtal(); +#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL) + system_clock_56m_hxtal(); +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) + system_clock_72m_hxtal(); +#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL) + system_clock_96m_hxtal(); +#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL) + system_clock_108m_hxtal(); + +#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M) + system_clock_48m_irc8m(); +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) + system_clock_72m_irc8m(); +#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M) + system_clock_108m_irc8m(); +#endif /* __SYSTEM_CLOCK_HXTAL */ +} + +/*! + \brief setup the microcontroller system, initialize the system + \param[in] none + \param[out] none + \retval none +*/ +void SystemInit(void) +{ + /* reset the RCC clock configuration to the default reset state */ + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* reset SCS, AHBPSC, APB1PSC, APB2PSC, ADCPSC, CKOUT0SEL bits */ + RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | + RCU_CFG0_ADCPSC | RCU_CFG0_ADCPSC_2 | RCU_CFG0_CKOUT0SEL); + + /* reset HXTALEN, CKMEN, PLLEN bits */ + RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN); + + /* Reset HXTALBPS bit */ + RCU_CTL &= ~(RCU_CTL_HXTALBPS); + + /* reset PLLSEL, PREDV0_LSB, PLLMF, USBFSPSC bits */ + +#ifdef GD32F10X_CL + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF | + RCU_CFG0_USBFSPSC | RCU_CFG0_PLLMF_4); + + RCU_CFG1 = 0x00000000U; +#else + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0 | RCU_CFG0_PLLMF | + RCU_CFG0_USBDPSC | RCU_CFG0_PLLMF_4); +#endif /* GD32F10X_CL */ + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* reset HXTALEN, CKMEN and PLLEN bits */ + RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN); + /* disable all interrupts */ + RCU_INT = 0x009F0000U; +#elif defined(GD32F10X_CL) + /* Reset HXTALEN, CKMEN, PLLEN, PLL1EN and PLL2EN bits */ + RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN); + /* disable all interrupts */ + RCU_INT = 0x00FF0000U; +#endif + + /* Configure the System clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */ + system_clock_config(); +} + +/*! + \brief update the SystemCoreClock with current core clock retrieved from cpu registers + \param[in] none + \param[out] none + \retval none +*/ +void SystemCoreClockUpdate(void) +{ + uint32_t scss; + uint32_t pllsel, predv0sel, pllmf, ck_src; +#ifdef GD32F10X_CL + uint32_t predv0, predv1, pll1mf; +#endif /* GD32F10X_CL */ + + scss = GET_BITS(RCU_CFG0, 2, 3); + + switch (scss) + { + /* IRC8M is selected as CK_SYS */ + case SEL_IRC8M: + SystemCoreClock = IRC8M_VALUE; + break; + + /* HXTAL is selected as CK_SYS */ + case SEL_HXTAL: + SystemCoreClock = HXTAL_VALUE; + break; + + /* PLL is selected as CK_SYS */ + case SEL_PLL: + /* PLL clock source selection, HXTAL or IRC8M/2 */ + pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL); + + + if(RCU_PLLSRC_IRC8M_DIV2 == pllsel){ + /* PLL clock source is IRC8M/2 */ + ck_src = IRC8M_VALUE / 2U; + }else{ + /* PLL clock source is HXTAL */ + ck_src = HXTAL_VALUE; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + predv0sel = (RCU_CFG0 & RCU_CFG0_PREDV0); + + /* PREDV0 input source clock divided by 2 */ + if(RCU_CFG0_PREDV0 == predv0sel){ + ck_src = HXTAL_VALUE / 2U; + } +#elif defined(GD32F10X_CL) + predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL); + + /* source clock use PLL1 */ + if(RCU_PREDV0SRC_CKPLL1 == predv0sel){ + predv1 = ((RCU_CFG1 & RCU_CFG1_PREDV1) >> 4) + 1U; + pll1mf = ((RCU_CFG1 & RCU_CFG1_PLL1MF) >> 8) + 2U; + if(17U == pll1mf){ + pll1mf = 20U; + } + ck_src = (ck_src / predv1) * pll1mf; + } + predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U; + ck_src /= predv0; +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + } + + /* PLL multiplication factor */ + pllmf = GET_BITS(RCU_CFG0, 18, 21); + + if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){ + pllmf |= 0x10U; + } + + if(pllmf >= 15U){ + pllmf += 1U; + }else{ + pllmf += 2U; + } + + SystemCoreClock = ck_src * pllmf; + +#ifdef GD32F10X_CL + if(15U == pllmf){ + /* PLL source clock multiply by 6.5 */ + SystemCoreClock = ck_src * 6U + ck_src / 2U; + } +#endif /* GD32F10X_CL */ + + break; + + /* IRC8M is selected as CK_SYS */ + default: + SystemCoreClock = IRC8M_VALUE; + break; + } +} + +#ifdef __SYSTEM_CLOCK_HXTAL +/*! + \brief configure the system clock to HXTAL + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* select HXTAL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_HXTAL; + + /* wait until HXTAL is selected as system clock */ + while(0 == (RCU_CFG0 & RCU_SCSS_HXTAL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL) +/*! + \brief configure the system clock to 24M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_24m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 6 = 24 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL6; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 6 = 24 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL6); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL) +/*! + \brief configure the system clock to 36M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_36m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 9 = 36 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL9; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 9 = 36 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL9); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL) +/*! + \brief configure the system clock to 48M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_48m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 12 = 48 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL12; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 12 = 48 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL12); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL) +/*! + \brief configure the system clock to 56M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_56m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 14 = 56 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL14; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 14 = 56 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL14); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL) +/*! + \brief configure the system clock to 72M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_72m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL18; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL18); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL) +/*! + \brief configure the system clock to 96M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_96m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 24 = 96 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL24; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 24 = 96 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL24); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while((RCU_CTL & RCU_CTL_PLL1STB) == 0){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL) +/*! + \brief configure the system clock to 108M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_108m_hxtal(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable HXTAL */ + RCU_CTL |= RCU_CTL_HXTALEN; + + /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB); + }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){ + while(1){ + } + } + + /* HXTAL is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + +#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD)) + /* select HXTAL/2 as clock source */ + RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0); + + /* CK_PLL = (CK_HXTAL/2) * 27 = 108 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL27; + +#elif defined(GD32F10X_CL) + /* CK_PLL = (CK_PREDIV0) * 27 = 108 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL27); + + /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */ + RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0); + RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10); + + /* enable PLL1 */ + RCU_CTL |= RCU_CTL_PLL1EN; + /* wait till PLL1 is ready */ + while(0U == (RCU_CTL & RCU_CTL_PLL1STB)){ + } +#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */ + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M) +/*! + \brief configure the system clock to 48M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_48m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + /* IRC8M is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 12 = 48 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL12; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M) +/*! + \brief configure the system clock to 72M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_72m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + /* IRC8M is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 18 = 72 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL18; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M) +/*! + \brief configure the system clock to 108M by PLL which selects IRC8M as its clock source + \param[in] none + \param[out] none + \retval none +*/ +static void system_clock_108m_irc8m(void) +{ + uint32_t timeout = 0U; + uint32_t stab_flag = 0U; + + /* enable IRC8M */ + RCU_CTL |= RCU_CTL_IRC8MEN; + + /* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */ + do{ + timeout++; + stab_flag = (RCU_CTL & RCU_CTL_IRC8MSTB); + } + while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout)); + + /* if fail */ + if(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){ + while(1){ + } + } + + /* IRC8M is stable */ + /* AHB = SYSCLK */ + RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; + /* APB2 = AHB/1 */ + RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; + /* APB1 = AHB/2 */ + RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + + /* CK_PLL = (CK_IRC8M/2) * 27 = 108 MHz */ + RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); + RCU_CFG0 |= RCU_PLL_MUL27; + + /* enable PLL */ + RCU_CTL |= RCU_CTL_PLLEN; + + /* wait until PLL is stable */ + while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){ + } + + /* select PLL as system clock */ + RCU_CFG0 &= ~RCU_CFG0_SCS; + RCU_CFG0 |= RCU_CKSYSSRC_PLL; + + /* wait until PLL is selected as system clock */ + while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){ + } +} + +#endif diff --git a/RTE/RTOS/FreeRTOSConfig.h b/RTE/RTOS/FreeRTOSConfig.h new file mode 100644 index 0000000..feff4d0 --- /dev/null +++ b/RTE/RTOS/FreeRTOSConfig.h @@ -0,0 +1,162 @@ +/* + * FreeRTOS Kernel V10.4.0 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html + *----------------------------------------------------------*/ + +#if (defined(__ARMCC_VERSION) || defined(__GNUC__) || defined(__ICCARM__)) +#include + +extern uint32_t SystemCoreClock; +#endif + +/* Constants that describe the hardware and memory usage. */ +#define configCPU_CLOCK_HZ (SystemCoreClock) +#define configTICK_RATE_HZ ((TickType_t)1000) +#define configTOTAL_HEAP_SIZE ((size_t)4096) +#define configMINIMAL_STACK_SIZE ((uint16_t)256) +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configSUPPORT_STATIC_ALLOCATION 0 + +/* Constants related to the behaviour or the scheduler. */ +#define configMAX_PRIORITIES 5 +#define configUSE_PREEMPTION 1 +#define configUSE_TIME_SLICING 1 +#define configIDLE_SHOULD_YIELD 1 +#define configMAX_TASK_NAME_LEN (10) +#define configUSE_16_BIT_TICKS 0 + +/* Software timer definitions. */ +#define configUSE_TIMERS 0 +#define configTIMER_TASK_PRIORITY 2 +#define configTIMER_QUEUE_LENGTH 5 +#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) + +/* Constants that build features in or out. */ +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_QUEUE_SETS 1 +#define configUSE_TASK_NOTIFICATIONS 1 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_TICKLESS_IDLE 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_NEWLIB_REENTRANT 0 +#define configUSE_CO_ROUTINES 0 + +/* Constants provided for debugging and optimisation assistance. */ +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configQUEUE_REGISTRY_SIZE 0 +#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } + +/* Constants that define which hook (callback) functions should be used. */ +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 +#define configUSE_MALLOC_FAILED_HOOK 0 + +/* Port specific configuration. */ +#define configENABLE_MPU 0 +#define configENABLE_FPU 1 +#define configENABLE_MVE 0 +#define configENABLE_TRUSTZONE 1 +#define configMINIMAL_SECURE_STACK_SIZE ((uint32_t)1024) +#define configRUN_FREERTOS_SECURE_ONLY 0 + +/* Cortex-M specific definitions. */ +#ifdef __NVIC_PRIO_BITS + /* __NVIC_PRIO_BITS will be specified when CMSIS is being used. */ + #define configPRIO_BITS __NVIC_PRIO_BITS +#else + /* 7 priority levels */ + #define configPRIO_BITS 3 +#endif + +/* The lowest interrupt priority that can be used in a call to a "set priority" function. */ +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x07 + +/* The highest interrupt priority that can be used by any interrupt service + * routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT + * CALL INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A + * HIGHER PRIORITY THAN THIS! (higher priorities are lower numeric values). */ +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 + +/* Interrupt priorities used by the kernel port layer itself. These are generic + * to all Cortex-M ports, and do not rely on any particular library functions. */ +#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) + +/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! + * See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) + +/* Set the following definitions to 1 to include the API function, or zero + * to exclude the API function. NOTE: Setting an INCLUDE_ parameter to 0 is + * only necessary if the linker does not automatically remove functions that are + * not referenced anyway. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_xTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xQueueGetMutexHolder 1 +#define INCLUDE_xSemaphoreGetMutexHolder 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_uxTaskGetStackHighWaterMark2 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 + +/* Map the FreeRTOS port interrupt handlers to their CMSIS standard names. */ +#define xPortPendSVHandler PendSV_Handler +#define vPortSVCHandler SVC_Handler +#define xPortSysTickHandler SysTick_Handler + +#if (defined(__ARMCC_VERSION) || defined(__GNUC__) || defined(__ICCARM__)) +/* Include debug event definitions */ +#include "freertos_evr.h" +#endif + +#endif /* FREERTOS_CONFIG_H */ diff --git a/RTE/RTOS/FreeRTOSConfig.h.base@10.4.0 b/RTE/RTOS/FreeRTOSConfig.h.base@10.4.0 new file mode 100644 index 0000000..feff4d0 --- /dev/null +++ b/RTE/RTOS/FreeRTOSConfig.h.base@10.4.0 @@ -0,0 +1,162 @@ +/* + * FreeRTOS Kernel V10.4.0 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html + *----------------------------------------------------------*/ + +#if (defined(__ARMCC_VERSION) || defined(__GNUC__) || defined(__ICCARM__)) +#include + +extern uint32_t SystemCoreClock; +#endif + +/* Constants that describe the hardware and memory usage. */ +#define configCPU_CLOCK_HZ (SystemCoreClock) +#define configTICK_RATE_HZ ((TickType_t)1000) +#define configTOTAL_HEAP_SIZE ((size_t)4096) +#define configMINIMAL_STACK_SIZE ((uint16_t)256) +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configSUPPORT_STATIC_ALLOCATION 0 + +/* Constants related to the behaviour or the scheduler. */ +#define configMAX_PRIORITIES 5 +#define configUSE_PREEMPTION 1 +#define configUSE_TIME_SLICING 1 +#define configIDLE_SHOULD_YIELD 1 +#define configMAX_TASK_NAME_LEN (10) +#define configUSE_16_BIT_TICKS 0 + +/* Software timer definitions. */ +#define configUSE_TIMERS 0 +#define configTIMER_TASK_PRIORITY 2 +#define configTIMER_QUEUE_LENGTH 5 +#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) + +/* Constants that build features in or out. */ +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_QUEUE_SETS 1 +#define configUSE_TASK_NOTIFICATIONS 1 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_TICKLESS_IDLE 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_NEWLIB_REENTRANT 0 +#define configUSE_CO_ROUTINES 0 + +/* Constants provided for debugging and optimisation assistance. */ +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configQUEUE_REGISTRY_SIZE 0 +#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } + +/* Constants that define which hook (callback) functions should be used. */ +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 +#define configUSE_MALLOC_FAILED_HOOK 0 + +/* Port specific configuration. */ +#define configENABLE_MPU 0 +#define configENABLE_FPU 1 +#define configENABLE_MVE 0 +#define configENABLE_TRUSTZONE 1 +#define configMINIMAL_SECURE_STACK_SIZE ((uint32_t)1024) +#define configRUN_FREERTOS_SECURE_ONLY 0 + +/* Cortex-M specific definitions. */ +#ifdef __NVIC_PRIO_BITS + /* __NVIC_PRIO_BITS will be specified when CMSIS is being used. */ + #define configPRIO_BITS __NVIC_PRIO_BITS +#else + /* 7 priority levels */ + #define configPRIO_BITS 3 +#endif + +/* The lowest interrupt priority that can be used in a call to a "set priority" function. */ +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x07 + +/* The highest interrupt priority that can be used by any interrupt service + * routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT + * CALL INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A + * HIGHER PRIORITY THAN THIS! (higher priorities are lower numeric values). */ +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 + +/* Interrupt priorities used by the kernel port layer itself. These are generic + * to all Cortex-M ports, and do not rely on any particular library functions. */ +#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) + +/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! + * See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) + +/* Set the following definitions to 1 to include the API function, or zero + * to exclude the API function. NOTE: Setting an INCLUDE_ parameter to 0 is + * only necessary if the linker does not automatically remove functions that are + * not referenced anyway. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_xTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xQueueGetMutexHolder 1 +#define INCLUDE_xSemaphoreGetMutexHolder 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_uxTaskGetStackHighWaterMark2 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 + +/* Map the FreeRTOS port interrupt handlers to their CMSIS standard names. */ +#define xPortPendSVHandler PendSV_Handler +#define vPortSVCHandler SVC_Handler +#define xPortSysTickHandler SysTick_Handler + +#if (defined(__ARMCC_VERSION) || defined(__GNUC__) || defined(__ICCARM__)) +/* Include debug event definitions */ +#include "freertos_evr.h" +#endif + +#endif /* FREERTOS_CONFIG_H */ diff --git a/RTE/_Target_1/RTE_Components.h b/RTE/_Target_1/RTE_Components.h new file mode 100644 index 0000000..fdd21e9 --- /dev/null +++ b/RTE/_Target_1/RTE_Components.h @@ -0,0 +1,45 @@ + +/* + * Auto generated Run-Time-Environment Configuration File + * *** Do not modify ! *** + * + * Project: 'Test_project_for_GD32107C-EVAL' + * Target: 'Target 1' + */ + +#ifndef RTE_COMPONENTS_H +#define RTE_COMPONENTS_H + + +/* + * Define the Device Header File: + */ +#define CMSIS_device_header "gd32f10x.h" + +/* ARM.FreeRTOS::RTOS:Config:FreeRTOS:10.5.1 */ +#define RTE_RTOS_FreeRTOS_CONFIG /* RTOS FreeRTOS Config for FreeRTOS API */ +/* ARM.FreeRTOS::RTOS:Core:Cortex-M:10.5.1 */ +#define RTE_RTOS_FreeRTOS_CORE /* RTOS FreeRTOS Core */ +/* ARM.FreeRTOS::RTOS:Heap:Heap_4:10.5.1 */ +#define RTE_RTOS_FreeRTOS_HEAP_4 /* RTOS FreeRTOS Heap 4 */ +/* GigaDevice::Device:GD32F10x_StdPeripherals:EXTI:2.0.2 */ +#define RTE_DEVICE_STDPERIPHERALS_EXTI +/* GigaDevice::Device:GD32F10x_StdPeripherals:GPIO:2.0.2 */ +#define RTE_DEVICE_STDPERIPHERALS_GPIO +/* GigaDevice::Device:GD32F10x_StdPeripherals:MISC:2.0.2 */ +#define RTE_DEVICE_STDPERIPHERALS_MISC +/* GigaDevice::Device:GD32F10x_StdPeripherals:PMU:2.0.2 */ +#define RTE_DEVICE_STDPERIPHERALS_PMU +/* GigaDevice::Device:GD32F10x_StdPeripherals:RCU:2.0.2 */ +#define RTE_DEVICE_STDPERIPHERALS_RCU +/* GigaDevice::Device:GD32F10x_StdPeripherals:USART:2.0.2 */ +#define RTE_DEVICE_STDPERIPHERALS_USART +/* Keil.ARM Compiler::Compiler:I/O:STDIN:User:1.2.0 */ +#define RTE_Compiler_IO_STDIN /* Compiler I/O: STDIN */ + #define RTE_Compiler_IO_STDIN_User /* Compiler I/O: STDIN User */ +/* Keil.ARM Compiler::Compiler:I/O:STDOUT:User:1.2.0 */ +#define RTE_Compiler_IO_STDOUT /* Compiler I/O: STDOUT */ + #define RTE_Compiler_IO_STDOUT_User /* Compiler I/O: STDOUT User */ + + +#endif /* RTE_COMPONENTS_H */ diff --git a/Test_project_for_GD32107C-EVAL.uvguix.right b/Test_project_for_GD32107C-EVAL.uvguix.right new file mode 100644 index 0000000..6e5a58d --- /dev/null +++ b/Test_project_for_GD32107C-EVAL.uvguix.right @@ -0,0 +1,3601 @@ + + + + -6.1 + +
### uVision Project, (C) Keil Software
+ + + D:\Users\right\Documents\Keil\Projects\Test_project_for_GD32107C-EVAL\FreeRTOS\source + + + + + + + 38003 + Registers + 194 195 + + + 346 + Code Coverage + 1404 160 + + + 204 + Performance Analyzer + 1564 + + + + + + 35141 + Event Statistics + + 200 50 700 + + + 1506 + Symbols + + 106 106 106 + + + 1936 + Watch 1 + + 200 133 133 + + + 1937 + Watch 2 + + 200 133 133 + + + 1935 + Call Stack + Locals + + 200 133 133 + + + 2506 + Trace Data + + 75 135 130 95 70 230 200 150 + + + 466 + Source Browser + 500 + 300 + + + + + + + + 0 + 0 + 0 + 50 + 16 + + + + + + + 44 + 2 + 3 + + -1 + -1 + + + -1 + -1 + + + 78 + 78 + 1358 + 739 + + + + 0 + + 60 + 010000000400000001000000010000000100000001000000000000000200000000000000010000000100000000000000280000002800000000000000 + + + + 0 + Build + + -1 + -1 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 440100005F0000006A0700001D010000 + + + 16 + 44010000810000006A0700003F010000 + + + + 1005 + 1005 + 1 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000008C01000041040000 + + + 16 + EF000000110100002F0200001E020000 + + + + 109 + 109 + 1 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000008C01000041040000 + + + 16 + EF000000110100006A02000009040000 + + + + 1465 + 1465 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 1466 + 1466 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 1467 + 1467 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 1468 + 1468 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 1506 + 1506 + 0 + 0 + 0 + 0 + 32767 + 0 + 16384 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 1913 + 1913 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 470100008300000067070000FA000000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 1935 + 1935 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF000000110100002F0200001E020000 + + + + 1936 + 1936 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF000000110100002F0200001E020000 + + + + 1937 + 1937 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF000000110100002F0200001E020000 + + + + 1939 + 1939 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 1940 + 1940 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 1941 + 1941 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 1942 + 1942 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 195 + 195 + 1 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000008C01000041040000 + + + 16 + EF000000110100006A02000009040000 + + + + 196 + 196 + 1 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000008C01000041040000 + + + 16 + EF000000110100006A02000009040000 + + + + 197 + 197 + 1 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + 0000000089040000000A000025050000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 198 + 198 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + 00000000BA0200006A07000099030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 199 + 199 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 030000008C040000FD09000002050000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 203 + 203 + 0 + 0 + 0 + 0 + 32767 + 0 + 8192 + 0 + + 16 + 470100008300000067070000FA000000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 204 + 204 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 470100008300000067070000FA000000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 221 + 221 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 00000000000000000000000000000000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 2506 + 2506 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 2507 + 2507 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 343 + 343 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 470100008300000067070000FA000000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 346 + 346 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 470100008300000067070000FA000000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 35141 + 35141 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 470100008300000067070000FA000000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35824 + 35824 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 470100008300000067070000FA000000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 35885 + 35885 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35886 + 35886 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35887 + 35887 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35888 + 35888 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35889 + 35889 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35890 + 35890 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35891 + 35891 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35892 + 35892 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35893 + 35893 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35894 + 35894 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35895 + 35895 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35896 + 35896 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35897 + 35897 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35898 + 35898 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35899 + 35899 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35900 + 35900 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35901 + 35901 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35902 + 35902 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35903 + 35903 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35904 + 35904 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 35905 + 35905 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 38003 + 38003 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000008C01000041040000 + + + 16 + EF000000110100006A02000009040000 + + + + 38007 + 38007 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 030000008C040000FD09000002050000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 436 + 436 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 030000008C040000FD09000002050000 + + + 16 + EF000000110100006A02000009040000 + + + + 437 + 437 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF000000110100002F0200001E020000 + + + + 440 + 440 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF000000110100002F0200001E020000 + + + + 463 + 463 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 030000008C040000FD09000002050000 + + + 16 + EF000000110100006A02000009040000 + + + + 466 + 466 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 030000008C040000FD09000002050000 + + + 16 + EF000000110100006A02000009040000 + + + + 470 + 470 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 470100008300000067070000FA000000 + + + 16 + EF00000011010000A4040000CF010000 + + + + 50000 + 50000 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50001 + 50001 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50002 + 50002 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50003 + 50003 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50004 + 50004 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50005 + 50005 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50006 + 50006 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50007 + 50007 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50008 + 50008 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50009 + 50009 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50010 + 50010 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50011 + 50011 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50012 + 50012 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50013 + 50013 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50014 + 50014 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50015 + 50015 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50016 + 50016 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50017 + 50017 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50018 + 50018 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 50019 + 50019 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + EF000000110100002F0200001E020000 + + + + 59392 + 59392 + 1 + 0 + 0 + 0 + 981 + 0 + 8192 + 0 + + 16 + 0000000000000000E003000020000000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 59393 + 0 + 1 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 0000000025050000000A000042050000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 59399 + 59399 + 1 + 0 + 0 + 0 + 481 + 0 + 8192 + 1 + + 16 + 0000000020000000EC01000040000000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 59400 + 59400 + 0 + 0 + 0 + 0 + 647 + 0 + 8192 + 2 + + 16 + 00000000400000009202000060000000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 824 + 824 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000DE0200006707000076030000 + + + 16 + EF000000110100002F0200001E020000 + + + + 3312 + 000000000B000000000000000020000000000000FFFFFFFFFFFFFFFF440100001D0100006A07000021010000000000000100000004000000010000000000000000000000FFFFFFFF08000000CB00000057010000CC000000F08B00005A01000079070000D601000045890000FFFF02000B004354616262656450616E65002000000000000044010000810000006A0700003F010000440100005F0000006A0700001D0100000000000040280046080000000B446973617373656D626C7900000000CB00000001000000FFFFFFFFFFFFFFFF14506572666F726D616E636520416E616C797A6572000000005701000001000000FFFFFFFFFFFFFFFF14506572666F726D616E636520416E616C797A657200000000CC00000001000000FFFFFFFFFFFFFFFF0E4C6F67696320416E616C797A657200000000F08B000001000000FFFFFFFFFFFFFFFF0D436F646520436F766572616765000000005A01000001000000FFFFFFFFFFFFFFFF11496E737472756374696F6E205472616365000000007907000001000000FFFFFFFFFFFFFFFF0F53797374656D20416E616C797A657200000000D601000001000000FFFFFFFFFFFFFFFF104576656E742053746174697374696373000000004589000001000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000001000000FFFFFFFFCB00000001000000FFFFFFFFCB000000000000000040000000000000FFFFFFFFFFFFFFFF260600005F0000002A060000D7020000000000000200000004000000010000000000000000000000FFFFFFFF2B000000E2050000CA0900002D8C00002E8C00002F8C0000308C0000318C0000328C0000338C0000348C0000358C0000368C0000378C0000388C0000398C00003A8C00003B8C00003C8C00003D8C00003E8C00003F8C0000408C0000418C000050C3000051C3000052C3000053C3000054C3000055C3000056C3000057C3000058C3000059C300005AC300005BC300005CC300005DC300005EC300005FC3000060C3000061C3000062C3000063C30000018000400000000000002A060000810000006A070000F90200002A0600005F0000006A070000D702000000000000404100462B0000000753796D626F6C7300000000E205000001000000FFFFFFFFFFFFFFFF0A5472616365204461746100000000CA09000001000000FFFFFFFFFFFFFFFF00000000002D8C000001000000FFFFFFFFFFFFFFFF00000000002E8C000001000000FFFFFFFFFFFFFFFF00000000002F8C000001000000FFFFFFFFFFFFFFFF0000000000308C000001000000FFFFFFFFFFFFFFFF0000000000318C000001000000FFFFFFFFFFFFFFFF0000000000328C000001000000FFFFFFFFFFFFFFFF0000000000338C000001000000FFFFFFFFFFFFFFFF0000000000348C000001000000FFFFFFFFFFFFFFFF0000000000358C000001000000FFFFFFFFFFFFFFFF0000000000368C000001000000FFFFFFFFFFFFFFFF0000000000378C000001000000FFFFFFFFFFFFFFFF0000000000388C000001000000FFFFFFFFFFFFFFFF0000000000398C000001000000FFFFFFFFFFFFFFFF00000000003A8C000001000000FFFFFFFFFFFFFFFF00000000003B8C000001000000FFFFFFFFFFFFFFFF00000000003C8C000001000000FFFFFFFFFFFFFFFF00000000003D8C000001000000FFFFFFFFFFFFFFFF00000000003E8C000001000000FFFFFFFFFFFFFFFF00000000003F8C000001000000FFFFFFFFFFFFFFFF0000000000408C000001000000FFFFFFFFFFFFFFFF0000000000418C000001000000FFFFFFFFFFFFFFFF000000000050C3000001000000FFFFFFFFFFFFFFFF000000000051C3000001000000FFFFFFFFFFFFFFFF000000000052C3000001000000FFFFFFFFFFFFFFFF000000000053C3000001000000FFFFFFFFFFFFFFFF000000000054C3000001000000FFFFFFFFFFFFFFFF000000000055C3000001000000FFFFFFFFFFFFFFFF000000000056C3000001000000FFFFFFFFFFFFFFFF000000000057C3000001000000FFFFFFFFFFFFFFFF000000000058C3000001000000FFFFFFFFFFFFFFFF000000000059C3000001000000FFFFFFFFFFFFFFFF00000000005AC3000001000000FFFFFFFFFFFFFFFF00000000005BC3000001000000FFFFFFFFFFFFFFFF00000000005CC3000001000000FFFFFFFFFFFFFFFF00000000005DC3000001000000FFFFFFFFFFFFFFFF00000000005EC3000001000000FFFFFFFFFFFFFFFF00000000005FC3000001000000FFFFFFFFFFFFFFFF000000000060C3000001000000FFFFFFFFFFFFFFFF000000000061C3000001000000FFFFFFFFFFFFFFFF000000000062C3000001000000FFFFFFFFFFFFFFFF000000000063C3000001000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000001000000FFFFFFFFE205000001000000FFFFFFFFE2050000000000000010000001000000FFFFFFFFFFFFFFFF8F0100005F0000009301000064040000010000000200001004000000010000000CFEFFFF01080000FFFFFFFF05000000ED0300006D000000C3000000C4000000739400000180001000000100000000000000810000008F01000086040000000000005F0000008F010000640400000000000040410056050000000750726F6A65637401000000ED03000001000000FFFFFFFFFFFFFFFF05426F6F6B73010000006D00000001000000FFFFFFFFFFFFFFFF0946756E6374696F6E7301000000C300000001000000FFFFFFFFFFFFFFFF0954656D706C6174657301000000C400000001000000FFFFFFFFFFFFFFFF09526567697374657273000000007394000001000000FFFFFFFFFFFFFFFF02000000000000000000000000000000000000000000000001000000FFFFFFFFED03000001000000FFFFFFFFED030000000000000080000000000000FFFFFFFFFFFFFFFF00000000B60200006A070000BA02000000000000010000000400000001000000000000000000000000000000000000000000000001000000C6000000FFFFFFFF0F0000008F070000930700009407000095070000960700009007000091070000B5010000B801000038030000B9050000BA050000BB050000BC050000CB0900000180008000000000000000000000DC0200006A070000BB03000000000000BA0200006A0700009903000000000000404100460F0000001343616C6C20537461636B202B204C6F63616C73000000008F07000001000000FFFFFFFFFFFFFFFF0755415254202331000000009307000001000000FFFFFFFFFFFFFFFF0755415254202332000000009407000001000000FFFFFFFFFFFFFFFF0755415254202333000000009507000001000000FFFFFFFFFFFFFFFF15446562756720287072696E74662920566965776572000000009607000001000000FFFFFFFFFFFFFFFF0757617463682031000000009007000001000000FFFFFFFFFFFFFFFF0757617463682032000000009107000001000000FFFFFFFFFFFFFFFF10547261636520457863657074696F6E7300000000B501000001000000FFFFFFFFFFFFFFFF0E4576656E7420436F756E7465727300000000B801000001000000FFFFFFFFFFFFFFFF09554C494E4B706C7573000000003803000001000000FFFFFFFFFFFFFFFF084D656D6F7279203100000000B905000001000000FFFFFFFFFFFFFFFF084D656D6F7279203200000000BA05000001000000FFFFFFFFFFFFFFFF084D656D6F7279203300000000BB05000001000000FFFFFFFFFFFFFFFF084D656D6F7279203400000000BC05000001000000FFFFFFFFFFFFFFFF105472616365204E617669676174696F6E00000000CB09000001000000FFFFFFFFFFFFFFFFFFFFFFFF0000000001000000000000000000000001000000FFFFFFFFB5030000BA020000B90300009903000000000000020000000400000000000000000000000000000000000000000000000000000002000000C6000000FFFFFFFF8F07000001000000FFFFFFFF8F07000001000000C6000000000000000080000001000000FFFFFFFFFFFFFFFF0000000064040000000A000068040000010000000100001004000000010000008AFCFFFF45010000FFFFFFFF06000000C5000000C7000000B4010000D2010000CF0100007794000001800080000001000000000000008A040000000A0000470500000000000068040000000A0000250500000000000040820056060000000C4275696C64204F757470757401000000C500000001000000FFFFFFFFFFFFFFFF0D46696E6420496E2046696C657300000000C700000001000000FFFFFFFFFFFFFFFF0A4572726F72204C69737400000000B401000001000000FFFFFFFFFFFFFFFF0E536F757263652042726F7773657200000000D201000001000000FFFFFFFFFFFFFFFF0E416C6C205265666572656E63657300000000CF01000001000000FFFFFFFFFFFFFFFF0742726F77736572000000007794000001000000FFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000001000000FFFFFFFFC500000001000000FFFFFFFFC5000000000000000000000000000000 + + + 59392 + File + + 2537 + 00200000010000002800FFFF01001100434D4643546F6F6C426172427574746F6E00E100000000000000000000000000000000000000000000000100000001000000018001E100000000000001000000000000000000000000000000000100000001000000018003E1000000000400020000000000000000000000000000000001000000010000000180CD7F0000000000000300000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018023E100000000040004000000000000000000000000000000000100000001000000018022E100000000040005000000000000000000000000000000000100000001000000018025E10000000004000600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001802BE10000000004000700000000000000000000000000000000010000000100000001802CE10000000004000800000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001807A8A0000000004000900000000000000000000000000000000010000000100000001807B8A0000000004000A00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180D3B00000000004000B000000000000000000000000000000000100000001000000018015B10000000004000C0000000000000000000000000000000001000000010000000180F4B00000000004000D000000000000000000000000000000000100000001000000018036B10000000004000E00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FF88000000000400460000000000000000000000000000000001000000010000000180FE880000000004004500000000000000000000000000000000010000000100000001800B810000000004001300000000000000000000000000000000010000000100000001800C810000000004001400000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180F0880000020000000F000000000000000000000000000000000100000001000000FFFF0100120043555646696E64436F6D626F427574746F6EE803000000000400000000000000000000000000000000000001000000010000009600000002002050FFFFFFFF0096000000000000000000018024E10000000000001100000000000000000000000000000000010000000100000001800A810000000004001200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000FFFF01001500434D4643546F6F6C4261724D656E75427574746F6E2280000002000000150000002153746172742F53746F70202644656275672053657373696F6E094374726C2B46350000000000000000000000000100000001000000000000000000000001000000020021802280000000000000150000002153746172742F53746F70202644656275672053657373696F6E094374726C2B4635000000000000000000000000010000000100000000000000000000000100000000002180E0010000000000007500000021456E65726779204D6561737572656D656E742026776974686F75742044656275670000000000000000000000000100000001000000000000000000000001000000000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C488000000000400160000000000000000000000000000000001000000010000000180C988000000000400180000000000000000000000000000000001000000010000000180C788000000000000190000000000000000000000000000000001000000010000002180C8880000000000001700000027264B696C6C20416C6C20427265616B706F696E747320696E2043757272656E7420546172676574000000000000000000000000010000000100000000000000000000000100000003002180C8880000000000001700000027264B696C6C20416C6C20427265616B706F696E747320696E2043757272656E7420546172676574000000000000000000000000010000000100000000000000000000000100000000002180E50100000000000078000000264B696C6C20416C6C20427265616B706F696E747320696E204163746976652050726F6A656374000000000000000000000000010000000100000000000000000000000100000000002180E601000000000000790000002F4B696C6C20416C6C20427265616B706F696E747320696E204D756C74692D50726F6A65637420576F726B73706163650000000000000000000000000100000001000000000000000000000001000000000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000021804C010000020001001A0000000F2650726F6A6563742057696E646F77000000000000000000000000010000000100000000000000000000000100000008002180DD880000000000001A0000000750726F6A656374000000000000000000000000010000000100000000000000000000000100000000002180DC8B0000000000003A00000005426F6F6B73000000000000000000000000010000000100000000000000000000000100000000002180E18B0000000000003B0000000946756E6374696F6E73000000000000000000000000010000000100000000000000000000000100000000002180E28B000000000000400000000954656D706C6174657300000000000000000000000001000000010000000000000000000000010000000000218018890000000000003D0000000E536F757263652042726F777365720000000000000000000000000100000001000000000000000000000001000000000021800000000000000400FFFFFFFF00000000000000000001000000000000000100000000000000000000000100000000002180D988000000000000390000000C4275696C64204F7574707574000000000000000000000000010000000100000000000000000000000100000000002180E38B000000000000410000000B46696E64204F75747075740000000000000000000000000100000001000000000000000000000001000000000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FB7F0000000000001B000000000000000000000000000000000100000001000000000000000446696C65D5030000 + + + 1423 + 2800FFFF01001100434D4643546F6F6C426172427574746F6E00E1000000000000FFFFFFFF000100000000000000010000000000000001000000018001E1000000000000FFFFFFFF000100000000000000010000000000000001000000018003E1000000000000FFFFFFFF0001000000000000000100000000000000010000000180CD7F000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF000000000000000000010000000000000001000000018023E1000000000000FFFFFFFF000100000000000000010000000000000001000000018022E1000000000000FFFFFFFF000100000000000000010000000000000001000000018025E1000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001802BE1000000000000FFFFFFFF00010000000000000001000000000000000100000001802CE1000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001807A8A000000000000FFFFFFFF00010000000000000001000000000000000100000001807B8A000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180D3B0000000000000FFFFFFFF000100000000000000010000000000000001000000018015B1000000000000FFFFFFFF0001000000000000000100000000000000010000000180F4B0000000000000FFFFFFFF000100000000000000010000000000000001000000018036B1000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180FF88000000000000FFFFFFFF0001000000000000000100000000000000010000000180FE88000000000000FFFFFFFF00010000000000000001000000000000000100000001800B81000000000000FFFFFFFF00010000000000000001000000000000000100000001800C81000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180F088000000000000FFFFFFFF0001000000000000000100000000000000010000000180EE7F000000000000FFFFFFFF000100000000000000010000000000000001000000018024E1000000000000FFFFFFFF00010000000000000001000000000000000100000001800A81000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001802280000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180C488000000000000FFFFFFFF0001000000000000000100000000000000010000000180C988000000000000FFFFFFFF0001000000000000000100000000000000010000000180C788000000000000FFFFFFFF0001000000000000000100000000000000010000000180C888000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180DD88000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180FB7F000000000000FFFFFFFF000100000000000000010000000000000001000000 + + + 1423 + 2800FFFF01001100434D4643546F6F6C426172427574746F6E00E100000000000000000000000000000000000000000000000100000001000000018001E100000000000001000000000000000000000000000000000100000001000000018003E1000000000000020000000000000000000000000000000001000000010000000180CD7F0000000000000300000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018023E100000000000004000000000000000000000000000000000100000001000000018022E100000000000005000000000000000000000000000000000100000001000000018025E10000000000000600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001802BE10000000000000700000000000000000000000000000000010000000100000001802CE10000000000000800000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001807A8A0000000000000900000000000000000000000000000000010000000100000001807B8A0000000000000A00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180D3B00000000000000B000000000000000000000000000000000100000001000000018015B10000000000000C0000000000000000000000000000000001000000010000000180F4B00000000000000D000000000000000000000000000000000100000001000000018036B10000000000000E00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FF880000000000000F0000000000000000000000000000000001000000010000000180FE880000000000001000000000000000000000000000000000010000000100000001800B810000000000001100000000000000000000000000000000010000000100000001800C810000000000001200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180F088000000000000130000000000000000000000000000000001000000010000000180EE7F00000000000014000000000000000000000000000000000100000001000000018024E10000000000001500000000000000000000000000000000010000000100000001800A810000000000001600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018022800000000000001700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C488000000000000180000000000000000000000000000000001000000010000000180C988000000000000190000000000000000000000000000000001000000010000000180C7880000000000001A0000000000000000000000000000000001000000010000000180C8880000000000001B00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180DD880000000000001C00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FB7F0000000000001D000000000000000000000000000000000100000001000000 + + + + 59399 + Build + + 976 + 00200000010000001000FFFF01001100434D4643546F6F6C426172427574746F6ECF7F0000000004001C0000000000000000000000000000000001000000010000000180D07F0000000000001D000000000000000000000000000000000100000001000000018030800000000000001E000000000000000000000000000000000100000001000000FFFF01001500434D4643546F6F6C4261724D656E75427574746F6EC7040000000000006A0000000C4261746368204275696C2664000000000000000000000000010000000100000000000000000000000100000004000580C7040000000000006A0000000C4261746368204275696C266400000000000000000000000001000000010000000000000000000000010000000000058046070000000000006B0000000D42617463682052656275696C640000000000000000000000000100000001000000000000000000000001000000000005804707000000000000FFFFFFFF0B426174636820436C65616E0100000000000000000000000100000001000000000000000000000001000000000005809E8A0000000000001F0000000F4261746326682053657475702E2E2E000000000000000000000000010000000100000000000000000000000100000000000180D17F0000000004002000000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001804C8A0000000000002100000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000FFFF01001900434D4643546F6F6C426172436F6D626F426F78427574746F6EBA000000000000000000000000000000000000000000000000010000000100000096000000030020500000000008546172676574203196000000000000000100085461726765742031000000000180EB880000000000002200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C07F000000000000230000000000000000000000000000000001000000010000000180B08A000000000400240000000000000000000000000000000001000000010000000180A8010000000000004E00000000000000000000000000000000010000000100000001807202000000000000530000000000000000000000000000000001000000010000000180BE010000000000005000000000000000000000000000000000010000000100000000000000054275696C64E1010000 + + + 583 + 1000FFFF01001100434D4643546F6F6C426172427574746F6ECF7F000000000000FFFFFFFF0001000000000000000100000000000000010000000180D07F000000000000FFFFFFFF00010000000000000001000000000000000100000001803080000000000000FFFFFFFF00010000000000000001000000000000000100000001809E8A000000000000FFFFFFFF0001000000000000000100000000000000010000000180D17F000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001804C8A000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001806680000000000000FFFFFFFF0001000000000000000100000000000000010000000180EB88000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180C07F000000000000FFFFFFFF0001000000000000000100000000000000010000000180B08A000000000000FFFFFFFF0001000000000000000100000000000000010000000180A801000000000000FFFFFFFF00010000000000000001000000000000000100000001807202000000000000FFFFFFFF0001000000000000000100000000000000010000000180BE01000000000000FFFFFFFF000100000000000000010000000000000001000000 + + + 583 + 1000FFFF01001100434D4643546F6F6C426172427574746F6ECF7F000000000000000000000000000000000000000000000001000000010000000180D07F00000000000001000000000000000000000000000000000100000001000000018030800000000000000200000000000000000000000000000000010000000100000001809E8A000000000000030000000000000000000000000000000001000000010000000180D17F0000000000000400000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001804C8A0000000000000500000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001806680000000000000060000000000000000000000000000000001000000010000000180EB880000000000000700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C07F000000000000080000000000000000000000000000000001000000010000000180B08A000000000000090000000000000000000000000000000001000000010000000180A8010000000000000A000000000000000000000000000000000100000001000000018072020000000000000B0000000000000000000000000000000001000000010000000180BE010000000000000C000000000000000000000000000000000100000001000000 + + + + 59400 + Debug + + 2373 + 00200000000000001900FFFF01001100434D4643546F6F6C426172427574746F6ECC880000000000002500000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018017800000000000002600000000000000000000000000000000010000000100000001801D800000000000002700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001801A800000000000002800000000000000000000000000000000010000000100000001801B80000000000000290000000000000000000000000000000001000000010000000180E57F0000000000002A00000000000000000000000000000000010000000100000001801C800000000000002B00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018000890000000000002C00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180E48B0000000000002D0000000000000000000000000000000001000000010000000180F07F0000000000002E0000000000000000000000000000000001000000010000000180E8880000000000003700000000000000000000000000000000010000000100000001803B010000000000002F0000000000000000000000000000000001000000010000000180BB8A00000000000030000000000000000000000000000000000100000001000000FFFF01001500434D4643546F6F6C4261724D656E75427574746F6E0E01000000000000310000000D57617463682057696E646F7773000000000000000000000000010000000100000000000000000000000100000003001380D88B00000000000031000000085761746368202631000000000000000000000000010000000100000000000000000000000100000000001380D98B00000000000031000000085761746368202632000000000000000000000000010000000100000000000000000000000100000000001380CE01000000000000FFFFFFFF0C576174636820416E63686F720000000000000000010000000000000001000000000000000000000001000000000013800F01000000000000320000000E4D656D6F72792057696E646F7773000000000000000000000000010000000100000000000000000000000100000004001380D28B00000000000032000000094D656D6F7279202631000000000000000000000000010000000100000000000000000000000100000000001380D38B00000000000032000000094D656D6F7279202632000000000000000000000000010000000100000000000000000000000100000000001380D48B00000000000032000000094D656D6F7279202633000000000000000000000000010000000100000000000000000000000100000000001380D58B00000000000032000000094D656D6F72792026340000000000000000000000000100000001000000000000000000000001000000000013801001000000000000330000000E53657269616C2057696E646F77730000000000000000000000000100000001000000000000000000000001000000040013809307000000000000330000000855415254202326310000000000000000000000000100000001000000000000000000000001000000000013809407000000000000330000000855415254202326320000000000000000000000000100000001000000000000000000000001000000000013809507000000000000330000000855415254202326330000000000000000000000000100000001000000000000000000000001000000000013809607000000000000330000001626446562756720287072696E746629205669657765720000000000000000000000000100000001000000000000000000000001000000000013803C010000000000007200000010416E616C797369732057696E646F7773000000000000000000000000010000000100000000000000000000000100000004001380658A000000000000340000000F264C6F67696320416E616C797A6572000000000000000000000000010000000100000000000000000000000100000000001380DC7F0000000000003E0000001526506572666F726D616E636520416E616C797A6572000000000000000000000000010000000100000000000000000000000100000000001380E788000000000000380000000E26436F646520436F766572616765000000000000000000000000010000000100000000000000000000000100000000001380CD01000000000000FFFFFFFF0F416E616C7973697320416E63686F7200000000000000000100000000000000010000000000000000000000010000000000138053010000000000003F0000000D54726163652057696E646F77730000000000000000000000000100000001000000000000000000000001000000010013805401000000000000FFFFFFFF115472616365204D656E7520416E63686F720000000000000000010000000000000001000000000000000000000001000000000013802901000000000000350000001553797374656D205669657765722057696E646F77730000000000000000000000000100000001000000000000000000000001000000010013804B01000000000000FFFFFFFF1453797374656D2056696577657220416E63686F720000000000000000010000000000000001000000000000000000000001000000000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000013800189000000000000360000000F26546F6F6C626F782057696E646F7700000000000000000000000001000000010000000000000000000000010000000300138044C5000000000000FFFFFFFF0E5570646174652057696E646F77730000000000000000010000000000000001000000000000000000000001000000000013800000000000000400FFFFFFFF000000000000000000010000000000000001000000000000000000000001000000000013805B01000000000000FFFFFFFF12546F6F6C626F78204D656E75416E63686F72000000000000000001000000000000000100000000000000000000000100000000000000000005446562756787020000 + + + 898 + 1900FFFF01001100434D4643546F6F6C426172427574746F6ECC88000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001801780000000000000FFFFFFFF00010000000000000001000000000000000100000001801D80000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001801A80000000000000FFFFFFFF00010000000000000001000000000000000100000001801B80000000000000FFFFFFFF0001000000000000000100000000000000010000000180E57F000000000000FFFFFFFF00010000000000000001000000000000000100000001801C80000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001800089000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180E48B000000000000FFFFFFFF0001000000000000000100000000000000010000000180F07F000000000000FFFFFFFF0001000000000000000100000000000000010000000180E888000000000000FFFFFFFF00010000000000000001000000000000000100000001803B01000000000000FFFFFFFF0001000000000000000100000000000000010000000180BB8A000000000000FFFFFFFF0001000000000000000100000000000000010000000180D88B000000000000FFFFFFFF0001000000000000000100000000000000010000000180D28B000000000000FFFFFFFF00010000000000000001000000000000000100000001809307000000000000FFFFFFFF0001000000000000000100000000000000010000000180658A000000000000FFFFFFFF0001000000000000000100000000000000010000000180C18A000000000000FFFFFFFF0001000000000000000100000000000000010000000180EE8B000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001800189000000000000FFFFFFFF000100000000000000010000000000000001000000 + + + 898 + 1900FFFF01001100434D4643546F6F6C426172427574746F6ECC880000000000000000000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018017800000000000000100000000000000000000000000000000010000000100000001801D800000000000000200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001801A800000000000000300000000000000000000000000000000010000000100000001801B80000000000000040000000000000000000000000000000001000000010000000180E57F0000000000000500000000000000000000000000000000010000000100000001801C800000000000000600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018000890000000000000700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180E48B000000000000080000000000000000000000000000000001000000010000000180F07F000000000000090000000000000000000000000000000001000000010000000180E8880000000000000A00000000000000000000000000000000010000000100000001803B010000000000000B0000000000000000000000000000000001000000010000000180BB8A0000000000000C0000000000000000000000000000000001000000010000000180D88B0000000000000D0000000000000000000000000000000001000000010000000180D28B0000000000000E000000000000000000000000000000000100000001000000018093070000000000000F0000000000000000000000000000000001000000010000000180658A000000000000100000000000000000000000000000000001000000010000000180C18A000000000000110000000000000000000000000000000001000000010000000180EE8B0000000000001200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180018900000000000013000000000000000000000000000000000100000001000000 + + + + 0 + 2560 + 1440 + + + + 1 + Debug + + -1 + -1 + 1 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 440100005F000000DF060000EB010000 + + + 16 + 4401000081000000DF0600000D020000 + + + + 1005 + 1005 + 1 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000003D0100001F040000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 109 + 109 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000003D0100001F040000 + + + 16 + 3B0100005D010000B602000055040000 + + + + 1465 + 1465 + 1 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1466 + 1466 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1467 + 1467 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1468 + 1468 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1506 + 1506 + 0 + 0 + 0 + 0 + 32767 + 0 + 16384 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 1913 + 1913 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 4701000083000000DC060000C8010000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 1935 + 1935 + 1 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1936 + 1936 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1937 + 1937 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1939 + 1939 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1940 + 1940 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1941 + 1941 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 1942 + 1942 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 195 + 195 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000003D0100001F040000 + + + 16 + 3B0100005D010000B602000055040000 + + + + 196 + 196 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000003D0100001F040000 + + + 16 + 3B0100005D010000B602000055040000 + + + + 197 + 197 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + 03000000FF0200006707000076030000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 198 + 198 + 1 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + 0000000046040000DF06000025050000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 199 + 199 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000FF0200006707000076030000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 203 + 203 + 1 + 0 + 0 + 0 + 32767 + 0 + 8192 + 0 + + 16 + 4401000080000000DF060000EB010000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 204 + 204 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 4701000083000000DC060000C8010000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 221 + 221 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 00000000000000000000000000000000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 2506 + 2506 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 2507 + 2507 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 343 + 343 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 4701000083000000DC060000C8010000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 346 + 346 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 4701000083000000DC060000C8010000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 35141 + 35141 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 4701000083000000DC060000C8010000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35824 + 35824 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 4701000083000000DC060000C8010000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 35885 + 35885 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35886 + 35886 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35887 + 35887 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35888 + 35888 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35889 + 35889 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35890 + 35890 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35891 + 35891 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35892 + 35892 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35893 + 35893 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35894 + 35894 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35895 + 35895 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35896 + 35896 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35897 + 35897 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35898 + 35898 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35899 + 35899 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35900 + 35900 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35901 + 35901 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35902 + 35902 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35903 + 35903 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35904 + 35904 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 35905 + 35905 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 38003 + 38003 + 1 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000830000003D0100001F040000 + + + 16 + 3B0100005D010000B602000055040000 + + + + 38007 + 38007 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000FF0200006707000076030000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 436 + 436 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000FF0200006707000076030000 + + + 16 + 3B0100005D010000B602000055040000 + + + + 437 + 437 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 440 + 440 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 463 + 463 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000FF0200006707000076030000 + + + 16 + 3B0100005D010000B602000055040000 + + + + 466 + 466 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 03000000FF0200006707000076030000 + + + 16 + 3B0100005D010000B602000055040000 + + + + 470 + 470 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 4701000083000000DC060000C8010000 + + + 16 + 3B0100005D010000F00400001B020000 + + + + 50000 + 50000 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50001 + 50001 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50002 + 50002 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50003 + 50003 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50004 + 50004 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50005 + 50005 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50006 + 50006 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50007 + 50007 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50008 + 50008 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50009 + 50009 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50010 + 50010 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50011 + 50011 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50012 + 50012 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50013 + 50013 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50014 + 50014 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50015 + 50015 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50016 + 50016 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50017 + 50017 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50018 + 50018 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 50019 + 50019 + 0 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 2D0600008300000067070000B4020000 + + + 16 + 3B0100005D0100007B0200006A020000 + + + + 59392 + 59392 + 1 + 0 + 0 + 0 + 981 + 0 + 8192 + 0 + + 16 + 0000000000000000E003000020000000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 59393 + 0 + 1 + 0 + 0 + 0 + 32767 + 0 + 4096 + 0 + + 16 + 0000000025050000000A000042050000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 59399 + 59399 + 0 + 0 + 0 + 0 + 481 + 0 + 8192 + 1 + + 16 + 0000000020000000EC01000040000000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 59400 + 59400 + 1 + 0 + 0 + 0 + 647 + 0 + 8192 + 2 + + 16 + 00000000200000009202000040000000 + + + 16 + 0A0000000A0000006E0000006E000000 + + + + 824 + 824 + 0 + 0 + 0 + 0 + 32767 + 0 + 32768 + 0 + + 16 + E606000083000000FD09000002050000 + + + 16 + 31080000D10200002D0D0000B0030000 + + + + 3415 + 000000000C000000000000000020000001000000FFFFFFFFFFFFFFFF44010000EB010000DF060000EF0100000100000001000010040000000100000044FFFFFF1C030000FFFFFFFF08000000CB00000057010000CC000000F08B00005A01000079070000D601000045890000FFFF02000B004354616262656450616E6500200000010000004401000081000000DF0600000D020000440100005F000000DF060000EB0100000000000040280056080000000B446973617373656D626C7901000000CB00000001000000FFFFFFFFFFFFFFFF14506572666F726D616E636520416E616C797A6572000000005701000001000000FFFFFFFFFFFFFFFF14506572666F726D616E636520416E616C797A657200000000CC00000001000000FFFFFFFFFFFFFFFF0E4C6F67696320416E616C797A657200000000F08B000001000000FFFFFFFFFFFFFFFF0D436F646520436F766572616765000000005A01000001000000FFFFFFFFFFFFFFFF11496E737472756374696F6E205472616365000000007907000001000000FFFFFFFFFFFFFFFF0F53797374656D20416E616C797A657200000000D601000001000000FFFFFFFFFFFFFFFF104576656E742053746174697374696373000000004589000001000000FFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000001000000FFFFFFFFCB00000001000000FFFFFFFFCB000000000000000040000000000000FFFFFFFFFFFFFFFF260600005F0000002A060000D7020000000000000200000004000000010000000000000000000000FFFFFFFF2B000000E2050000CA0900002D8C00002E8C00002F8C0000308C0000318C0000328C0000338C0000348C0000358C0000368C0000378C0000388C0000398C00003A8C00003B8C00003C8C00003D8C00003E8C00003F8C0000408C0000418C000050C3000051C3000052C3000053C3000054C3000055C3000056C3000057C3000058C3000059C300005AC300005BC300005CC300005DC300005EC300005FC3000060C3000061C3000062C3000063C30000018000400000000000002A060000810000006A070000F90200002A0600005F0000006A070000D702000000000000404100462B0000000753796D626F6C7300000000E205000001000000FFFFFFFFFFFFFFFF0A5472616365204461746100000000CA09000001000000FFFFFFFFFFFFFFFF00000000002D8C000001000000FFFFFFFFFFFFFFFF00000000002E8C000001000000FFFFFFFFFFFFFFFF00000000002F8C000001000000FFFFFFFFFFFFFFFF0000000000308C000001000000FFFFFFFFFFFFFFFF0000000000318C000001000000FFFFFFFFFFFFFFFF0000000000328C000001000000FFFFFFFFFFFFFFFF0000000000338C000001000000FFFFFFFFFFFFFFFF0000000000348C000001000000FFFFFFFFFFFFFFFF0000000000358C000001000000FFFFFFFFFFFFFFFF0000000000368C000001000000FFFFFFFFFFFFFFFF0000000000378C000001000000FFFFFFFFFFFFFFFF0000000000388C000001000000FFFFFFFFFFFFFFFF0000000000398C000001000000FFFFFFFFFFFFFFFF00000000003A8C000001000000FFFFFFFFFFFFFFFF00000000003B8C000001000000FFFFFFFFFFFFFFFF00000000003C8C000001000000FFFFFFFFFFFFFFFF00000000003D8C000001000000FFFFFFFFFFFFFFFF00000000003E8C000001000000FFFFFFFFFFFFFFFF00000000003F8C000001000000FFFFFFFFFFFFFFFF0000000000408C000001000000FFFFFFFFFFFFFFFF0000000000418C000001000000FFFFFFFFFFFFFFFF000000000050C3000001000000FFFFFFFFFFFFFFFF000000000051C3000001000000FFFFFFFFFFFFFFFF000000000052C3000001000000FFFFFFFFFFFFFFFF000000000053C3000001000000FFFFFFFFFFFFFFFF000000000054C3000001000000FFFFFFFFFFFFFFFF000000000055C3000001000000FFFFFFFFFFFFFFFF000000000056C3000001000000FFFFFFFFFFFFFFFF000000000057C3000001000000FFFFFFFFFFFFFFFF000000000058C3000001000000FFFFFFFFFFFFFFFF000000000059C3000001000000FFFFFFFFFFFFFFFF00000000005AC3000001000000FFFFFFFFFFFFFFFF00000000005BC3000001000000FFFFFFFFFFFFFFFF00000000005CC3000001000000FFFFFFFFFFFFFFFF00000000005DC3000001000000FFFFFFFFFFFFFFFF00000000005EC3000001000000FFFFFFFFFFFFFFFF00000000005FC3000001000000FFFFFFFFFFFFFFFF000000000060C3000001000000FFFFFFFFFFFFFFFF000000000061C3000001000000FFFFFFFFFFFFFFFF000000000062C3000001000000FFFFFFFFFFFFFFFF000000000063C3000001000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000001000000FFFFFFFFE205000001000000FFFFFFFFE2050000000000000010000001000000FFFFFFFFFFFFFFFF400100005F0000004401000042040000010000000200001004000000010000000000000000000000FFFFFFFF05000000ED0300006D000000C3000000C4000000739400000180001000000100000000000000810000004001000064040000000000005F00000040010000420400000000000040410056050000000750726F6A65637401000000ED03000001000000FFFFFFFFFFFFFFFF05426F6F6B73000000006D00000001000000FFFFFFFFFFFFFFFF0946756E6374696F6E7300000000C300000001000000FFFFFFFFFFFFFFFF0954656D706C6174657300000000C400000001000000FFFFFFFFFFFFFFFF09526567697374657273010000007394000001000000FFFFFFFFFFFFFFFF04000000000000000000000000000000000000000000000001000000FFFFFFFFED03000001000000FFFFFFFFED030000000000000080000001000000FFFFFFFFFFFFFFFF0000000042040000DF0600004604000001000000010000100400000001000000000000000000000000000000000000000000000001000000C60000000000000001000000000000000000000001000000FFFFFFFF0005000046040000040500002505000000000000020000000400000000000000000000000000000000000000000000000000000001000000C600000001000000C6000000000000000080000000000000FFFFFFFFFFFFFFFF00000000D70200006A070000DB020000000000000100000004000000010000000000000000000000FFFFFFFF06000000C5000000C7000000B4010000D2010000CF010000779400000180008000000000000000000000FD0200006A070000BB03000000000000DB0200006A070000990300000000000040820046060000000C4275696C64204F757470757400000000C500000001000000FFFFFFFFFFFFFFFF0D46696E6420496E2046696C657300000000C700000001000000FFFFFFFFFFFFFFFF0A4572726F72204C69737400000000B401000001000000FFFFFFFFFFFFFFFF0E536F757263652042726F7773657200000000D201000001000000FFFFFFFFFFFFFFFF0E416C6C205265666572656E63657300000000CF01000001000000FFFFFFFFFFFFFFFF0642726F777365000000007794000001000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000001000000FFFFFFFFC500000001000000FFFFFFFFC5000000000000000040000001000000FFFFFFFFFFFFFFFFDF0600005F000000E30600002505000001000000020000100400000001000000ABFBFFFF5C04000000000000000000000000000001000000FFFFFFFF0F0000008F070000930700009407000095070000960700009007000091070000B5010000B801000038030000B9050000BA050000BB050000BC050000CB09000001800040000001000000E306000081000000000A000047050000E30600005F000000000A00002505000000000000404100560F0000001343616C6C20537461636B202B204C6F63616C73010000008F07000001000000FFFFFFFFFFFFFFFF0755415254202331000000009307000001000000FFFFFFFFFFFFFFFF0755415254202332000000009407000001000000FFFFFFFFFFFFFFFF0755415254202333000000009507000001000000FFFFFFFFFFFFFFFF15446562756720287072696E74662920566965776572000000009607000001000000FFFFFFFFFFFFFFFF0757617463682031000000009007000001000000FFFFFFFFFFFFFFFF0757617463682032000000009107000001000000FFFFFFFFFFFFFFFF10547261636520457863657074696F6E7300000000B501000001000000FFFFFFFFFFFFFFFF0E4576656E7420436F756E7465727300000000B801000001000000FFFFFFFFFFFFFFFF09554C494E4B706C7573000000003803000001000000FFFFFFFFFFFFFFFF084D656D6F7279203101000000B905000001000000FFFFFFFFFFFFFFFF084D656D6F7279203200000000BA05000001000000FFFFFFFFFFFFFFFF084D656D6F7279203300000000BB05000001000000FFFFFFFFFFFFFFFF084D656D6F7279203400000000BC05000001000000FFFFFFFFFFFFFFFF105472616365204E617669676174696F6E00000000CB09000001000000FFFFFFFFFFFFFFFF0A00000000000000000000000000000000000000000000000000000001000000FFFFFFFF8F07000001000000FFFFFFFF8F070000000000000000000000000000 + + + 59392 + File + + 2537 + 00200000010000002800FFFF01001100434D4643546F6F6C426172427574746F6E00E100000000000000000000000000000000000000000000000100000001000000018001E100000000000001000000000000000000000000000000000100000001000000018003E1000000000000020000000000000000000000000000000001000000010000000180CD7F0000000000000300000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018023E100000000040004000000000000000000000000000000000100000001000000018022E100000000040005000000000000000000000000000000000100000001000000018025E10000000000000600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001802BE10000000000000700000000000000000000000000000000010000000100000001802CE10000000004000800000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001807A8A0000000000000900000000000000000000000000000000010000000100000001807B8A0000000004000A00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180D3B00000000000000B000000000000000000000000000000000100000001000000018015B10000000004000C0000000000000000000000000000000001000000010000000180F4B00000000004000D000000000000000000000000000000000100000001000000018036B10000000004000E00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FF88000000000400460000000000000000000000000000000001000000010000000180FE880000000004004500000000000000000000000000000000010000000100000001800B810000000004001300000000000000000000000000000000010000000100000001800C810000000004001400000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180F0880000020000000F000000000000000000000000000000000100000001000000FFFF0100120043555646696E64436F6D626F427574746F6EE803000000000000000000000000000000000000000000000001000000010000009600000002002050FFFFFFFF0096000000000000000000018024E10000000000001100000000000000000000000000000000010000000100000001800A810000000000001200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000FFFF01001500434D4643546F6F6C4261724D656E75427574746F6E2280000002000100150000002153746172742F53746F70202644656275672053657373696F6E094374726C2B46350000000000000000000000000100000001000000000000000000000001000000020021802280000000000000150000002153746172742F53746F70202644656275672053657373696F6E094374726C2B4635000000000000000000000000010000000100000000000000000000000100000000002180E0010000000000007500000021456E65726779204D6561737572656D656E742026776974686F75742044656275670000000000000000000000000100000001000000000000000000000001000000000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C488000000000000160000000000000000000000000000000001000000010000000180C988000000000400180000000000000000000000000000000001000000010000000180C788000000000000190000000000000000000000000000000001000000010000002180C8880000000000001700000027264B696C6C20416C6C20427265616B706F696E747320696E2043757272656E7420546172676574000000000000000000000000010000000100000000000000000000000100000003002180C8880000000000001700000027264B696C6C20416C6C20427265616B706F696E747320696E2043757272656E7420546172676574000000000000000000000000010000000100000000000000000000000100000000002180E50100000000000078000000264B696C6C20416C6C20427265616B706F696E747320696E204163746976652050726F6A656374000000000000000000000000010000000100000000000000000000000100000000002180E601000000000000790000002F4B696C6C20416C6C20427265616B706F696E747320696E204D756C74692D50726F6A65637420576F726B73706163650000000000000000000000000100000001000000000000000000000001000000000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000021804C010000020001001A0000000F2650726F6A6563742057696E646F77000000000000000000000000010000000100000000000000000000000100000008002180DD880000000000001A0000000750726F6A656374000000000000000000000000010000000100000000000000000000000100000000002180DC8B0000000000003A00000005426F6F6B73000000000000000000000000010000000100000000000000000000000100000000002180E18B0000000000003B0000000946756E6374696F6E73000000000000000000000000010000000100000000000000000000000100000000002180E28B000000000000400000000954656D706C6174657300000000000000000000000001000000010000000000000000000000010000000000218018890000000000003D0000000E536F757263652042726F777365720000000000000000000000000100000001000000000000000000000001000000000021800000000000000400FFFFFFFF00000000000000000001000000000000000100000000000000000000000100000000002180D988000000000000390000000C4275696C64204F7574707574000000000000000000000000010000000100000000000000000000000100000000002180E38B000000000000410000000B46696E64204F75747075740000000000000000000000000100000001000000000000000000000001000000000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FB7F0000000000001B000000000000000000000000000000000100000001000000000000000446696C65D5030000 + + + 1423 + 2800FFFF01001100434D4643546F6F6C426172427574746F6E00E1000000000000FFFFFFFF000100000000000000010000000000000001000000018001E1000000000000FFFFFFFF000100000000000000010000000000000001000000018003E1000000000000FFFFFFFF0001000000000000000100000000000000010000000180CD7F000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF000000000000000000010000000000000001000000018023E1000000000000FFFFFFFF000100000000000000010000000000000001000000018022E1000000000000FFFFFFFF000100000000000000010000000000000001000000018025E1000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001802BE1000000000000FFFFFFFF00010000000000000001000000000000000100000001802CE1000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001807A8A000000000000FFFFFFFF00010000000000000001000000000000000100000001807B8A000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180D3B0000000000000FFFFFFFF000100000000000000010000000000000001000000018015B1000000000000FFFFFFFF0001000000000000000100000000000000010000000180F4B0000000000000FFFFFFFF000100000000000000010000000000000001000000018036B1000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180FF88000000000000FFFFFFFF0001000000000000000100000000000000010000000180FE88000000000000FFFFFFFF00010000000000000001000000000000000100000001800B81000000000000FFFFFFFF00010000000000000001000000000000000100000001800C81000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180F088000000000000FFFFFFFF0001000000000000000100000000000000010000000180EE7F000000000000FFFFFFFF000100000000000000010000000000000001000000018024E1000000000000FFFFFFFF00010000000000000001000000000000000100000001800A81000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001802280000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180C488000000000000FFFFFFFF0001000000000000000100000000000000010000000180C988000000000000FFFFFFFF0001000000000000000100000000000000010000000180C788000000000000FFFFFFFF0001000000000000000100000000000000010000000180C888000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180DD88000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180FB7F000000000000FFFFFFFF000100000000000000010000000000000001000000 + + + 1423 + 2800FFFF01001100434D4643546F6F6C426172427574746F6E00E100000000000000000000000000000000000000000000000100000001000000018001E100000000000001000000000000000000000000000000000100000001000000018003E1000000000000020000000000000000000000000000000001000000010000000180CD7F0000000000000300000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018023E100000000000004000000000000000000000000000000000100000001000000018022E100000000000005000000000000000000000000000000000100000001000000018025E10000000000000600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001802BE10000000000000700000000000000000000000000000000010000000100000001802CE10000000000000800000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001807A8A0000000000000900000000000000000000000000000000010000000100000001807B8A0000000000000A00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180D3B00000000000000B000000000000000000000000000000000100000001000000018015B10000000000000C0000000000000000000000000000000001000000010000000180F4B00000000000000D000000000000000000000000000000000100000001000000018036B10000000000000E00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FF880000000000000F0000000000000000000000000000000001000000010000000180FE880000000000001000000000000000000000000000000000010000000100000001800B810000000000001100000000000000000000000000000000010000000100000001800C810000000000001200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180F088000000000000130000000000000000000000000000000001000000010000000180EE7F00000000000014000000000000000000000000000000000100000001000000018024E10000000000001500000000000000000000000000000000010000000100000001800A810000000000001600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018022800000000000001700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C488000000000000180000000000000000000000000000000001000000010000000180C988000000000000190000000000000000000000000000000001000000010000000180C7880000000000001A0000000000000000000000000000000001000000010000000180C8880000000000001B00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180DD880000000000001C00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FB7F0000000000001D000000000000000000000000000000000100000001000000 + + + + 59399 + Build + + 955 + 00200000000000001000FFFF01001100434D4643546F6F6C426172427574746F6ECF7F0000000000001C0000000000000000000000000000000001000000010000000180D07F0000000000001D000000000000000000000000000000000100000001000000018030800000000000001E000000000000000000000000000000000100000001000000FFFF01001500434D4643546F6F6C4261724D656E75427574746F6EC7040000000000006A0000000C4261746368204275696C2664000000000000000000000000010000000100000000000000000000000100000004000580C7040000000000006A0000000C4261746368204275696C266400000000000000000000000001000000010000000000000000000000010000000000058046070000000000006B0000000D42617463682052656275696C640000000000000000000000000100000001000000000000000000000001000000000005804707000000000000FFFFFFFF0B426174636820436C65616E0100000000000000010000000000000001000000000000000000000001000000000005809E8A0000000000001F0000000F4261746326682053657475702E2E2E000000000000000000000000010000000100000000000000000000000100000000000180D17F0000000000002000000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001804C8A0000000000002100000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000FFFF01001900434D4643546F6F6C426172436F6D626F426F78427574746F6EBA00000000000000000000000000000000000000000000000001000000010000009600000003002050FFFFFFFF00960000000000000000000180EB880000000000002200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C07F000000000000230000000000000000000000000000000001000000010000000180B08A000000000000240000000000000000000000000000000001000000010000000180A8010000000000004E00000000000000000000000000000000010000000100000001807202000000000000530000000000000000000000000000000001000000010000000180BE010000000000005000000000000000000000000000000000010000000100000000000000054275696C64E1010000 + + + 583 + 1000FFFF01001100434D4643546F6F6C426172427574746F6ECF7F000000000000FFFFFFFF0001000000000000000100000000000000010000000180D07F000000000000FFFFFFFF00010000000000000001000000000000000100000001803080000000000000FFFFFFFF00010000000000000001000000000000000100000001809E8A000000000000FFFFFFFF0001000000000000000100000000000000010000000180D17F000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001804C8A000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001806680000000000000FFFFFFFF0001000000000000000100000000000000010000000180EB88000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180C07F000000000000FFFFFFFF0001000000000000000100000000000000010000000180B08A000000000000FFFFFFFF0001000000000000000100000000000000010000000180A801000000000000FFFFFFFF00010000000000000001000000000000000100000001807202000000000000FFFFFFFF0001000000000000000100000000000000010000000180BE01000000000000FFFFFFFF000100000000000000010000000000000001000000 + + + 583 + 1000FFFF01001100434D4643546F6F6C426172427574746F6ECF7F000000000000000000000000000000000000000000000001000000010000000180D07F00000000000001000000000000000000000000000000000100000001000000018030800000000000000200000000000000000000000000000000010000000100000001809E8A000000000000030000000000000000000000000000000001000000010000000180D17F0000000000000400000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001804C8A0000000000000500000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001806680000000000000060000000000000000000000000000000001000000010000000180EB880000000000000700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C07F000000000000080000000000000000000000000000000001000000010000000180B08A000000000000090000000000000000000000000000000001000000010000000180A8010000000000000A000000000000000000000000000000000100000001000000018072020000000000000B0000000000000000000000000000000001000000010000000180BE010000000000000C000000000000000000000000000000000100000001000000 + + + + 59400 + Debug + + 2362 + 00200000010000001900FFFF01001100434D4643546F6F6C426172427574746F6ECC880000000000002500000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018017800000000000002600000000000000000000000000000000010000000100000001801D800000000004002700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001801A800000000000002800000000000000000000000000000000010000000100000001801B80000000000000290000000000000000000000000000000001000000010000000180E57F0000000000002A00000000000000000000000000000000010000000100000001801C800000000000002B00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018000890000000000002C00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180E48B0000020001002D0000000000000000000000000000000001000000010000000180F07F0000020001002E0000000000000000000000000000000001000000010000000180E8880000020000003700000000000000000000000000000000010000000100000001803B010000020001002F0000000000000000000000000000000001000000010000000180BB8A00000200010030000000000000000000000000000000000100000001000000FFFF01001500434D4643546F6F6C4261724D656E75427574746F6E0E01000002000000310000000D57617463682057696E646F7773000000000000000000000000010000000100000000000000000000000100000003001380D88B00000000000031000000085761746368202631000000000000000000000000010000000100000000000000000000000100000000001380D98B00000000000031000000085761746368202632000000000000000000000000010000000100000000000000000000000100000000001380CE01000000000000FFFFFFFF0C576174636820416E63686F720100000000000000010000000000000001000000000000000000000001000000000013800F0100000200010032000000094D656D6F7279202631000000000000000000000000010000000100000000000000000000000100000004001380D28B00000000000032000000094D656D6F7279202631000000000000000000000000010000000100000000000000000000000100000000001380D38B00000000000032000000094D656D6F7279202632000000000000000000000000010000000100000000000000000000000100000000001380D48B00000000000032000000094D656D6F7279202633000000000000000000000000010000000100000000000000000000000100000000001380D58B00000000000032000000094D656D6F72792026340000000000000000000000000100000001000000000000000000000001000000000013801001000002000000330000000855415254202326310000000000000000000000000100000001000000000000000000000001000000040013809307000000000000330000000855415254202326310000000000000000000000000100000001000000000000000000000001000000000013809407000000000000330000000855415254202326320000000000000000000000000100000001000000000000000000000001000000000013809507000000000000330000000855415254202326330000000000000000000000000100000001000000000000000000000001000000000013809607000000000000330000001626446562756720287072696E746629205669657765720000000000000000000000000100000001000000000000000000000001000000000013803C010000000000007200000010416E616C797369732057696E646F7773000000000000000000000000010000000100000000000000000000000100000004001380658A000000000000340000000F264C6F67696320416E616C797A6572000000000000000000000000010000000100000000000000000000000100000000001380DC7F0000000000003E0000001526506572666F726D616E636520416E616C797A6572000000000000000000000000010000000100000000000000000000000100000000001380E788000000000000380000000E26436F646520436F766572616765000000000000000000000000010000000100000000000000000000000100000000001380CD01000000000000FFFFFFFF0F416E616C7973697320416E63686F7201000000000000000100000000000000010000000000000000000000010000000000138053010000000000003F0000000D54726163652057696E646F77730000000000000000000000000100000001000000000000000000000001000000010013805401000000000000FFFFFFFF115472616365204D656E7520416E63686F720100000000000000010000000000000001000000000000000000000001000000000013802901000000000000350000001553797374656D205669657765722057696E646F77730000000000000000000000000100000001000000000000000000000001000000010013804B01000000000000FFFFFFFF1453797374656D2056696577657220416E63686F720100000000000000010000000000000001000000000000000000000001000000000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000013800189000002000000360000000F26546F6F6C626F782057696E646F7700000000000000000000000001000000010000000000000000000000010000000300138044C5000000000000FFFFFFFF0E5570646174652057696E646F77730100000000000000010000000000000001000000000000000000000001000000000013800000000000000400FFFFFFFF000000000000000000010000000000000001000000000000000000000001000000000013805B01000000000000FFFFFFFF12546F6F6C626F78204D656E75416E63686F72010000000000000001000000000000000100000000000000000000000100000000000000000005446562756787020000 + + + 898 + 1900FFFF01001100434D4643546F6F6C426172427574746F6ECC88000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001801780000000000000FFFFFFFF00010000000000000001000000000000000100000001801D80000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001801A80000000000000FFFFFFFF00010000000000000001000000000000000100000001801B80000000000000FFFFFFFF0001000000000000000100000000000000010000000180E57F000000000000FFFFFFFF00010000000000000001000000000000000100000001801C80000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001800089000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180E48B000000000000FFFFFFFF0001000000000000000100000000000000010000000180F07F000000000000FFFFFFFF0001000000000000000100000000000000010000000180E888000000000000FFFFFFFF00010000000000000001000000000000000100000001803B01000000000000FFFFFFFF0001000000000000000100000000000000010000000180BB8A000000000000FFFFFFFF0001000000000000000100000000000000010000000180D88B000000000000FFFFFFFF0001000000000000000100000000000000010000000180D28B000000000000FFFFFFFF00010000000000000001000000000000000100000001809307000000000000FFFFFFFF0001000000000000000100000000000000010000000180658A000000000000FFFFFFFF0001000000000000000100000000000000010000000180C18A000000000000FFFFFFFF0001000000000000000100000000000000010000000180EE8B000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001800189000000000000FFFFFFFF000100000000000000010000000000000001000000 + + + 898 + 1900FFFF01001100434D4643546F6F6C426172427574746F6ECC880000000000000000000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018017800000000000000100000000000000000000000000000000010000000100000001801D800000000000000200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001801A800000000000000300000000000000000000000000000000010000000100000001801B80000000000000040000000000000000000000000000000001000000010000000180E57F0000000000000500000000000000000000000000000000010000000100000001801C800000000000000600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018000890000000000000700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180E48B000000000000080000000000000000000000000000000001000000010000000180F07F000000000000090000000000000000000000000000000001000000010000000180E8880000000000000A00000000000000000000000000000000010000000100000001803B010000000000000B0000000000000000000000000000000001000000010000000180BB8A0000000000000C0000000000000000000000000000000001000000010000000180D88B0000000000000D0000000000000000000000000000000001000000010000000180D28B0000000000000E000000000000000000000000000000000100000001000000018093070000000000000F0000000000000000000000000000000001000000010000000180658A000000000000100000000000000000000000000000000001000000010000000180C18A000000000000110000000000000000000000000000000001000000010000000180EE8B0000000000001200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180018900000000000013000000000000000000000000000000000100000001000000 + + + + 0 + 2560 + 1440 + + + + +
diff --git a/Test_project_for_GD32107C-EVAL.uvoptx b/Test_project_for_GD32107C-EVAL.uvoptx new file mode 100644 index 0000000..de1473e --- /dev/null +++ b/Test_project_for_GD32107C-EVAL.uvoptx @@ -0,0 +1,300 @@ + + + + 1.0 + +
### uVision Project, (C) Keil Software
+ + + *.c + *.s*; *.src; *.a* + *.obj; *.o + *.lib + *.txt; *.h; *.inc; *.md + *.plm + *.cpp; *.cc; *.cxx + 0 + + + + 0 + 0 + + + + Target 1 + 0x4 + ARM-ADS + + 12000000 + + 1 + 1 + 0 + 1 + 0 + + + 1 + 65535 + 0 + 0 + 0 + + + 79 + 66 + 8 + .\Listings\ + + + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 0 + 0 + 0 + 0 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + + + 1 + 0 + 1 + + 255 + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 3 + + + + + + + + + + + BIN\CMSIS_AGDI.dll + + + + 0 + ARMRTXEVENTFLAGS + -L70 -Z18 -C0 -M0 -T1 + + + 0 + DLGTARM + (1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0) + + + 0 + ARMDBGFLAGS + + + + 0 + DLGUARM + + + + 0 + CMSIS_AGDI + -X"Any" -UAny -O1230 -S0 -C0 -P00000000 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -TO65554 -TC10000000 -TT10000000 -TP20 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO23 -FD20000000 -FC1000 -FN1 -FF0GD32F10x_CL.FLM -FS08000000 -FL040000 -FP0($$Device:GD32F107VC$Flash\GD32F10x_CL.FLM) + + + 0 + ST-LINKIII-KEIL_SWO + -U50FF75067788545419141567 -O1166 -SF1800 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -TO131090 -TC10000000 -TT10000000 -TP21 -TDS8000 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO7 -FD20000000 -FC1000 -FN1 -FF0GD32F10x_CL.FLM -FS08000000 -FL040000 -FP0($$Device:GD32F107VC$Flash\GD32F10x_CL.FLM) -WA0 -WE0 -WVCE4 -WS2710 -WM0 -WP2 + + + 0 + UL2CM3 + UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0GD32F10x_CL -FS08000000 -FL040000 -FP0($$Device:GD32F107VC$Flash\GD32F10x_CL.FLM)) + + + + + 0 + 0 + 0 + 1 +
134218370
+ 0 + 0 + 0 + 0 + 0 + 1 + + + 0x08000282 +
+
+ + + 1 + 0 + 0x40011000 + 0 + + + + C:\Users\User\AppData\Local\Arm\Packs\ARM\CMSIS-FreeRTOS\10.5.1\CMSIS\RTOS2\FreeRTOS\FreeRTOS.scvd + ARM.CMSIS-FreeRTOS.10.5.1 + 1 + + + 0 + + + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + 0 + 0 + 0 + + + + + + + + +
+
+ + + Source Group 1 + 0 + 0 + 0 + 0 + + 1 + 1 + 1 + 0 + 0 + 0 + .\main.c + main.c + 0 + 0 + + + + + FreeRTOS + 0 + 0 + 0 + 0 + + + + ::CMSIS + 0 + 0 + 0 + 1 + + + + ::Compiler + 0 + 0 + 0 + 1 + + + + ::Device + 0 + 0 + 0 + 1 + + + + ::FreeRTOS + 0 + 0 + 0 + 1 + + + + ::RTOS + 1 + 0 + 0 + 1 + + +
diff --git a/Test_project_for_GD32107C-EVAL.uvprojx b/Test_project_for_GD32107C-EVAL.uvprojx new file mode 100644 index 0000000..cb4ed9c --- /dev/null +++ b/Test_project_for_GD32107C-EVAL.uvprojx @@ -0,0 +1,634 @@ + + + + 2.1 + +
### uVision Project, (C) Keil Software
+ + + + Target 1 + 0x4 + ARM-ADS + 6190000::V6.19::ARMCLANG + 6190000::V6.19::ARMCLANG + 1 + + + GD32F107VC + GigaDevice + GigaDevice.GD32F10x_DFP.2.0.3 + https://gd32mcu.com/data/documents/pack/ + IRAM(0x20000000,0x00018000) IROM(0x08000000,0x00040000) CPUTYPE("Cortex-M3") CLOCK(12000000) ELITTLE + + + UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0GD32F10x_CL -FS08000000 -FL040000 -FP0($$Device:GD32F107VC$Flash\GD32F10x_CL.FLM)) + 0 + $$Device:GD32F107VC$Device\Include\gd32f10x.h + + + + + + + + + + $$Device:GD32F107VC$SVD\GD32F10x\GD32F10x_CL.svd + 0 + 0 + + + + + + + 0 + 0 + 0 + 0 + 1 + + .\Objects\ + Test_project_for_GD32107C-EVAL + 1 + 0 + 0 + 1 + 1 + .\Listings\ + 1 + 0 + 0 + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 0 + 0 + + + 0 + 0 + 0 + 0 + + 0 + + + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 1 + + + SARMCM3.DLL + -REMAP + DCM.DLL + -pCM3 + SARMCM3.DLL + + TCM.DLL + -pCM3 + + + + 1 + 0 + 0 + 0 + 16 + + + + + 1 + 0 + 0 + 1 + 1 + 4096 + + 1 + BIN\UL2CM3.DLL + "" () + + + + + 0 + + + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + "Cortex-M3" + + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 8 + 0 + 0 + 0 + 0 + 3 + 3 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x18000 + + + 1 + 0x8000000 + 0x40000 + + + 0 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x8000000 + 0x40000 + + + 1 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x18000 + + + 0 + 0x0 + 0x0 + + + + + + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 4 + 0 + 0 + 0 + 1 + 0 + 5 + 3 + 1 + 1 + 0 + 0 + 0 + + + + + + + + + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + + + + + + + + + 1 + 0 + 0 + 0 + 1 + 0 + 0x08000000 + 0x20000000 + + + + + + + + + + + + + Source Group 1 + + + main.c + 1 + .\main.c + + + + + FreeRTOS + + + ::CMSIS + + + ::Compiler + + + ::Device + + + ::FreeRTOS + + + ::RTOS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RTE\Device\GD32F107VC\gd32f107c_eval.c + + + + + + + + RTE\Device\GD32F107VC\gd32f10x_dbg.c + + + + + + RTE\Device\GD32F107VC\gd32f10x_exti.c + + + + + + + + RTE\Device\GD32F107VC\gd32f10x_gpio.c + + + + + + + + RTE\Device\GD32F107VC\gd32f10x_misc.c + + + + + + + + RTE\Device\GD32F107VC\gd32f10x_pmu.c + + + + + + + + RTE\Device\GD32F107VC\gd32f10x_rcu.c + + + + + + + + RTE\Device\GD32F107VC\gd32f10x_usart.c + + + + + + + + RTE\Device\GD32F107VC\startup_gd32f10x_cl.s + + + + + + + + RTE\Device\GD32F107VC\system_gd32f10x.c + + + + + + + + RTE\RTOS\FreeRTOSConfig.h + + + + + + + + + + + + + Test_project_for_GD32107C-EVAL + 1 + + + + +
diff --git a/main.c b/main.c new file mode 100644 index 0000000..80756c6 --- /dev/null +++ b/main.c @@ -0,0 +1,60 @@ +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "gd32f107c_eval.h" +#include "gd32f10x_gpio.h" +#include "stdio.h" +#define BUTTON_USER GPIO_PIN_14 +#define LED_USER GPIO_PIN_0 +char ButtonState = 0; +int stdout_putchar (int ch); +int stdin_getchar (void); +void DelayMS(unsigned char ms); +int stdout_putchar (int ch) +{ + usart_data_transmit(EVAL_COM1, (uint16_t)ch); + DelayMS(1); + return ch; +} +int stdin_getchar (void) +{ + return usart_data_receive(EVAL_COM1); +} +void DelayMS(unsigned char ms) +{ +unsigned long us = 1000*ms; + +while (us--) + { + __NOP(); + } +} + +{ +while (1) + { + ButtonState = !gpio_input_bit_get(GPIOB, BUTTON_USER); + if (ButtonState) + { + printf("Hello world\n"); + DelayMS(250); + DelayMS(250); + } + } +} +int main(void) +{ + + SystemInit(); + //gd_eval_led_init(LED2); + //gd_eval_led_on(LED2); + rcu_periph_clock_enable(RCU_GPIOC); + gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, LED_USER); + gpio_bit_set(GPIOC, LED_USER); + gd_eval_com_init(EVAL_COM1); + rcu_periph_clock_enable(RCU_GPIOB); + gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, BUTTON_USER); + + + xPortStartScheduler(); + while(1); +}