#include #include #include #include "ti_drivers_config.h" #include "FreeRTOS.h" #include "FreeRTOS_IP.h" #include "FreeRTOS_IP_private.h" #include "FreeRTOSIPConfigDefaults.h" #include "NetworkBufferManagement.h" #include "task.h" #include "constants.h" #include "ethernet.h" #include "uart_debug.h" #include "adc_rand.h" #include "main.h" /* The setup is done using driverlib to use the FreeRTOS+TCP port. This ensures that the interface operates in a consistent fashion and also allows for multiple IP addresses to be assigned using FreeRTOS multi. In the vReleaseNetworkBufferAndDescriptor() function in the BufferAllocation1.c file, I had to add the following line to check for a null pointer. if(pxNetworkBuffer == NULL) return; This is not a zero copy interface, but the code can be updated to create a zero-copy interface. REFERENCES: https://e2e.ti.com/support/microcontrollers/other/f/908/t/534375?Reading-MAC-address-using-FlashUserGet-function file:///C:/ti/simplelink_msp432e4_sdk_4_20_00_12/docs/driverlib/msp432e4/api_guide/html/group__emac__api.html https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Porting.html https://e2e.ti.com/support/microcontrollers/other/f/908/t/498224?TM4C129-Tiva-C-Series-Ethernet-Rj45-cable-link-disconnection-interrupt- https://e2e.ti.com/support/microcontrollers/msp430/f/166/t/898167 https://e2e.ti.com/support/microcontrollers/msp430/f/166/t/782195?MSP432E401Y-ICMP-ping-checksum-is-set-to-0 https://github.com/FreeRTOS/FreeRTOS/issues/87 */ // Turn on this define for debugging to print the ethernet RX and TX events // #define PRINT_ETHERNET_RX_TX //-------------------------------------------------------------------- // DMA descriptors tEMACDMADescriptor g_psRxDescriptor[NUM_TX_DESCRIPTORS]; tEMACDMADescriptor g_psTxDescriptor[NUM_RX_DESCRIPTORS]; uint32_t g_ui32RxDescIndex; uint32_t g_ui32TxDescIndex; // transmit and receive buffers uint8_t g_ppui8RxBuffer[NUM_RX_DESCRIPTORS][RX_BUFFER_SIZE]; // task handle indicating that the Ethernet has received an RX packet TaskHandle_t xTaskToNotifyEthernetRX = NULL; BaseType_t xHigherPriorityTaskWoken = pdFALSE; // MAC address for the stack uint8_t pui8MACAddr[6]; // name of the device char DEV_NAME[MAX_NAME_LLMNR]; #define MAX_BUFF_SIZ 1500 uint8_t ethBuffer[MAX_BUFF_SIZ]; // buffer uint32_t i32FrameLen_received; // length of received frame //-------------------------------------------------------------------- // Call this function to setup the Ethernet interface // Using FreeRTOS, the hardware is setup by the network stack. void setup_ethernet() { uint32_t ui32User0, ui32User1, ui32Loop; // 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. FlashUserGet(&ui32User0, &ui32User1); if((ui32User0 == 0xffffffff) || (ui32User1 == 0xffffffff)) { // TODO: load from external IC over the I2C bus while(1) {} } // re-arrange the MAC address pui8MACAddr[0] = ((ui32User0 >> 0) & 0xff); pui8MACAddr[1] = ((ui32User0 >> 8) & 0xff); pui8MACAddr[2] = ((ui32User0 >> 16) & 0xff); pui8MACAddr[3] = ((ui32User1 >> 0) & 0xff); pui8MACAddr[4] = ((ui32User1 >> 8) & 0xff); pui8MACAddr[5] = ((ui32User1 >> 16) & 0xff); // enable and reset the ethernet modules SysCtlPeripheralEnable(SYSCTL_PERIPH_EMAC0); SysCtlPeripheralEnable(SYSCTL_PERIPH_EPHY0); SysCtlPeripheralReset(SYSCTL_PERIPH_EMAC0); SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0); // wait for the MAC to be ready while(!SysCtlPeripheralReady(SYSCTL_PERIPH_EMAC0)){} // configure the internal PHY EMACPHYConfigSet(EMAC0_BASE, (EMAC_PHY_TYPE_INTERNAL | EMAC_PHY_INT_MDIX_EN | EMAC_PHY_AN_100B_T_FULL_DUPLEX)); // reset the MAC to latch the configuration EMACReset(EMAC0_BASE); // init the EMAC EMACInit(EMAC0_BASE, get_clock_speed(), EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, 4, 4, 0); // set the MAC configuration options /* Since the checksum is computed in the hardware using EMAC_CONFIG_CHECKSUM_OFFLOAD, there is also a need to define these in the FreeRTOS+TCP as well. See the xNetworkInterfaceOutput function below as well. #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 */ EMACConfigSet(EMAC0_BASE, (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_MODE_RX_STORE_FORWARD | EMAC_MODE_TX_STORE_FORWARD | EMAC_MODE_TX_THRESHOLD_64_BYTES | EMAC_MODE_RX_THRESHOLD_64_BYTES), 0); // DMA descriptors InitDescriptors(EMAC0_BASE); // program MAC address EMACAddrSet(EMAC0_BASE, 0, pui8MACAddr); // Wait for the link to be active - this will wait forever if the link is not up // The link status can be checked in a FreeRTOS task using the polling mechanism below. // There appears to not be an interrupt for the link status, so polling is the only way to proceed. // while((EMACPHYRead(EMAC0_BASE, ui8PHYAddr, EPHY_BMSR) & // EPHY_BMSR_LINKSTAT) == 0){} // Set MAC filtering options (if required) // EMACFrameFilterSet(EMAC0_BASE, EMAC_FRMFILTER_SADDR | EMAC_FRMFILTER_PASS_ALL_CTRL | EMAC_FRMFILTER_PASS_MULTICAST); // Clear any interrupts EMACIntClear(EMAC0_BASE, EMACIntStatus(EMAC0_BASE, false)); // indicate that the receive descriptors are available to the DMA to start the receive processing for(ui32Loop = 0; ui32Loop < NUM_RX_DESCRIPTORS; ui32Loop++) { g_psRxDescriptor[ui32Loop].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) into the last three bits for Cortex M4F // register the interrupt handler for the Ethernet MAC EMACIntRegister(EMAC0_BASE, EthernetIntHandler); // enable the interrupt IntEnable(INT_EMAC0); // enable the Ethernet receive interrupt EMACIntEnable(EMAC0_BASE, EMAC_INT_RECEIVE); } // end // init the DMA descriptors void InitDescriptors(uint32_t ui32Base) { uint32_t ui32Loop; // init transmit for(ui32Loop = 0; ui32Loop < NUM_TX_DESCRIPTORS; ui32Loop++) { g_psTxDescriptor[ui32Loop].ui32Count = DES1_TX_CTRL_SADDR_INSERT; g_psTxDescriptor[ui32Loop].DES3.pLink = (ui32Loop == (NUM_TX_DESCRIPTORS - 1)) ? g_psTxDescriptor : &g_psTxDescriptor[ui32Loop + 1]; g_psTxDescriptor[ui32Loop].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); } // init receive for(ui32Loop = 0; ui32Loop < NUM_RX_DESCRIPTORS; ui32Loop++) { g_psRxDescriptor[ui32Loop].ui32CtrlStatus = 0; g_psRxDescriptor[ui32Loop].ui32Count = (DES1_RX_CTRL_CHAINED | (RX_BUFFER_SIZE << DES1_RX_CTRL_BUFF1_SIZE_S)); g_psRxDescriptor[ui32Loop].pvBuffer1 = g_ppui8RxBuffer[ui32Loop]; g_psRxDescriptor[ui32Loop].DES3.pLink = (ui32Loop == (NUM_RX_DESCRIPTORS - 1)) ? g_psRxDescriptor : &g_psRxDescriptor[ui32Loop + 1]; } // set the pointers in the hardware EMACRxDMADescriptorListSet(ui32Base, g_psRxDescriptor); EMACTxDMADescriptorListSet(ui32Base, g_psTxDescriptor); // start from the beginning of the descriptor chains g_ui32RxDescIndex = 0; g_ui32TxDescIndex = NUM_TX_DESCRIPTORS - 1; } // end // Transmit a packet - call this function from the network stack // pui8Buf = the buffer // i32BufLen = length of the buffer int32_t PacketTransmit(uint8_t *pui8Buf, uint32_t i32BufLen) { // Wait for the transmit descriptor to free up. while(g_psTxDescriptor[g_ui32TxDescIndex].ui32CtrlStatus & DES0_TX_CTRL_OWN) { } #ifdef PRINT_ETHERNET_RX_TX vLoggingPrintf("TX:%d\r\n", i32BufLen); #endif // Move to the next descriptor. g_ui32TxDescIndex++; if(g_ui32TxDescIndex == NUM_TX_DESCRIPTORS) { g_ui32TxDescIndex = 0; } // Fill in the packet size and pointer, and tell the transmitter to start work g_psTxDescriptor[g_ui32TxDescIndex].ui32Count = i32BufLen; g_psTxDescriptor[g_ui32TxDescIndex].pvBuffer1 = pui8Buf; g_psTxDescriptor[g_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); // Tell the DMA to reaquire the descriptor EMACTxDMAPollDemand(EMAC0_BASE); // return the number of bytes sent return(i32BufLen); } // end // Interrupt handler void EthernetIntHandler(void) { uint32_t ui32Temp; ui32Temp = EMACIntStatus(EMAC0_BASE, true); // Check to see if an RX interrupt has occurred if(ui32Temp & EMAC_INT_RECEIVE) { ProcessReceivedPacket(); } EMACIntClear(EMAC0_BASE, ui32Temp); portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } // end // Function to process the received packet uint32_t ProcessReceivedPacket(void) { uint32_t i32FrameLen; // By default, we assume we got a bad frame. i32FrameLen = 0; // Make sure that we own the receive descriptor. if(!(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & DES0_RX_CTRL_OWN)) { // Does it have a valid frame? if(!(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & DES0_RX_STAT_ERR)) { if(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & DES0_RX_STAT_LAST_DESC) { // get the frame length i32FrameLen = ((g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & DES0_RX_STAT_FRAME_LENGTH_M) >> DES0_RX_STAT_FRAME_LENGTH_S); // call the function that sends the data to the task ApplicationProcessFrame(i32FrameLen, g_psRxDescriptor[g_ui32RxDescIndex].pvBuffer1, g_ui32RxDescIndex); } } // Now that we are finished dealing with this descriptor, hand // it back to the hardware. g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus = DES0_RX_CTRL_OWN; // Move on to the next descriptor in the chain ready for the next RX packet g_ui32RxDescIndex++; if(g_ui32RxDescIndex == NUM_RX_DESCRIPTORS) { g_ui32RxDescIndex = 0; } } // end if // ensure that the task is called portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); // return the frame length return(i32FrameLen); } // end // This function passes the framelength and the buffer into the FreeRTOS task. // The function is called from the ISR. void ApplicationProcessFrame(uint32_t i32FrameLen, uint8_t *pui8Buf, uint32_t index ) { // copy the buffer and the length of the frame i32FrameLen_received = i32FrameLen; memcpy(ethBuffer, pui8Buf, i32FrameLen); // Signal to the consumer task that data has been received... // this is called from an ISR. vTaskNotifyGiveFromISR( xTaskToNotifyEthernetRX, &xHigherPriorityTaskWoken); } // end // FreeRTOS task that handles the interrupt using task handle xTaskToNotifyEthernetRX void prvEMACDeferredInterruptHandlerTask( void *pvParameters ) { NetworkBufferDescriptor_t *pxDescriptor = NULL; IPStackEvent_t xRxEvent; for(;;) { ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); #ifdef PRINT_ETHERNET_RX_TX vLoggingPrintf("RX:%d\r\n", i32FrameLen_received); #endif if(i32FrameLen_received == 0) continue; if( eConsiderFrameForProcessing( ethBuffer ) != eProcessBuffer ) continue; pxDescriptor = pxGetNetworkBufferWithDescriptor(i32FrameLen_received, 0 ); if (pxDescriptor == NULL) continue; pxDescriptor->pucEthernetBuffer = ethBuffer; pxDescriptor->xDataLength = i32FrameLen_received; xRxEvent.eEventType = eNetworkRxEvent; xRxEvent.pvData = ( void * ) pxDescriptor; if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) { vReleaseNetworkBufferAndDescriptor( pxDescriptor ); pxDescriptor = NULL; } } // end for } // end // Call this function to setup the ethernet task void setup_ethernet_tasks() { xTaskCreate(prvEMACDeferredInterruptHandlerTask, "EthRX", 1000, NULL, 1, &xTaskToNotifyEthernetRX ); } // end //------------------------------------------------------------- // FREERTOS+TCP //------------------------------------------------------------- // Call this function before starting the scheduler and after the MAC address and device name has been loaded void setup_freertos_tcp() { // for now, the fallback addresses are given here, but these can be stored in flash and then read static const uint8_t ucIPAddress[ 4 ] = { 192, 168, 1, 171 }; static const uint8_t ucNetMask[ 4 ] = { 255, 255, 255, 0 }; static const uint8_t ucGatewayAddress[ 4 ] = { 192, 168, 1, 1 }; static const uint8_t ucDNSServerAddress[ 4 ] = { 208, 67, 222, 222 }; // setup the tasks setup_ethernet_tasks(); // setup the device name (load serial number from flash) setup_device_name(DEV_NAME_DEFAULT); // init the network stack FreeRTOS_IPInit( ucIPAddress, ucNetMask, ucGatewayAddress, ucDNSServerAddress, pui8MACAddr); } // end // 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; } // end // Function that returns a random number for TCP. This is taken to be a true 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; } // end // function to obtain random number UBaseType_t uxRand() { uint32_t num = obtain_rand32(); return num; } // end // Function called when the network connects or disconnects void vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent ) { uint32_t ulIPAddress, ulNetMask, ulGatewayAddress, ulDNSServerAddress; static BaseType_t xTasksAlreadyCreated = pdFALSE; char cBuffer[ 16 ]; if( eNetworkEvent == eNetworkUp ) { if( xTasksAlreadyCreated == pdFALSE ) { // create any tasks here when the network is up xTasksAlreadyCreated = 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 ) { // network is down } // end if } // end // Function that indicates there is a response // Also check the code in IPTraceMacroDefaults void pingReply( uint32_t ulIPAddress ) { char cBuffer[ 16 ]; FreeRTOS_inet_ntoa( ulIPAddress, cBuffer ); vLoggingPrintf( "Ping response to: %s\r\n", cBuffer ); } // end // 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; } // end // Hook to return a human-readable name const char *pcApplicationHostnameHook( void ) { const char *name; if (DEV_NAME[0] != 0) { name = DEV_NAME; } else { name = DEV_NAME_DEFAULT; } vLoggingPrintf("hostname:%s\r\n", DEV_NAME); return name; } // end // Call this function to assign the device name before the network stack begins void setup_device_name(char *name) { memset(DEV_NAME,0,sizeof(DEV_NAME)); strncpy(DEV_NAME, name, MAX_NAME_LLMNR-1); } // end // Network initialization is already done outside of this function BaseType_t xNetworkInterfaceInitialise( void ) { return pdPASS; } // end // function to output packets to the network BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t xReleaseAfterSend ) { taskENTER_CRITICAL(); #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) // this is required if the network hardware computes the checksum ProtocolPacket_t * pxPacket; pxPacket = ( ProtocolPacket_t * ) ( pxDescriptor->pucEthernetBuffer ); if( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) { pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t ) 0u; } #endif PacketTransmit(pxDescriptor->pucEthernetBuffer, (uint32_t)pxDescriptor->xDataLength); // transmit taskEXIT_CRITICAL(); iptraceNETWORK_INTERFACE_TRANSMIT(); // log the data transmit if( xReleaseAfterSend == pdTRUE ) // release the buffer if requested { vReleaseNetworkBufferAndDescriptor( pxDescriptor ); } return pdTRUE; } // end //----------------------------------------------------------------------------------------------------- // For BufferAllocation_1.c //----------------------------------------------------------------------------------------------------- #define BUFFER_SIZE ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING ) #define BUFFER_SIZE_ROUNDED_UP ( ( BUFFER_SIZE + 7 ) & ~0x07UL ) static uint8_t ucBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ BUFFER_SIZE_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 ] ); } } // end //-----------------------------------------------------------------------------------------------------