Hi Vasilij,
A lot is good now, here below I added some “//” comments to make things even better 🙂
~~~~
BaseType
t xNetworkInterfaceInitialise( void )
{
if( xEMACTaskHandle == NULL )
{
xTaskCreate( prvEMACHandlerTask, “EMAC”, 20 * configMINIMALSTACK
SIZE, NULL,
configMAXPRIORITIES – 1, &xEMACTaskHandle );
}
// Only return pdPASS if you have detected a Link Status and if the device (PHY) is usable.
// The function will be called again and again until it returns pdPASS
return pdPASS;
}
~~~~
~~~~
BaseType
t xNetworkInterfaceOutput( xNetworkBufferDescriptort * const pxDescriptor,
BaseType_t bReleaseAfterSend )
{
static u8 TxBufIndex = 0;
u32 ulTransmitSize = pxDescriptor->xDataLength;
u32 i = TxBufIndex;
/* Wait until previous packet transmitted. */
// In a later version you might want to make this blocking and get woken-up
// by an ISR: TX-complete or so.
while (Tx
Desc[i].CtrlStat & DMATX_OWN);
// The TX-complete event could also trigger cleaning up old TX buffers.
// Now you’re leaving Network Buffers unused for a longer period
// of time.
uint8_t* ucBuffer = ( uint8_t * )Tx_Desc[i].Addr;
if ( ucBuffer != 0 )
{
NetworkBufferDescriptor_t *pxNetworkBuffer =
pxPacketBuffer_to_NetworkBuffer( ( void * )ucBuffer );
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
Tx_Desc[i].Addr = ( uint32_t )0u;
}
// Before actually sending a packet, please check if the Link Status
// is still high. If it is low, just do not send anything and release
// the Network Buffer as below.
...
Tx_Desc[i].Addr = ( uint32_t )pxDescriptor->pucEthernetBuffer;
// You are passing the ownership of 'pxDescriptor' to DMA, soa you may not
// release it anymore. Set bReleaseAfterSend to false here:
bReleaseAfterSend = pdFALSE;
// Also make sure that '#define ipconfigZERO_COPY_TX_DRIVER 1'
Tx_Desc[i].Size = ulTransmitSize;
Tx_Desc[i].CtrlStat |= DMA_TX_OWN;
if (++i == NUM_TX_BUF) i = 0;
TxBufIndex = i;
/* Start frame transmission. */
ETH->DMASR = DSR_TPSS;
ETH->DMATPDR = 0;
iptraceNETWORK_INTERFACE_TRANSMIT();
if( bReleaseAfterSend != pdFALSE )
{
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
}
~~~~
~~~~
void ETH
IRQHandler( void )
{
// What I miss here is the checks for the RX and TX-complete interrupts
// These would be good reasons to wake-up your task.
BaseTypet pxHigherPriorityTaskWoken;
vTaskNotifyGiveFromISR( xEMACTaskHandle, &pxHigherPriorityTaskWoken );
// I tend to put ‘portYIELD
FROMISR()’ as the last call in an ISR
portYIELD
FROMISR( pxHigherPriorityTaskWoken );
if (ETH->DMASR & INT
RBUIE)
{
/* Rx DMA suspended, resume DMA reception. */
ETH->DMASR = INTRBUIE;
ETH->DMARPDR = 0;
ETH->DMASR |= INT
RBUIE;
}
/* Clear the interrupt pending bits. */
ETH->DMASR = INTNISE | INT_RIE;
}
~~~~
~~~~
static void prvEMACHandlerTask( void *pvParameters )
{
IPStackEvent_t xRxEvent;
uint8_t *pucTemp;
NetworkBufferDescriptor_t *pxDescriptor;
_xNetworkInterfaceInitialise();
for( ;; )
{
ulTaskNotifyTake( pdFALSE, portMAX_DELAY );
u32 i = RxBufIndex;
do
{
if (Rx_Desc[i].Stat & DMA_RX_ERROR_MASK)
{
// What here? Reset the descriptor maybe and carry on?
}
else if ((Rx
Desc[i].Stat & DMARX
SEGMASK) != DMA
RXSEG
MASK)
{
// What does ‘DMARX
SEGMASK’ stand for?
}
else
{
pxDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL
ETHERNETFRAME_SIZE, 0 );
pucTemp = pxDescriptor->pucEthernetBuffer;
pxDescriptor->xDataLength = ((Rx_Desc[i].Stat >> 16) & 0x3FFF) - 4;
pxDescriptor->pucEthernetBuffer = (u8 *)(Rx_Desc[i].Addr);
Rx_Desc[i].Addr = (u32)pucTemp;
// Oops: you don’t have to do such difficult things !
// The IP-task will have done this and there are access functions for this, most importantly:
// NetworkBufferDescriptor
t *pxPacketBufferto_NetworkBuffer( void *pvBuffer )
// Here is some new code:
// Get the char buffer:
uint8_t *pucBuffer = (uint8_t *)(Rx_Desc[i].Addr);
// Lookup to which Network Buffer it belongs (using the back-pointer) :
NetworkBufferDescriptor_t *pxDescriptor = pxPacketBuffer_to_NetworkBuffer( (void *)pucBuffer );
// And take the buffer away, assign NULL or a new buffer:
Rx_Desc[i].Addr = (u32)NULL;
*( ( NetworkBufferDescriptor_t ** )
( pxDescriptor->pucEthernetBuffer - ipBUFFER_PADDING ) ) =
pxDescriptor;
if( eConsiderFrameForProcessing( pxDescriptor->pucEthernetBuffer )
== eProcessBuffer )
{
xRxEvent.eEventType = eNetworkRxEvent;
xRxEvent.pvData = ( void * ) pxDescriptor;
if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
{
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
iptraceETHERNET_RX_EVENT_LOST();
}
else
{
iptraceNETWORK_INTERFACE_RECEIVE();
}
}
else
{
// You’re dropping this packet but you could re-use it for the next DMA transaction
// That would save time
/* The Ethernet frame can be dropped, but the Ethernet buffer
must be released. */
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
}
}
// Don’t set “Stat = DMA
RXOWN” but “Stat |= DMA
RXOWN”
// Unless you want to clear all other flags.
Rx
Desc[i].Stat = DMARX_OWN;
// So what I’m missing here is reloading the ‘Addr’ field
if (++i == NUM_RX_BUF) i = 0;
}while ( (Rx_Desc[i].Stat & DMA_RX_OWN) == 0);
RxBufIndex = i;
}
}
~~~~
If things aren’t clear, please ask.
Regards.