/** * @file: NetworkInterface.c * @author: Adrien Cardinale * @date: Nov 23, 2023 * @copyright: * * 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 f28388d. */ #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_cm.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" #include "FreeRTOS_IP_Private.h" #include "FreeRTOS_DNS.h" #include "FreeRTOS_ARP.h" #include "FreeRTOS_Routing.h" #include "NetworkBufferManagement.h" #include "NetworkInterface.h" #define ETHERNET_NO_OF_RX_PACKETS 2U #define ETHERNET_MAX_PACKET_LENGTH 1538U // _HT_ A length of 1536 would even be better because of its perfect alignment. // This will only become important when caching is active. #define NUM_PACKET_DESC_RX_APPLICATION 8 #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 ) BaseType_t pyhLinkStatus = pdFALSE; TaskHandle_t xEMACTaskHandle = NULL; Ethernet_Handle emac_handle; Ethernet_InitConfig *pInitCfg; // _HT_ // As the following global variable is set form an interrupt context, // it would be good to make it volatile. // But it looks like there is a better solution. RX packets may // come in a "fast train". // You don't want to overwrite 'receivedDataLength' before it // has been processed by the EMAC task. size_t receivedDataLength; static NetworkInterface_t* pxMyInterface = NULL; uint32_t genericISRCustomcount = 0; uint32_t genericISRCustomRBUcount = 0; uint32_t genericISRCustomROVcount = 0; uint32_t genericISRCustomRIcount = 0; extern Ethernet_Device Ethernet_device_struct; uint8_t Ethernet_rxBuffer[ETHERNET_NO_OF_RX_PACKETS * ETHERNET_MAX_PACKET_LENGTH]; // _HT_ You're declaring an array of bytes, and thus it might // be important to tell the compiler what alignment you wish, for instance: // // __attribute__( ( aligned( 32 ) ) ) // uint8_t Ethernet_rxBuffer[]; extern uint32_t Ethernet_rxInterruptCount; static void prvEMACDeferredInterruptHandlerTask( void *pvParameters ); static BaseType_t prvf28388d_NetworkInterfaceInitialise(NetworkInterface_t* pxInterface); static BaseType_t prvf28388d_NetworkInterfaceOutput( NetworkInterface_t* pxInterface, NetworkBufferDescriptor_t* const pxDescriptor, BaseType_t xReleaseAfterSend); NetworkInterface_t* pxf28388d_FillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t* pxInterface); static BaseType_t prvf28388d_GetPhyLinkStatus(NetworkInterface_t* pxInterface); #if (ipconfigIPv4_BACKWARD_COMPATIBLE == 1) NetworkInterface_t* pxFillInterfaceDescriptor(BaseType_t xEMACIndex, NetworkInterface_t* pxInterface) { return pxf28388d_FillInterfaceDescriptor(xEMACIndex, pxInterface); } #endif NetworkInterface_t* pxf28388d_FillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t* pxInterface) { // _HT_ // What about calling it "f28388d"? pxInterface->pcName ="ethernet"; pxInterface->pvArgument = (void*)xEMACIndex; pxInterface->pfInitialise = prvf28388d_NetworkInterfaceInitialise; pxInterface->pfOutput = prvf28388d_NetworkInterfaceOutput; pxInterface->pfGetPhyLinkStatus = NULL; FreeRTOS_AddNetworkInterface(pxInterface); return pxInterface; } static BaseType_t prvf28388d_GetPhyLinkStatus(NetworkInterface_t* pxInterface) { return pyhLinkStatus; } Ethernet_Pkt_Desc *Ethernet_receivePacketCallbackCustom(Ethernet_Handle handleApplication, Ethernet_Pkt_Desc *pPktDesc) { BaseType_t higher_priority_task_woken = pdFALSE; Ethernet_rxInterruptCount++; receivedDataLength = pPktDesc->validLength; vTaskNotifyGiveFromISR( xEMACTaskHandle, &higher_priority_task_woken); portYIELD_FROM_ISR( higher_priority_task_woken ); return Ethernet_getPacketBuffer(); } static BaseType_t prvf28388d_NetworkInterfaceInitialise(NetworkInterface_t* pxInterface){ BaseType_t xReturn; // _HT_ // This initialise function will be called repeatedly until it returns pdPASS ( a '1' ). // It would be good to do the initialisation only a single time. // Many network interfaces use a process status here like e.g. UltraScale: // typedef enum xEMAC_STATE // { // xEMAC_Init, // xEMAC_SetupPHY, // xEMAC_WaitPHY, // xEMAC_Ready, // xEMAC_Fatal, // } EMACState_t; // And mind you, there can also me a fatal state, eg. when malloc failed if(xEMACTaskHandle == NULL) { // _HT_ // Normally, starting the EMAC task is the last thing to do in the initialisation. // The reason is that the EMAC task might also access the EMAC, which may lead // to conflicts. xTaskCreate(prvEMACDeferredInterruptHandlerTask, "EMACInt", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle); configASSERT(xEMACTaskHandle); pxMyInterface = pxInterface; } Ethernet_InitInterfaceConfig initInterfaceConfig; uint32_t macLower; uint32_t macHigher; uint8_t *temp; initInterfaceConfig.ssbase = EMAC_SS_BASE; initInterfaceConfig.enet_base = EMAC_BASE; initInterfaceConfig.phyMode = ETHERNET_SS_PHY_INTF_SEL_MII; // // Assign SoC specific functions for Enabling,Disabling interrupts // and for enabling the Peripheral at system level // initInterfaceConfig.ptrPlatformInterruptDisable = &Platform_disableInterrupt; initInterfaceConfig.ptrPlatformInterruptEnable = &Platform_enableInterrupt; initInterfaceConfig.ptrPlatformPeripheralEnable = &Platform_enablePeripheral; initInterfaceConfig.ptrPlatformPeripheralReset = &Platform_resetPeripheral; // // Assign the peripheral number at the SoC // initInterfaceConfig.peripheralNum = SYSCTL_PERIPH_CLK_ENET; // // Assign the default SoC specific interrupt numbers of Ethernet interrupts // initInterfaceConfig.interruptNum[0] = INT_EMAC; initInterfaceConfig.interruptNum[1] = INT_EMAC_TX0; initInterfaceConfig.interruptNum[2] = INT_EMAC_TX1; initInterfaceConfig.interruptNum[3] = INT_EMAC_RX0; initInterfaceConfig.interruptNum[4] = INT_EMAC_RX1; pInitCfg = Ethernet_initInterface(initInterfaceConfig); Ethernet_getInitConfig(pInitCfg); pInitCfg->dmaMode.InterruptMode = ETHERNET_DMA_MODE_INTM_MODE2; // // Assign the callbacks for Getting packet buffer when needed // Releasing the TxPacketBuffer on Transmit interrupt callbacks // Receive packet callback on Receive packet completion interrupt // pInitCfg->pfcbRxPacket = &Ethernet_receivePacketCallbackCustom; pInitCfg->pfcbGetPacket = &Ethernet_getPacketBuffer; pInitCfg->pfcbFreePacket = &Ethernet_releaseTxPacketBuffer; // //Assign the Buffer to be used by the Low level driver for receiving //Packets. This should be accessible by the Ethernet DMA // pInitCfg->rxBuffer = Ethernet_rxBuffer; // // The Application handle is not used by this application // Hence using a dummy value of 1 // Ethernet_getHandle((Ethernet_Handle)1, pInitCfg , &emac_handle); // //Do global Interrupt Enable // (void)Interrupt_enableInProcessor(); // //Assign default ISRs // Interrupt_registerHandler(INT_EMAC_TX0, Ethernet_transmitISR); Interrupt_registerHandler(INT_EMAC_RX0, Ethernet_receiveISR); // Interrupt_registerHandler(INT_EMAC, Ethernet_genericISR); // //Change the priority of the interrupt handlers // Interrupt_setPriority(INT_EMAC_TX0, 15); Interrupt_setPriority(INT_EMAC_RX0, 15); // Interrupt_setPriority(INT_EMAC, 15); // //Enable the default interrupt handlers // Interrupt_enable(INT_EMAC_TX0); Interrupt_enable(INT_EMAC_RX0); // Interrupt_enable(INT_EMAC); if(pInitCfg == NULL){ xReturn = pdFAIL; }else{ //TODO: Set the MAC address by FreeRTOS Ethernet_setMACAddr(EMAC_BASE, 0, 0X0000002B, 0X00F263A8, ETHERNET_CHANNEL_0); xReturn = pdPASS; } // _HT_ Where is the actual PHY code? // It that still a TODO ? // I recommend to use this generic PHY module: // NetworkInterface\Common\phyHandling.c // pyhLinkStatus = pdTRUE; return xReturn; } static BaseType_t prvf28388d_NetworkInterfaceOutput(NetworkInterface_t* pxInterface, NetworkBufferDescriptor_t* const pxDescriptor, BaseType_t xReleaseAfterSend){ Ethernet_Pkt_Desc pktDesc; //creat temp buffer with max ethernet frame size uint8_t temp[ETHERNET_MAX_PACKET_LENGTH]; // _HT_ // Note that 1536 are declared on the stack of the IP-task, that is a lot. // Are you sure that there is enough stack space? See `ipconfigIP_TASK_STACK_SIZE_WORDS` // PS. It is not necessary to make NetworkInterfaceOutput() "re-entrant". // It will be only be called by the IP-task, and only once at a time. //copy first 6 bytes of pucEthernetBuffer to temp buffer memcpy(temp, pxDescriptor->pucEthernetBuffer, 6); //copy last bytes of pucEthernetBuffer to temp buffer // _HT_ // It looks like you are not copying the "from" MAC address, why? // Does the driver send the "from" address? memcpy(temp + 6, pxDescriptor->pucEthernetBuffer + 12, pxDescriptor->xDataLength - 6); pktDesc.bufferLength = pxDescriptor->xDataLength; pktDesc.dataOffset = 0; pktDesc.dataBuffer = temp; pktDesc.nextPacketDesc = 0; /* Set the flags Start of Packet, End Of Packet, and SA Insertion Flags pktDesc.flags = ETHERNET_PKT_FLAG_SOP | ETHERNET_PKT_FLAG_EOP | ETHERNET_PKT_FLAG_SA_INS; pktDesc.pktChannel = ETHERNET_DMA_CHANNEL_NUM_0; pktDesc.pktLength = pxDescriptor->xDataLength; pktDesc.validLength = pxDescriptor->xDataLength; pktDesc.numPktFrags = 1; // _HT_ Did you check the packets sent in WireShark? Do the look all right? Ethernet_sendPacket(emac_handle, &pktDesc); SysCtl_delay(5000); // _HT_ Is this 5000 uS? // Isn't there an interrupt when the packet is delivered? iptraceNETWORK_INTERFACE_TRANSMIT(); pktDesc.dataBuffer = NULL; if(xReleaseAfterSend != pdFALSE){ // _HT_ // I would remove the following line of code: pxDescriptor->pucEthernetBuffer = NULL; vReleaseNetworkBufferAndDescriptor(pxDescriptor); } return pdTRUE; } static void prvEMACDeferredInterruptHandlerTask( void *pvParameters ) { NetworkBufferDescriptor_t *pxBufferDescriptor; size_t xBytesReceived; IPStackEvent_t xRxEvent; uint32_t ulISREvents = 0U; for( ;; ) { ulTaskNotifyTake( pdFALSE, portMAX_DELAY ); // xTaskNotifyWait( 0U, /* ulBitsToClearOnEntry */ // EMAC_IF_ALL_EVENT, /* ulBitsToClearOnExit */ // &( ulISREvents ), /* pulNotificationValue */ // portMAX_DELAY ); // _HT_ // In my experience it is helpful to use the interrupt flags like: // #define EMAC_IF_RX_EVENT 1UL // #define EMAC_IF_TX_EVENT 2UL // #define EMAC_IF_ERR_EVENT 4UL xBytesReceived = receivedDataLength; if(xBytesReceived > 0){ pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 ); if(pxBufferDescriptor != NULL){ // _HT_ // This help variable 'xUsePacket' can make your code simpler: BaseType_t xUsePacket = pdFALSE; // 'Ethernet_rxBuffer' has space for two RX packets. Why don't you use the full length? memcpy(pxBufferDescriptor->pucEthernetBuffer, Ethernet_rxBuffer, xBytesReceived); pxBufferDescriptor->xDataLength = xBytesReceived; if(eConsiderFrameForProcessing(pxBufferDescriptor->pucEthernetBuffer) == eProcessBuffer){ pxBufferDescriptor->pxEndPoint = FreeRTOS_MatchingEndpoint(pxMyInterface, pxBufferDescriptor->pucEthernetBuffer); if(pxBufferDescriptor->pxEndPoint != NULL){ xRxEvent.eEventType = eNetworkRxEvent; xRxEvent.pvData = (void*)pxBufferDescriptor; if(xSendEventStructToIPTask(&xRxEvent, 0) == pdFALSE){ iptraceETHERNET_RX_EVENT_LOST(); }else{ xUsePacket = pdTRUE; iptraceNETWORK_INTERFACE_RECEIVE(); } } } if( xUsePacket == pdFALSE ) { vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); } }else{ iptraceETHERNET_RX_EVENT_LOST(); } } } }