# +TCP buffers exhausted after a while (ENC28J60)

**URL:** <https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522>\
**Category:** Libraries\
**Created:** [January 13, 2021, 9:48am UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522 "2021-01-13T09:48:15Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![arturlangner](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/arturlangner/32/1181_2.png) [@arturlangner](https://forums.freertos.org/u/arturlangner)\
**Post date:** [January 13, 2021, 9:48am UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/1 "2021-01-13T09:48:15Z")

</div>

I am running FreeRTOS+TCP on a Cortex-M4F with an ENC28J60 ethernet controller via SPI. After a while network buffers are exhausted.

I am using stock `BufferAllocation1.c` (I only added printf macros to track allocations and deallocations).

Increasing `ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS` only delays the time to exhaust. This bug happens reliably every time. DHCP works okay and sending UDP frames also works fine so most of the network driver is definitely correct. I have the usual network noise, nothing out of the ordinary. The bug does not go away after unplugging the network cable so I think it is not just a surge of traffic.

> **buffer allocation function**
>
> ```
> void vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS]){
> // Taken from:
> // https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/51590f627e9fe830fabd51958df250181f2712d2/portable/NetworkInterface/xilinx_ultrascale/NetworkInterface.c#L326
> #define niBUFFER_1_PACKET_SIZE (ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING)
> static uint8_t ucNetworkPackets[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE] __attribute__ ((aligned(4)));
> 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;
> }
> debugf("buffers allocated");
> }
> 
> ```

> **send function**
>
> ipconfigZERO\_COPY\_TX\_DRIVER is set to 1 (otherwise the breakpoint would be hit)
> 
> ```
> BaseType_t xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t xReleaseAfterSend){
> debugf("output len %d", (unsigned int)pxNetworkBuffer->xDataLength);
> enc28j60_send_packet(pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength );
> 
> /* Call the standard trace macro to log the send event. */
> iptraceNETWORK_INTERFACE_TRANSMIT();
> 
> if (xReleaseAfterSend){
> vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
> } else {
> __BKPT(101); //network stack is misconfigured?!
> }
> return pdTRUE;
> }
> 
> ```

> **send function (lower level)**
>
> The only hack is that I am using `data[-1]` to store an SPI opcode to transmit the whole thing in a single DMA transfer (and it works fine). Config has `#define ipconfigBUFFER_PADDING 4`.
> 
> ```
> void enc28j60_send_packet(uint8_t *data, uint16_t len){
> uint32_t tickstart = xTaskGetTickCount();
> while(enc28j60_rcr(ECON1) & ECON1_TXRTS){
> // TXRTS may not clear - ENC28J60 bug. We must reset
> // transmit logic in cause of Tx error
> if(enc28j60_rcr(EIR) & EIR_TXERIF){
> enc28j60_bfs(ECON1, ECON1_TXRST);
> enc28j60_bfc(ECON1, ECON1_TXRST);
> }
> 
> //If previous packet can't be sent within 100 ms, abort previous packet, ENC28J60 errata #12
> if((xTaskGetTickCount() - tickstart) > pdMS_TO_TICKS(100)){
> enc28j60_bfc(ECON1, ECON1_TXRTS);
> debugf("Errata #12 workaround");
> }
> }
> 
> enc28j60_wcr16(EWRPT, ENC28J60_TXSTART);
> 
> pin_eth_cs_low();
> uint8_t buffer[] = { ENC28J60_SPI_WBM, 0 };
> spi_transfer_blocking(sizeof(buffer), buffer);
> pin_eth_cs_high();
> 
> pin_eth_cs_low();
> //The buffer does have space in front of it, see vNetworkInterfaceAllocateRAMToBuffers()
> data[-1] = ENC28J60_SPI_WBM;
> spi_transfer_blocking(len + 1, &data[-1]);
> pin_eth_cs_high();
> 
> enc28j60_wcr16(ETXST, ENC28J60_TXSTART);
> enc28j60_wcr16(ETXND, ENC28J60_TXSTART + len);
> 
> enc28j60_bfs(ECON1, ECON1_TXRTS); // Request packet send
> }
> 
> ```

> **receive function**
>
> It is scheduled from GPIO interrupt handler via `xTimerPendFunctionCallFromISR`.
> 
> ```
> void prvReceivePacket(void *unused1 __attribute__ ((unused)), uint32_t unused2 __attribute__ ((unused))){
> enc28j60_bfc(EIE, EIE_INTIE); // mask enc28j60 interrupts
> 
> volatile uint8_t eir_flags = enc28j60_rcr(EIR);
> 
> if (eir_flags & EIR_PKTIF){ /* if there is pending packet */
> // retrieve packet from enc28j60
> uint16_t xBytesReceived = 0;
> uint16_t rxlen = 0;
> uint16_t status = 0;
> uint16_t temp;
> xNetworkBufferDescriptor_t *pxBufferDescriptor;
> xIPStackEvent_t xRxEvent;
> 
> enc28j60_read_buffer((void *)(&enc28j60_rxrdpt), sizeof(enc28j60_rxrdpt));
> enc28j60_read_buffer((void *)(&rxlen), sizeof(rxlen));
> enc28j60_read_buffer((void *)(&status), sizeof(status));
> 
> //debugf("Packet pending, rxrdpt %d, len %d, status %d", enc28j60_rxrdpt, rxlen, status);
> 
> if(status & 0x80){ //success
> // Throw out crc
> xBytesReceived = rxlen - 4;
> 
> // Allocate buffer for packet
> pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 );
> if( pxBufferDescriptor != NULL ){
> 
> // Read packet content
> enc28j60_read_buffer( pxBufferDescriptor->pucEthernetBuffer, xBytesReceived );
> pxBufferDescriptor->xDataLength = xBytesReceived;
> 
> /* 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();
> 
> debugf("Stack did not accept incoming packet");
> 
> } 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 event was lost because a network buffer was not available.
> Call the standard trace macro to log the occurrence. */
> iptraceETHERNET_RX_EVENT_LOST();
> debugf("Couldn't allocate buffer for incoming packet ******************************");
> }
> } else {
> debugf("Wrong status?");
> }
> 
> // Set Rx buffer guard to next packet
> if (enc28j60_rxrdpt == ENC28J60_RXSTART){
> temp = ENC28J60_RXEND;
> } else {
> temp = enc28j60_rxrdpt - 1;
> }
> 
> enc28j60_wcr16(ERXRDPT, temp);
> 
> // Set Rx pointer to next packet
> enc28j60_wcr16(ERDPT, enc28j60_rxrdpt);
> 
> // Decrement packet counter
> enc28j60_bfs(ECON2, ECON2_PKTDEC);
> 
> } else if (eir_flags & EIR_TXIF) {
> //debugf("enc28j60: transmit done\n");
> enc28j60_bfc(EIR, EIR_TXIF);
> } else if (eir_flags & EIR_TXERIF) {
> debugf("enc28j60: transmit error !!\n");
> enc28j60_bfc(EIR, EIR_TXERIF);
> } else if (eir_flags & EIR_RXERIF) {
> debugf("enc28j60: receive error !!\n");
> enc28j60_bfc(EIR, EIR_RXERIF);
> } else {
> debugf("enc28j60: unknown interrupt flag, we shouldn't be here\n");
> }
> 
> enc28j60_bfs(EIE, EIE_INTIE); // unmask enc28j60 interrupts
> }
> 
> ```

> **typical log**
>
> The numbers 2001xxxx next to allocation/deallocation are simply the pointers to `pxNetworkBuffer`. IP addresses make sense for my network.
> 
> ```
> 0 576:vNetworkInterfaceAllocateRAMToBuffers buffers allocated
> 0 90:main RTOS start
> 0 397:prvIPTask prvIPTask started
> 0 543:xNetworkInterfaceInitialise ----------- network interface init ----------------
> 0 534:enc28j60_init Ethernet MAC readback aa:bb:cc:dd:ee:aa
> 0 779:prvInitialiseDHCP prvInitialiseDHCP: start after 100 ticks
> 0 53:xApplicationDHCPHook DHCP phase 0 address 192.168.9.2
> 0 301:pxGetNetworkBufferWithDescriptor_INTERNAL 20015790 allocated from prvCreatePartDHCPMessage
> 0 1248:prvSendDHCPDiscover vDHCPProcess: discover
> 0 548:xNetworkInterfaceOutput output len 300
> 0 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015790 released by xNetworkInterfaceOutput
> 0 3062:vPrintResourceStats Network buffers: 15 lowest 14
> 0 301:pxGetNetworkBufferWithDescriptor_INTERNAL 200157B4 allocated from prvReceivePacket
> 0 1038:prvProcessDHCPReplies vDHCPProcess: offer ac10c9c0ip
> 0 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 00000000 released by FreeRTOS_ReleaseUDPPayloadBuffer
> 0 53:xApplicationDHCPHook DHCP phase 1 address 172.16.201.192
> 0 301:pxGetNetworkBufferWithDescriptor_INTERNAL 200157D8 allocated from prvCreatePartDHCPMessage
> 0 1203:prvSendDHCPRequest vDHCPProcess: reply ac10c9c0ip
> 0 548:xNetworkInterfaceOutput output len 307
> 0 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 200157D8 released by xNetworkInterfaceOutput
> 0 301:pxGetNetworkBufferWithDescriptor_INTERNAL 200157FC allocated from prvReceivePacket
> 0 1038:prvProcessDHCPReplies vDHCPProcess: offer ac10c9c0ip
> 0 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 00000000 released by FreeRTOS_ReleaseUDPPayloadBuffer
> 0 519:vDHCPProcess vDHCPProcess: acked ac10c9c0ip
> 0 301:pxGetNetworkBufferWithDescriptor_INTERNAL 20015820 allocated from FreeRTOS_OutputARPRequest
> 0 548:xNetworkInterfaceOutput output len 42
> 0 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015820 released by xNetworkInterfaceOutput
> 0 301:pxGetNetworkBufferWithDescriptor_INTERNAL 20015844 allocated from prvReceivePacket
> 0 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015844 released by prvProcessEthernetPacket
> 0 301:pxGetNetworkBufferWithDescriptor_INTERNAL 20015868 allocated from prvReceivePacket
> 0 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015868 released by prvProcessEthernetPacket
> 1 301:pxGetNetworkBufferWithDescriptor_INTERNAL 2001588C allocated from prvReceivePacket
> 1 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 2001588C released by prvProcessEthernetPacket
> 1 301:pxGetNetworkBufferWithDescriptor_INTERNAL 200158B0 allocated from prvReceivePacket
> 1 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 200158B0 released by prvProcessEthernetPacket
> 1 301:pxGetNetworkBufferWithDescriptor_INTERNAL 200158D4 allocated from prvReceivePacket
> 1 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 200158D4 released by prvProcessEthernetPacket
> 1 301:pxGetNetworkBufferWithDescriptor_INTERNAL 200158F8 allocated from prvReceivePacket
> 1 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 200158F8 released by prvProcessEthernetPacket
> 1 301:pxGetNetworkBufferWithDescriptor_INTERNAL 2001591C allocated from prvReceivePacket
> 1 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 2001591C released by prvProcessEthernetPacket
> 2 301:pxGetNetworkBufferWithDescriptor_INTERNAL 20015940 allocated from prvReceivePacket
> 2 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015940 released by prvProcessEthernetPacket
> 2 301:pxGetNetworkBufferWithDescriptor_INTERNAL 20015964 allocated from prvReceivePacket
> 2 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015964 released by prvProcessEthernetPacket
> 2 301:pxGetNetworkBufferWithDescriptor_INTERNAL 20015988 allocated from prvReceivePacket
> 2 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015988 released by prvProcessEthernetPacket
> 3 301:pxGetNetworkBufferWithDescriptor_INTERNAL 20015790 allocated from prvReceivePacket
> 3 369:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015790 released by prvProcessEthernetPacket
> 3 262:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BFA9 (valid 1)
> 3 301:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated from prvReceivePacket
> 3 360:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 3 262:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BFA9 (valid 1)
> 3 301:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated from prvReceivePacket
> 3 360:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 4 262:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BFA9 (valid 1)
> 4 301:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated from prvReceivePacket
> 4 360:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 4 262:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BFA9 (valid 1)
> 4 301:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated from prvReceivePacket
> 4 360:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 5 262:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BFA9 (valid 1)
> 5 301:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated from prvReceivePacket
> 5 360:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 5 262:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BFA9 (valid 1)
> 5 301:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated from prvReceivePacket
> 5 360:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 
> ```

Basically It looks like I can use every buffer just once. What am I doing wrong?  
Many thanks

---

<div class="post-metadata">

**Author:** ![rtel](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/rtel/32/31_2.png) [@rtel](https://forums.freertos.org/u/rtel)\
**Post date:** [January 13, 2021, 5:44pm UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/2 "2021-01-13T17:44:29Z")

</div>

There is nothing obvious so I will ask some questions that may be helpful only if they make you think of different ways to debug:

- I see 26 allocations and 16 releases. At this point how many free buffers do you expect to see? Have any of those released buffers ever got reused?
- If you step into pxGetNetworkBufferWithDescriptor() can you see why it says the buffer is invalid? For example, is it just because [the semaphore count](https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/portable/BufferManagement/BufferAllocation_1.c#L233) is unexpectedly zero or because something is corrected?
- Is 4 byte alignment adequate for the buffers?

---

<div class="post-metadata">

**Author:** ![arturlangner](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/arturlangner/32/1181_2.png) [@arturlangner](https://forums.freertos.org/u/arturlangner)\
**Post date:** [January 14, 2021, 1:04pm UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/3 "2021-01-14T13:04:48Z")

</div>

I found something “interesting”. Buffers work perfectly fine when DHCP is disabled. The numbers in ( ) are absolute allocation and deallocation counters, so I see that the buffers are deallocated as many times as they are allocated.

> **without DHCP**
>
> #define ipconfigUSE\_DHCP 0
> 
> ```
> 0 564:vNetworkInterfaceAllocateRAMToBuffers buffers allocated
> 0 397:prvIPTask prvIPTask started
> 0 531:xNetworkInterfaceInitialise ----------- network interface init ----------------
> 6 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015760 allocated (0) from prvReceivePacket
> 6 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015760 released (0) by prvProcessEthernetPacket
> 6 3062:vPrintResourceStats Network buffers: 15 lowest 14
> 6 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015784 allocated (1) from prvReceivePacket
> 6 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015784 released (1) by prvProcessEthernetPacket
> 6 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200157A8 allocated (2) from prvReceivePacket
> 6 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200157A8 released (2) by prvProcessEthernetPacket
> 6 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200157CC allocated (3) from prvReceivePacket
> 6 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200157CC released (3) by prvProcessEthernetPacket
> 7 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200157F0 allocated (4) from prvReceivePacket
> 7 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200157F0 released (4) by prvProcessEthernetPacket
> 8 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015814 allocated (5) from prvReceivePacket
> 8 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015814 released (5) by prvProcessEthernetPacket
> 9 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015838 allocated (6) from prvReceivePacket
> 9 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015838 released (6) by prvProcessEthernetPacket
> 9 302:pxGetNetworkBufferWithDescriptor_INTERNAL 2001585C allocated (7) from prvReceivePacket
> 9 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 2001585C released (7) by prvProcessEthernetPacket
> 9 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015880 allocated (8) from prvReceivePacket
> 9 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015880 released (8) by prvProcessEthernetPacket
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200158A4 allocated (9) from prvReceivePacket
> 10 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200158A4 released (9) by prvProcessEthernetPacket
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200158C8 allocated (10) from prvReceivePacket
> 10 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200158C8 released (10) by prvProcessEthernetPacket
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200158EC allocated (11) from prvReceivePacket
> 10 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200158EC released (11) by prvProcessEthernetPacket
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015910 allocated (12) from FreeRTOS_OutputARPRequest
> 10 536:xNetworkInterfaceOutput output len 42
> 10 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015910 released (12) by xNetworkInterfaceOutput
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015934 allocated (13) from prvReceivePacket
> 10 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015934 released (13) by prvProcessEthernetPacket
> 11 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015958 allocated (14) from prvReceivePacket
> 11 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015760 allocated (15) from prvReceivePacket
> 11 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015958 released (14) by prvProcessEthernetPacket
> 11 3062:vPrintResourceStats Network buffers: 14 lowest 13
> 11 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015760 released (15) by prvProcessEthernetPacket
> 12 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015784 allocated (16) from prvReceivePacket
> 12 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015784 released (16) by prvProcessEthernetPacket
> 12 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200157A8 allocated (17) from prvReceivePacket
> 12 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200157A8 released (17) by prvProcessEthernetPacket
> 12 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200157CC allocated (18) from prvReceivePacket
> 12 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200157CC released (18) by prvProcessEthernetPacket
> 13 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200157F0 allocated (19) from prvReceivePacket
> 13 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200157F0 released (19) by prvProcessEthernetPacket
> 13 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015814 allocated (20) from prvReceivePacket
> 13 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015814 released (20) by prvProcessEthernetPacket
> 13 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015838 allocated (21) from prvReceivePacket
> 13 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015838 released (21) by prvProcessEthernetPacket
> 13 302:pxGetNetworkBufferWithDescriptor_INTERNAL 2001585C allocated (22) from prvReceivePacket
> 13 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 2001585C released (22) by prvProcessEthernetPacket
> 13 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015880 allocated (23) from prvReceivePacket
> 13 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015880 released (23) by prvProcessEthernetPacket
> 14 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200158A4 allocated (24) from prvReceivePacket
> 14 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200158A4 released (24) by prvProcessEthernetPacket
> 
> ```

> **with DHCP**
>
> with #define ipconfigUSE\_DHCP 1 (DHCP hook is 0)
> 
> ```
> 0 564:vNetworkInterfaceAllocateRAMToBuffers buffers allocated
> 0 397:prvIPTask prvIPTask started
> 0 531:xNetworkInterfaceInitialise ----------- network interface init ----------------
> 0 779:prvInitialiseDHCP prvInitialiseDHCP: start after 100 ticks
> 0 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015798 allocated (0) from prvCreatePartDHCPMessage
> 0 1248:prvSendDHCPDiscover vDHCPProcess: discover
> 0 536:xNetworkInterfaceOutput output len 300
> 0 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015798 released (0) by xNetworkInterfaceOutput
> 0 3062:vPrintResourceStats Network buffers: 15 lowest 14
> 5 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200157BC allocated (1) from prvCreatePartDHCPMessage
> 5 1248:prvSendDHCPDiscover vDHCPProcess: discover
> 5 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 00000000 released (1) by FreeRTOS_ReleaseUDPPayloadBuffer
> 5 469:vDHCPProcess vDHCPProcess: timeout 4000 ticks
> 5 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200157E0 allocated (2) from prvReceivePacket
> 5 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200157E0 released (2) by prvProcessEthernetPacket
> 6 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015804 allocated (3) from prvReceivePacket
> 6 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015804 released (3) by prvProcessEthernetPacket
> 6 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015828 allocated (4) from prvReceivePacket
> 6 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015828 released (4) by prvProcessEthernetPacket
> 6 302:pxGetNetworkBufferWithDescriptor_INTERNAL 2001584C allocated (5) from prvReceivePacket
> 6 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 2001584C released (5) by prvProcessEthernetPacket
> 7 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015870 allocated (6) from prvReceivePacket
> 7 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015870 released (6) by prvProcessEthernetPacket
> 7 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015894 allocated (7) from prvReceivePacket
> 7 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200158B8 allocated (8) from prvReceivePacket
> 7 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015894 released (7) by prvProcessEthernetPacket
> 7 3062:vPrintResourceStats Network buffers: 14 lowest 13
> 7 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200158B8 released (8) by prvProcessEthernetPacket
> 7 302:pxGetNetworkBufferWithDescriptor_INTERNAL 200158DC allocated (9) from prvReceivePacket
> 7 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 200158DC released (9) by prvProcessEthernetPacket
> 7 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015900 allocated (10) from prvReceivePacket
> 7 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015900 released (10) by prvProcessEthernetPacket
> 7 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015924 allocated (11) from prvReceivePacket
> 7 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015924 released (11) by prvProcessEthernetPacket
> 7 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015948 allocated (12) from prvReceivePacket
> 7 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015948 released (12) by prvProcessEthernetPacket
> 8 302:pxGetNetworkBufferWithDescriptor_INTERNAL 2001596C allocated (13) from prvReceivePacket
> 8 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 2001596C released (13) by prvProcessEthernetPacket
> 8 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015990 allocated (14) from prvReceivePacket
> 8 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015990 released (14) by prvProcessEthernetPacket
> 8 302:pxGetNetworkBufferWithDescriptor_INTERNAL 20015798 allocated (15) from prvReceivePacket
> 8 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 8 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (16) from prvReceivePacket
> 8 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 8 372:vReleaseNetworkBufferAndDescriptor_INTERNAL 20015798 released (15) by prvProcessEthernetPacket
> 8 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 8 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (17) from prvReceivePacket
> 8 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 8 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 8 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (18) from prvReceivePacket
> 8 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 9 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 9 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (19) from prvReceivePacket
> 9 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 9 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 9 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (20) from prvReceivePacket
> 9 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 10 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (21) from prvReceivePacket
> 10 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 10 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (22) from prvReceivePacket
> 10 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 10 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (23) from prvReceivePacket
> 10 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 10 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (24) from prvReceivePacket
> 10 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 10 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (25) from prvReceivePacket
> 10 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 10 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (26) from prvReceivePacket
> 10 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 10 263:pxGetNetworkBufferWithDescriptor_INTERNAL pxGetNetworkBufferWithDescriptor: INVALID BUFFER: 0000BF0D (valid 1)
> 10 302:pxGetNetworkBufferWithDescriptor_INTERNAL 00000000 allocated (27) from prvReceivePacket
> 10 354:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 
> ```

The problem appears after the last buffer has been used once and the first one should be reused. The check in `pxGetNetworkBufferWithDescriptor` that fails is `listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxReturn->xBufferListItem ) )`

I also found out that the last “working” `uxListRemove( &( pxReturn->xBufferListItem ) );` in [https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/3372cd57cf9f18fdd0f02313c47ce6fd08b92e3c/portable/BufferManagement/BufferAllocation\_1.c#L244](https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/3372cd57cf9f18fdd0f02313c47ce6fd08b92e3c/portable/BufferManagement/BufferAllocation_1.c#L244) sets `xFreeBuffersList.xListEnd.pxNext` to zero, so obviously next time the list is accessed it has no next element.

I also tried to blindly increase the stack sizes of the IP task, idle task, timer tasks - no difference.

4 byte alignment is fine in my case.

I #defined `configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 1` and I am hitting configASSERT in `prvDetermineSocketSize` [https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/3372cd57cf9f18fdd0f02313c47ce6fd08b92e3c/FreeRTOS\_Sockets.c#L331](https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/3372cd57cf9f18fdd0f02313c47ce6fd08b92e3c/FreeRTOS_Sockets.c#L331) (call stack: prvIPTask-\>vDHCPProcess-\>prvInitialiseDHCP-\>prvCreateDHCPSocket-\>FreeRTOS\_Socket)

---

<div class="post-metadata">

**Author:** ![kanherea](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/kanherea/32/4106_2.png) [@kanherea](https://forums.freertos.org/u/kanherea)\
**Post date:** [January 15, 2021, 5:58am UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/4 "2021-01-15T05:58:54Z")

</div>

Hello!

This is a very interesting problem. It seems that the buffers are not being released properly and thus not being added to the list of free buffers.

Can you try to use version 2.3.1 of FreeRTOS+TCP and see whether this problem persists? (The version can be found [here](https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/tree/V2.3.1)).  
If the problem is with the current version of the TCP stack, it will help us zero in on the problem.

Till that time, I shall try to find out what is wrong here.

---

<div class="post-metadata">

**Author:** ![htibosch](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/htibosch/32/62_2.png) [@htibosch](https://forums.freertos.org/u/htibosch)\
**Post date:** [January 15, 2021, 7:29am UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/5 "2021-01-15T07:29:55Z")

</div>

Where did you get a network interface for ENC28J60? Is that public, could I have a look at it?  
Thanks.

---

<div class="post-metadata">

**Author:** ![arturlangner](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/arturlangner/32/1181_2.png) [@arturlangner](https://forums.freertos.org/u/arturlangner)\
**Post date:** [January 18, 2021, 2:57pm UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/6 "2021-01-18T14:57:34Z")

</div>

I updated FreeRTOS to v10.4.3 and TCP to v2.3.1. I am mpw hitting this assert ` listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );` in [https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/578d040659fb7d0da039f662baca2cc2fe535563/list.c#L94](https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/578d040659fb7d0da039f662baca2cc2fe535563/list.c#L94)

This is called from `vReleaseNetworkBufferAndDescriptor()`, called from `prvSendDHCPCDiscover()` [https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/95a5df5473727cce2fd3a34ac5a437fcb285e3a8/FreeRTOS\_DHCP.c#L1141](https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/95a5df5473727cce2fd3a34ac5a437fcb285e3a8/FreeRTOS_DHCP.c#L1141). **It happens on the second deallocation** (no matter how many buffers I have).

Everything else apart from the network stack works fine (tasks, queues, semaphores) so I guess I would run into much bigger trouble earlier if I had something grossly misconfigured (it is a bog standard M4F with gcc).

What is even more strange - I have switched to BufferAllocation\_2.c (with an 8KB heap). The device starts up fine, gets address from DHCP. I can ping it. After a longer while it also runs out of buffers.

My driver is based on [https://github.com/v-zaburdaev/FreeRTOS-TCP-enc28j60](https://github.com/v-zaburdaev/FreeRTOS-TCP-enc28j60)

> **header**
>
> ```
> #pragma once
> #include <stdint.h>
> 
> #define ENC28J60_BUFSIZE 0x2000
> #define ENC28J60_RXSIZE 0x1A00
> #define ENC28J60_BUFEND (ENC28J60_BUFSIZE-1)
> 
> #define ENC28J60_MAXFRAME 1518
> 
> #define ENC28J60_RXSTART 0
> #define ENC28J60_RXEND (ENC28J60_RXSIZE-1)
> #define ENC28J60_TXSTART ENC28J60_RXSIZE
> 
> #define ENC28J60_SPI_RCR 0x00
> #define ENC28J60_SPI_RBM 0x3A
> #define ENC28J60_SPI_WCR 0x40
> #define ENC28J60_SPI_WBM 0x7A
> #define ENC28J60_SPI_BFS 0x80
> #define ENC28J60_SPI_BFC 0xA0
> #define ENC28J60_SPI_SC 0xFF
> 
> #define ENC28J60_ADDR_MASK 0x1F
> #define ENC28J60_COMMON_CR 0x1B
> 
> /*
> * Main registers
> */
> 
> #define EIE 0x1B
> #define EIR 0x1C
> #define ESTAT 0x1D
> #define ECON2 0x1E
> #define ECON1 0x1F
> 
> // Buffer read pointer
> #define ERDPTL 0x00
> #define ERDPTH 0x01
> #define ERDPT ERDPTL
> 
> // Buffer write pointer
> #define EWRPTL 0x02
> #define EWRPTH 0x03
> #define EWRPT EWRPTL
> 
> // Tx packet start pointer
> #define ETXSTL 0x04
> #define ETXSTH 0x05
> #define ETXST ETXSTL
> 
> // Tx packet end pointer
> #define ETXNDL 0x06
> #define ETXNDH 0x07
> #define ETXND ETXNDL
> 
> // Rx FIFO start pointer
> #define ERXSTL 0x08
> #define ERXSTH 0x09
> #define ERXST ERXSTL
> 
> // Rx FIFO end pointer
> #define ERXNDL 0x0A
> #define ERXNDH 0x0B
> #define ERXND ERXNDL
> 
> // Rx FIFO read pointer
> #define ERXRDPTL 0x0C
> #define ERXRDPTH 0x0D
> #define ERXRDPT ERXRDPTL
> 
> // Rx FIFO write pointer
> #define ERXWRPTL 0x0E
> #define ERXWRPTH 0x0F
> #define ERXWRPT ERXWRPTL
> 
> // DMA source block start pointer
> #define EDMASTL 0x10
> #define EDMASTH 0x11
> #define EDMAST EDMASTL
> 
> // DMA source block end pointer
> #define EDMANDL 0x12
> #define EDMANDH 0x13
> #define EDMAND EDMANDL
> 
> // DMA destination pointer
> #define EDMADSTL 0x14
> #define EDMADSTH 0x15
> #define EDMADST EDMADSTL
> 
> // DMA checksum
> #define EDMACSL 0x16
> #define EDMACSH 0x17
> #define EDMACS EDMACSL
> 
> // Hash table registers
> #define EHT0 (0x00 | 0x20)
> #define EHT1 (0x01 | 0x20)
> #define EHT2 (0x02 | 0x20)
> #define EHT3 (0x03 | 0x20)
> #define EHT4 (0x04 | 0x20)
> #define EHT5 (0x05 | 0x20)
> #define EHT6 (0x06 | 0x20)
> #define EHT7 (0x07 | 0x20)
> 
> // Pattern match registers
> #define EPMM0 (0x08 | 0x20)
> #define EPMM1 (0x09 | 0x20)
> #define EPMM2 (0x0A | 0x20)
> #define EPMM3 (0x0B | 0x20)
> #define EPMM4 (0x0C | 0x20)
> #define EPMM5 (0x0D | 0x20)
> #define EPMM6 (0x0E | 0x20)
> #define EPMM7 (0x0F | 0x20)
> #define EPMCSL (0x10 | 0x20)
> #define EPMCSH (0x11 | 0x20)
> #define EPMOL (0x14 | 0x20)
> #define EPMOH (0x15 | 0x20)
> 
> // Wake-on-LAN interrupt registers
> #define EWOLIE (0x16 | 0x20)
> #define EWOLIR (0x17 | 0x20)
> 
> // Receive filters mask
> #define ERXFCON (0x18 | 0x20)
> 
> // Packet counter
> #define EPKTCNT (0x19 | 0x20)
> 
> // MAC control registers
> #define MACON1 (0x00 | 0x40 | 0x80)
> #define MACON2 (0x01 | 0x40 | 0x80)
> #define MACON3 (0x02 | 0x40 | 0x80)
> #define MACON4 (0x03 | 0x40 | 0x80)
> 
> // MAC Back-to-back gap
> #define MABBIPG (0x04 | 0x40 | 0x80)
> 
> // MAC Non back-to-back gap
> #define MAIPGL (0x06 | 0x40 | 0x80)
> #define MAIPGH (0x07 | 0x40 | 0x80)
> 
> // Collision window & rexmit timer
> #define MACLCON1 (0x08 | 0x40 | 0x80)
> #define MACLCON2 (0x09 | 0x40 | 0x80)
> 
> // Max frame length
> #define MAMXFLL (0x0A | 0x40 | 0x80)
> #define MAMXFLH (0x0B | 0x40 | 0x80)
> #define MAMXFL MAMXFLL
> 
> // MAC-PHY support register
> #define MAPHSUP (0x0D | 0x40 | 0x80)
> #define MICON (0x11 | 0x40 | 0x80)
> 
> // MII registers
> #define MICMD (0x12 | 0x40 | 0x80)
> #define MIREGADR (0x14 | 0x40 | 0x80)
> 
> #define MIWRL (0x16 | 0x40 | 0x80)
> #define MIWRH (0x17 | 0x40 | 0x80)
> #define MIWR MIWRL
> 
> #define MIRDL (0x18 | 0x40 | 0x80)
> #define MIRDH (0x19 | 0x40 | 0x80)
> #define MIRD MIRDL
> 
> // MAC Address
> #define MAADR1 (0x00 | 0x60 | 0x80)
> #define MAADR0 (0x01 | 0x60 | 0x80)
> #define MAADR3 (0x02 | 0x60 | 0x80)
> #define MAADR2 (0x03 | 0x60 | 0x80)
> #define MAADR5 (0x04 | 0x60 | 0x80)
> #define MAADR4 (0x05 | 0x60 | 0x80)
> 
> // Built-in self-test
> #define EBSTSD (0x06 | 0x60)
> #define EBSTCON (0x07 | 0x60)
> #define EBSTCSL (0x08 | 0x60)
> #define EBSTCSH (0x09 | 0x60)
> #define MISTAT (0x0A | 0x60 | 0x80)
> 
> // Revision ID
> #define EREVID (0x12 | 0x60)
> 
> // Clock output control register
> #define ECOCON (0x15 | 0x60)
> 
> // Flow control registers
> #define EFLOCON (0x17 | 0x60)
> #define EPAUSL (0x18 | 0x60)
> #define EPAUSH (0x19 | 0x60)
> 
> // PHY registers
> #define PHCON1 0x00
> #define PHSTAT1 0x01
> #define PHID1 0x02
> #define PHID2 0x03
> #define PHCON2 0x10
> #define PHSTAT2 0x11
> #define PHIE 0x12
> #define PHIR 0x13
> #define PHLCON 0x14
> 
> // EIE
> #define EIE_INTIE 0x80
> #define EIE_PKTIE 0x40
> #define EIE_DMAIE 0x20
> #define EIE_LINKIE 0x10
> #define EIE_TXIE 0x08
> #define EIE_WOLIE 0x04
> #define EIE_TXERIE 0x02
> #define EIE_RXERIE 0x01
> 
> // EIR
> #define EIR_PKTIF 0x40
> #define EIR_DMAIF 0x20
> #define EIR_LINKIF 0x10
> #define EIR_TXIF 0x08
> #define EIR_WOLIF 0x04
> #define EIR_TXERIF 0x02
> #define EIR_RXERIF 0x01
> 
> // ESTAT
> #define ESTAT_INT 0x80
> #define ESTAT_LATECOL 0x10
> #define ESTAT_RXBUSY 0x04
> #define ESTAT_TXABRT 0x02
> #define ESTAT_CLKRDY 0x01
> 
> // ECON2
> #define ECON2_AUTOINC 0x80
> #define ECON2_PKTDEC 0x40
> #define ECON2_PWRSV 0x20
> #define ECON2_VRPS 0x08
> 
> // ECON1
> #define ECON1_TXRST 0x80
> #define ECON1_RXRST 0x40
> #define ECON1_DMAST 0x20
> #define ECON1_CSUMEN 0x10
> #define ECON1_TXRTS 0x08
> #define ECON1_RXEN 0x04
> #define ECON1_BSEL1 0x02
> #define ECON1_BSEL0 0x01
> 
> // EWOLIE
> #define EWOLIE_UCWOLIE 0x80
> #define EWOLIE_AWOLIE 0x40
> #define EWOLIE_PMWOLIE 0x10
> #define EWOLIE_MPWOLIE 0x08
> #define EWOLIE_HTWOLIE 0x04
> #define EWOLIE_MCWOLIE 0x02
> #define EWOLIE_BCWOLIE 0x01
> 
> // EWOLIR
> #define EWOLIR_UCWOLIF 0x80
> #define EWOLIR_AWOLIF 0x40
> #define EWOLIR_PMWOLIF 0x10
> #define EWOLIR_MPWOLIF 0x08
> #define EWOLIR_HTWOLIF 0x04
> #define EWOLIR_MCWOLIF 0x02
> #define EWOLIR_BCWOLIF 0x01
> 
> // ERXFCON
> #define ERXFCON_UCEN 0x80
> #define ERXFCON_ANDOR 0x40
> #define ERXFCON_CRCEN 0x20
> #define ERXFCON_PMEN 0x10
> #define ERXFCON_MPEN 0x08
> #define ERXFCON_HTEN 0x04
> #define ERXFCON_MCEN 0x02
> #define ERXFCON_BCEN 0x01
> 
> // MACON1
> #define MACON1_LOOPBK 0x10
> #define MACON1_TXPAUS 0x08
> #define MACON1_RXPAUS 0x04
> #define MACON1_PASSALL 0x02
> #define MACON1_MARXEN 0x01
> 
> // MACON2
> #define MACON2_MARST 0x80
> #define MACON2_RNDRST 0x40
> #define MACON2_MARXRST 0x08
> #define MACON2_RFUNRST 0x04
> #define MACON2_MATXRST 0x02
> #define MACON2_TFUNRST 0x01
> 
> // MACON3
> #define MACON3_PADCFG2 0x80
> #define MACON3_PADCFG1 0x40
> #define MACON3_PADCFG0 0x20
> #define MACON3_TXCRCEN 0x10
> #define MACON3_PHDRLEN 0x08
> #define MACON3_HFRMEN 0x04
> #define MACON3_FRMLNEN 0x02
> #define MACON3_FULDPX 0x01
> 
> // MACON4
> #define MACON4_DEFER 0x40
> #define MACON4_BPEN 0x20
> #define MACON4_NOBKOFF 0x10
> #define MACON4_LONGPRE 0x02
> #define MACON4_PUREPRE 0x01
> 
> // MAPHSUP
> #define MAPHSUP_RSTINTFC 0x80
> #define MAPHSUP_RSTRMII 0x08
> 
> // MICON
> #define MICON_RSTMII 0x80
> 
> // MICMD
> #define MICMD_MIISCAN 0x02
> #define MICMD_MIIRD 0x01
> 
> // EBSTCON
> #define EBSTCON_PSV2 0x80
> #define EBSTCON_PSV1 0x40
> #define EBSTCON_PSV0 0x20
> #define EBSTCON_PSEL 0x10
> #define EBSTCON_TMSEL1 0x08
> #define EBSTCON_TMSEL0 0x04
> #define EBSTCON_TME 0x02
> #define EBSTCON_BISTST 0x01
> 
> // MISTAT
> #define MISTAT_NVALID 0x04
> #define MISTAT_SCAN 0x02
> #define MISTAT_BUSY 0x01
> 
> // ECOCON
> #define ECOCON_COCON2 0x04
> #define ECOCON_COCON1 0x02
> #define ECOCON_COCON0 0x01
> 
> // EFLOCON
> #define EFLOCON_FULDPXS 0x04
> #define EFLOCON_FCEN1 0x02
> #define EFLOCON_FCEN0 0x01
> 
> // PHCON1
> #define PHCON1_PRST 0x8000
> #define PHCON1_PLOOPBK 0x4000
> #define PHCON1_PPWRSV 0x0800
> #define PHCON1_PDPXMD 0x0100
> 
> // PHSTAT1
> #define PHSTAT1_PFDPX 0x1000
> #define PHSTAT1_PHDPX 0x0800
> #define PHSTAT1_LLSTAT 0x0004
> #define PHSTAT1_JBSTAT 0x0002
> 
> // PHCON2
> #define PHCON2_FRCLNK 0x4000
> #define PHCON2_TXDIS 0x2000
> #define PHCON2_JABBER 0x0400
> #define PHCON2_HDLDIS 0x0100
> 
> // PHSTAT2
> #define PHSTAT2_TXSTAT 0x2000
> #define PHSTAT2_RXSTAT 0x1000
> #define PHSTAT2_COLSTAT 0x0800
> #define PHSTAT2_LSTAT 0x0400
> #define PHSTAT2_DPXSTAT 0x0200
> #define PHSTAT2_PLRITY 0x0010
> 
> // PHIE
> #define PHIE_PLNKIE 0x0010
> #define PHIE_PGEIE 0x0002
> 
> // PHIR
> #define PHIR_PLNKIF 0x0010
> #define PHIR_PGIF 0x0004
> 
> // PHLCON
> #define PHLCON_LACFG3 0x0800
> #define PHLCON_LACFG2 0x0400
> #define PHLCON_LACFG1 0x0200
> #define PHLCON_LACFG0 0x0100
> #define PHLCON_LBCFG3 0x0080
> #define PHLCON_LBCFG2 0x0040
> #define PHLCON_LBCFG1 0x0020
> #define PHLCON_LBCFG0 0x0010
> #define PHLCON_LFRQ1 0x0008
> #define PHLCON_LFRQ0 0x0004
> #define PHLCON_STRCH 0x0002
> 
> ```

> **source**
>
> ```
> /*
> * FreeRTOS+TCP V2.3.2
> * Copyright (C) 2020 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://aws.amazon.com/freertos
> * http://www.FreeRTOS.org
> */
> 
> #include "FreeRTOS.h"
> #include "list.h"
> #include "FreeRTOS_IP.h"
> #include "FreeRTOS_IP_Private.h"
> #include "enc28j60_registers.h"
> #include "network.h"
> #include "NetworkBufferManagement.h"
> #include "pins.h"
> #include "rtos_common.h"
> #include <spidrv.h>
> 
> //Ethernet SPI uses USART2 at location #4
> #define USART_ETH USART2
> #define USART_ETH_LOCATION 4
> 
> static SemaphoreHandle_t _spi_semaphore;
> static SPIDRV_HandleData_t spiHandleData;
> static SPIDRV_Handle_t _spi_handle = &spiHandleData;
> 
> /* 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
> 
> // -------------------------- ENC28J60 driver functions -----------------------
> static volatile uint8_t enc28j60_current_bank = 0;
> static volatile uint16_t enc28j60_rxrdpt = 0;
> 
> static void SPI_transfer_complete_cb_fromISR(SPIDRV_Handle_t spi_handle __attribute__ ((unused)), Ecode_t transferStatus, int itemsTransferred __attribute__ ((unused))){
> if ( transferStatus != ECODE_EMDRV_SPIDRV_OK){
> __BKPT(1); //what happened?!
> }
> BaseType_t xHigherPriorityTaskWoken = pdFALSE;
> xSemaphoreGiveFromISR(_spi_semaphore, &xHigherPriorityTaskWoken);
> portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
> }
> 
> static void spi_transfer_blocking(uint32_t length, uint8_t *data){
> SPIDRV_MTransfer(_spi_handle, data/*tx buffer*/, data/*rx buffer*/, length, SPI_transfer_complete_cb_fromISR);
> xSemaphoreTake(_spi_semaphore, portMAX_DELAY); //the DMA callback will unlock the semaphore
> }
> 
> void enc28j60_init_once(void){
> 
> static StaticSemaphore_t eth_spi_semaphore_buffer;
> _spi_semaphore = xSemaphoreCreateBinaryStatic(&eth_spi_semaphore_buffer);
> 
> SPIDRV_Init_t init = {
> .port = USART_ETH,
> .portLocationClk = USART_ETH_LOCATION,
> .portLocationTx = USART_ETH_LOCATION,
> .portLocationRx = USART_ETH_LOCATION,
> .bitRate = 8000000, //8MHz is good for ENC28J60 errata #1 (if we bought an old revision of the chip)
> .frameLength = 8,
> .dummyTxValue = 0x00,
> .type = spidrvMaster,
> .bitOrder = spidrvBitOrderMsbFirst,
> .clockMode = spidrvClockMode0,
> .csControl = spidrvCsControlApplication,
> };
> SPIDRV_Init(_spi_handle, &init);
> 
> GPIO_PinModeSet(PORT_ETH_CS, PIN_ETH_CS, gpioModePushPull, 1/*output level*/);
> 
> //Strange - after the driver initializes the peripheral the MISO pin has to be reconfigured again
> GPIO_PinModeSet(PORT_ETH_MISO, PIN_ETH_MISO, gpioModeInputPull, 1/*pullup*/);
> 
> debugf("Ethernet controller SPI initialized");
> 
> //configure PF2 for IRQ
> GPIO_IntConfig(PORT_ETH_INT, PIN_ETH_INT, false/*rising*/, true/*falling*/, true/*enable*/);
> NVIC_SetPriority(GPIO_EVEN_IRQn, 7); //low priority
> NVIC_EnableIRQ(GPIO_EVEN_IRQn);
> }
> 
> // Generic SPI read command
> static uint8_t enc28j60_read_op(uint8_t cmd, uint8_t adr){
> pin_eth_cs_low();
> //always transmit 3 bytes
> uint8_t buffer[] = {
> cmd | (adr & ENC28J60_ADDR_MASK),
> 0xFF,
> 0xFF
> };
> //debugf("TX %02X %02X %02X", buffer[0], buffer[1], buffer[2]);
> spi_transfer_blocking(sizeof(buffer), buffer);
> pin_eth_cs_high();
> //debugf("RX %02X %02X %02X", buffer[0], buffer[1], buffer[2]);
> 
> if(adr & 0x80){ // throw out dummy byte when reading MII/MAC register
> return buffer[2];
> }
> return buffer[1];
> }
> 
> // Generic SPI write command
> static void enc28j60_write_op(uint8_t cmd, uint8_t adr, uint8_t data){
> pin_eth_cs_low();
> 
> uint8_t buffer[] = {
> cmd | (adr & ENC28J60_ADDR_MASK),
> data
> };
> spi_transfer_blocking(sizeof(buffer), buffer);
> pin_eth_cs_high();
> }
> 
> // Set register bank
> static void enc28j60_set_bank(uint8_t adr){
> uint8_t bank;
> 
> if( (adr & ENC28J60_ADDR_MASK) < ENC28J60_COMMON_CR )
> {
> bank = (adr >> 5) & 0x03; //BSEL1|BSEL0=0x03
> if(bank != enc28j60_current_bank)
> {
> enc28j60_write_op(ENC28J60_SPI_BFC, ECON1, 0x03);
> enc28j60_write_op(ENC28J60_SPI_BFS, ECON1, bank);
> enc28j60_current_bank = bank;
> }
> }
> }
> 
> // Read register
> static uint8_t enc28j60_rcr(uint8_t adr){
> enc28j60_set_bank(adr);
> return enc28j60_read_op(ENC28J60_SPI_RCR, adr);
> }
> 
> // Read register pair
> static uint16_t enc28j60_rcr16(uint8_t adr){
> enc28j60_set_bank(adr);
> return enc28j60_read_op(ENC28J60_SPI_RCR, adr) |
> (enc28j60_read_op(ENC28J60_SPI_RCR, adr+1) << 8);
> }
> 
> // Write register
> static void enc28j60_wcr(uint8_t adr, uint8_t arg){
> enc28j60_set_bank(adr);
> enc28j60_write_op(ENC28J60_SPI_WCR, adr, arg);
> }
> 
> // Write register pair
> static void enc28j60_wcr16(uint8_t adr, uint16_t arg){
> enc28j60_set_bank(adr);
> enc28j60_write_op(ENC28J60_SPI_WCR, adr, arg);
> enc28j60_write_op(ENC28J60_SPI_WCR, adr+1, arg>>8);
> }
> 
> // Clear bits in register (reg &= ~mask)
> static void enc28j60_bfc(uint8_t adr, uint8_t mask){
> enc28j60_set_bank(adr);
> enc28j60_write_op(ENC28J60_SPI_BFC, adr, mask);
> }
> 
> // Set bits in register (reg |= mask)
> static void enc28j60_bfs(uint8_t adr, uint8_t mask){
> enc28j60_set_bank(adr);
> enc28j60_write_op(ENC28J60_SPI_BFS, adr, mask);
> }
> 
> //TODO: find out where to hook up this function
> void prvSoftTimerInt(void *unused __attribute__ ((unused)), uint32_t unused2 __attribute__ ((unused)))
> {
> if (enc28j60_rcr(EIR) == 0xFF) {
> if (xTimerPendFunctionCall( prvSoftTimerInt, NULL, 0, 10) == pdFALSE) {
> debugf("PANIC: enc28j60: daemon's queue full\n");
> }
> } else {
> taskENTER_CRITICAL();
> GPIO_EVEN_IRQHandler();
> taskEXIT_CRITICAL();
> }
> 
> }
> 
> // Read Rx/Tx buffer (at ERDPT)
> static void enc28j60_read_buffer(uint8_t *buf, uint16_t len){
> //debugf("ptr %08X, len %d", (unsigned int)buf, len);
> pin_eth_cs_low();
> //The buffer does have space in front of it, see vNetworkInterfaceAllocateRAMToBuffers()
> uint8_t opcode[] = { ENC28J60_SPI_RBM };
> spi_transfer_blocking(sizeof(opcode), opcode);
> spi_transfer_blocking(len, buf);
> pin_eth_cs_high();
> }
> 
> // Read PHY register
> static uint16_t enc28j60_read_phy(uint8_t adr){
> enc28j60_wcr(MIREGADR, adr);
> enc28j60_bfs(MICMD, MICMD_MIIRD);
> while(enc28j60_rcr(MISTAT) & MISTAT_BUSY){}
> enc28j60_bfc(MICMD, MICMD_MIIRD);
> return enc28j60_rcr16(MIRD);
> }
> 
> // Write PHY register
> static void enc28j60_write_phy(uint8_t adr, uint16_t data){
> enc28j60_wcr(MIREGADR, adr);
> enc28j60_wcr16(MIWR, data);
> while(enc28j60_rcr(MISTAT) & MISTAT_BUSY) {} //function call will block anyway, so no sleeps are required in this loop
> }
> 
> static void enc28j60_soft_reset(){
> pin_eth_cs_low();
> uint8_t buffer[] = {
> ENC28J60_SPI_SC
> };
> spi_transfer_blocking(sizeof(buffer), buffer);
> pin_eth_cs_high();
> 
> enc28j60_current_bank = 0;
> rtos_sleep_ms(5);
> }
> 
> void prvReceivePacket(void *unused1 __attribute__ ((unused)), uint32_t unused2 __attribute__ ((unused))){
> //debugf("rx from service task");
> enc28j60_bfc(EIE, EIE_INTIE); // mask enc28j60 interrupts
> 
> volatile uint8_t eir_flags = enc28j60_rcr(EIR);
> 
> if (eir_flags & EIR_PKTIF){ /* if there is pending packet */
> // retrieve packet from enc28j60
> uint16_t xBytesReceived = 0;
> uint16_t rxlen = 0;
> uint16_t status = 0;
> uint16_t temp;
> xNetworkBufferDescriptor_t *pxBufferDescriptor;
> xIPStackEvent_t xRxEvent;
> 
> enc28j60_read_buffer((void *)(&enc28j60_rxrdpt), sizeof(enc28j60_rxrdpt));
> enc28j60_read_buffer((void *)(&rxlen), sizeof(rxlen));
> enc28j60_read_buffer((void *)(&status), sizeof(status));
> 
> //debugf("Packet pending, rxrdpt %d, len %d, status %d", enc28j60_rxrdpt, rxlen, status);
> 
> if(status & 0x80){ //success
> // Throw out crc
> xBytesReceived = rxlen - 4;
> 
> // Allocate buffer for packet
> pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 );
> if( pxBufferDescriptor != NULL ){
> 
> // Read packet content
> enc28j60_read_buffer( pxBufferDescriptor->pucEthernetBuffer, xBytesReceived );
> pxBufferDescriptor->xDataLength = xBytesReceived;
> 
> /* 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();
> 
> debugf("Stack did not accept incoming packet");
> 
> } 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 event was lost because a network buffer was not available.
> Call the standard trace macro to log the occurrence. */
> iptraceETHERNET_RX_EVENT_LOST();
> debugf("Couldn't allocate buffer for incoming packet ******************************");
> }
> } else {
> debugf("Wrong status?");
> }
> 
> // Set Rx buffer guard to next packet
> if (enc28j60_rxrdpt == ENC28J60_RXSTART){
> temp = ENC28J60_RXEND;
> } else {
> temp = enc28j60_rxrdpt - 1;
> }
> 
> enc28j60_wcr16(ERXRDPT, temp);
> 
> // Set Rx pointer to next packet
> enc28j60_wcr16(ERDPT, enc28j60_rxrdpt);
> 
> // Decrement packet counter
> enc28j60_bfs(ECON2, ECON2_PKTDEC);
> 
> } else if (eir_flags & EIR_TXIF) {
> //debugf("enc28j60: transmit done\n");
> enc28j60_bfc(EIR, EIR_TXIF);
> } else if (eir_flags & EIR_TXERIF) {
> debugf("enc28j60: transmit error !!\n");
> enc28j60_bfc(EIR, EIR_TXERIF);
> } else if (eir_flags & EIR_RXERIF) {
> debugf("enc28j60: receive error !!\n");
> enc28j60_bfc(EIR, EIR_RXERIF);
> } else {
> debugf("enc28j60: unknown interrupt flag, we shouldn't be here\n");
> }
> 
> enc28j60_bfs(EIE, EIE_INTIE); // unmask enc28j60 interrupts
> }
> 
> void GPIO_EVEN_IRQHandler(void){
> GPIO_IntClear(0x04); //pin2 generates 0x4 interrupt mask, strange, see figure 34.6 in reference manual
> //SEGGER_RTT_printf(0, "IF %08X", GPIO->IF); //don't use debugf in ISR
> 
> BaseType_t xHigherPriorityTaskWoken = pdFALSE;
> if (xTimerPendFunctionCallFromISR( prvReceivePacket, NULL, 0, &xHigherPriorityTaskWoken ) == pdFALSE) {
> __BKPT(170);
> debugf("PANIC: enc28j60: daemon's queue full\n");
> while(1);
> }
> portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
> }
> 
> void enc28j60_send_packet(uint8_t *data, uint16_t len){
> uint32_t tickstart = xTaskGetTickCount();
> while(enc28j60_rcr(ECON1) & ECON1_TXRTS){
> // TXRTS may not clear - ENC28J60 bug. We must reset
> // transmit logic in cause of Tx error
> 
> if(enc28j60_rcr(EIR) & EIR_TXERIF){
> enc28j60_bfs(ECON1, ECON1_TXRST);
> enc28j60_bfc(ECON1, ECON1_TXRST);
> }
> 
> //If previous packet can't be sent within 100 ms, abort previous packet, ENC28J60 errata #12
> if((xTaskGetTickCount() - tickstart) > pdMS_TO_TICKS(100)){
> enc28j60_bfc(ECON1, ECON1_TXRTS);
> debugf("Errata #12 workaround");
> }
> }
> 
> enc28j60_wcr16(EWRPT, ENC28J60_TXSTART);
> 
> pin_eth_cs_low();
> uint8_t buffer[] = { ENC28J60_SPI_WBM, 0 };
> spi_transfer_blocking(sizeof(buffer), buffer);
> pin_eth_cs_high();
> 
> pin_eth_cs_low();
> //The buffer does have space in front of it, see vNetworkInterfaceAllocateRAMToBuffers()
> data[-1] = ENC28J60_SPI_WBM;
> spi_transfer_blocking(len + 1, &data[-1]);
> pin_eth_cs_high();
> 
> enc28j60_wcr16(ETXST, ENC28J60_TXSTART);
> enc28j60_wcr16(ETXND, ENC28J60_TXSTART + len);
> 
> enc28j60_bfs(ECON1, ECON1_TXRTS); // Request packet send
> }
> 
> uint8_t enc28j60_init(uint8_t *macadr){
> 
> //this function is called by the TCP stack
> 
> //pins have been initialized by pins_init()
> //SPI has been initialized by enc28j60_init_once() called from network.c
> 
> //reset is held low by pins_init()
> rtos_sleep_ms(50);
> pin_eth_rst_high();
> rtos_sleep_ms(50);
> #if 0
> debugf("ECON1 = %02X", enc28j60_read_op(ENC28J60_SPI_RCR, ECON1));
> #endif
> // Reset ENC28J60
> enc28j60_soft_reset();
> #if 0
> //trivial test if we can switch register banks, this proves that SPI is okay
> debugf("ECON1 = %02X", enc28j60_read_op(ENC28J60_SPI_RCR, ECON1));
> enc28j60_write_op(ENC28J60_SPI_WCR, ECON1, 0x01);
> debugf("ECON1 = %02X", enc28j60_read_op(ENC28J60_SPI_RCR, ECON1));
> enc28j60_write_op(ENC28J60_SPI_WCR, ECON1, 0x02);
> debugf("ECON1 = %02X", enc28j60_read_op(ENC28J60_SPI_RCR, ECON1));
> enc28j60_write_op(ENC28J60_SPI_WCR, ECON1, 0x03);
> debugf("ECON1 = %02X", enc28j60_read_op(ENC28J60_SPI_RCR, ECON1));
> #endif
> // Setup Rx/Tx buffer
> enc28j60_wcr16(ERXST, ENC28J60_RXSTART);
> enc28j60_rcr16(ERXST);
> enc28j60_wcr16(ERXRDPT, ENC28J60_RXSTART);
> enc28j60_wcr16(ERXND, ENC28J60_RXEND);
> enc28j60_rxrdpt = ENC28J60_RXSTART;
> 
> // set read packet pointer
> enc28j60_wcr16(ERDPT, enc28j60_rxrdpt);
> 
> // set autoincrement of pointers mode
> enc28j60_bfs(ECON2, ECON2_AUTOINC);
> 
> // Setup MAC
> enc28j60_wcr(MACON1, MACON1_TXPAUS| // Enable flow control
> MACON1_RXPAUS|MACON1_MARXEN); // Enable MAC Rx
> enc28j60_wcr(MACON2, 0); // Clear reset
> enc28j60_wcr(MACON3,
> MACON3_PADCFG0 | MACON3_PADCFG2 | // Enable padding and automatic vlan frames recognition
> MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX); // Enable crc & frame len chk
> enc28j60_wcr16(MAMXFL, ENC28J60_MAXFRAME);
> enc28j60_wcr(MABBIPG, 0x15); // Set inter-frame gap
> enc28j60_wcr(MAIPGL, 0x12);
> enc28j60_wcr(MAIPGH, 0x0c); // ICE
> enc28j60_wcr(MAADR5, macadr[0]); // Set MAC address
> enc28j60_wcr(MAADR4, macadr[1]);
> enc28j60_wcr(MAADR3, macadr[2]);
> enc28j60_wcr(MAADR2, macadr[3]);
> enc28j60_wcr(MAADR1, macadr[4]);
> enc28j60_wcr(MAADR0, macadr[5]);
> 
> // Setup PHY
> enc28j60_write_phy(PHCON1, PHCON1_PDPXMD); // Force full-duplex mode
> enc28j60_write_phy(PHCON2, PHCON2_HDLDIS); // Disable loopback
> enc28j60_write_phy(PHLCON, PHLCON_LACFG2| // Configure LED ctrl
> PHLCON_LBCFG2|PHLCON_LBCFG1|PHLCON_LBCFG0|
> PHLCON_LFRQ0|PHLCON_STRCH);
> 
> // Enable interrupt line
> //HAL_NVIC_EnableIRQ(irqn_line);
> 
> // Enable enc28j60 receive packet pending interrupt
> // and transmit and receive error interrupt
> enc28j60_bfs(EIE, EIE_INTIE | EIE_TXIE | EIE_PKTIE | EIE_TXERIE | EIE_RXERIE);
> // Enable Rx packets
> // enc28j60_wcr(ERXFCON, 0x9F); // packet filtering
> enc28j60_bfs(ECON1, ECON1_RXEN);
> 
> debugf("Ethernet MAC readback %02X:%02X:%02X:%02X:%02X:%02X",
> enc28j60_rcr(MAADR5),
> enc28j60_rcr(MAADR4),
> enc28j60_rcr(MAADR3),
> enc28j60_rcr(MAADR2),
> enc28j60_rcr(MAADR1),
> enc28j60_rcr(MAADR0)
> );
> 
> return pdPASS;
> }
> 
> // -------------------------- FreeRTOS+TCP functions --------------------------
> 
> BaseType_t xNetworkInterfaceInitialise( void )
> {
> debugf("----------- network interface init ----------------");
> return enc28j60_init(GLOBAL_ethernet_mac);
> }
> 
> BaseType_t xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t xReleaseAfterSend){
> debugf("output len %d", (unsigned int)pxNetworkBuffer->xDataLength);
> enc28j60_send_packet(pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength );
> 
> /* Call the standard trace macro to log the send event. */
> iptraceNETWORK_INTERFACE_TRANSMIT();
> 
> if (xReleaseAfterSend){
> vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
> } else {
> __BKPT(101); //network stack is misconfigured?!
> }
> return pdTRUE;
> }
> 
> // Doc: https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Porting.html#vNetworkInterfaceAllocateRAMToBuffers
> void vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS]){
> // Taken from:
> // https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/51590f627e9fe830fabd51958df250181f2712d2/portable/NetworkInterface/xilinx_ultrascale/NetworkInterface.c#L326
> #define niBUFFER_1_PACKET_SIZE (ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING)
> static uint8_t ucNetworkPackets[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE] __attribute__ ((aligned(4)));
> 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;
> }
> debugf("buffers allocated");
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![htibosch](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/htibosch/32/62_2.png) [@htibosch](https://forums.freertos.org/u/htibosch)\
**Post date:** [January 18, 2021, 6:13pm UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/7 "2021-01-18T18:13:20Z")

</div>

thanks for sharing the driver. It looks good, no obvious problems. Yet a few remarks:

At the moment `enc28j60_send_packet()` returns, is the transmission already ready?  
In other words, is it OK to release the descriptor immediately?

```auto
if (xReleaseAfterSend){
    vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
} else {
    __BKPT(101); //network stack is misconfigured?!
}

```

`xReleaseAfterSend` is always true in case you have a zero-copy network driver. The idea is that the driver takes over ownership of the packet descriptor.  
If you have a non-zero-copy driver, `xReleaseAfterSend` may sometimes be false.

```auto
void GPIO_EVEN_IRQHandler(void){
    GPIO_IntClear(0x04); //pin2 generates 0x4 interrupt mask, strange, see figure 34.6 in reference manual
    //SEGGER_RTT_printf(0, "IF %08X", GPIO->IF); //don't use debugf in ISR

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    if (xTimerPendFunctionCallFromISR( prvReceivePacket, NULL, 0, &xHigherPriorityTaskWoken ) == pdFALSE) {

```

I would consider creating a normal task that handles the incoming packets, other than a timer task. I think that it will be faster, and also it can do more administration, like e.g. checking the Link Status. You can wake up the task with a tasknotify function. See for instance [this driver](https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/portable/NetworkInterface/STM32Fxx/NetworkInterface.c).

```auto
static uint16_t enc28j60_read_phy(uint8_t adr){
static void enc28j60_write_phy(uint8_t adr, uint16_t data){

```

You are declaring two static functions that are not used?

Tomorrow more about your DHCP observations…

---

<div class="post-metadata">

**Author:** ![arturlangner](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/arturlangner/32/1181_2.png) [@arturlangner](https://forums.freertos.org/u/arturlangner)\
**Post date:** [January 18, 2021, 6:30pm UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/8 "2021-01-18T18:30:07Z")

</div>

`send_packet()` is fully blocking because `spi_transfer_blocking()` is fully blocking, so it is okay to release the buffer immediately. Tomorrow I will try disabling sending and reception in the driver altogether to see if the stack alone (without interacting with the actual Ethernet chip & SPI & DMA) hits any assertions (though I’m pretty confident of the SPI routine because I am using it for other peripherals in the same fashion, but you never know…).

I will certainly end up with a dedicated network task of some sort but for now I am not concerned at all about performance.

Dangling declarations are probably leftovers from the original driver from github.

---

<div class="post-metadata">

**Author:** ![htibosch](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/htibosch/32/62_2.png) [@htibosch](https://forums.freertos.org/u/htibosch)\
**Post date:** [January 19, 2021, 5:36am UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/9 "2021-01-19T05:36:38Z")

</div>

The FreeRTOS+TCP library is not known to leak network buffers. The library keeps well track of the ownership of all network buffer.

Now about DHCP : it uses UDP packets. When a device receives a UDP packet for which it has no open UDP socket, the packet will be dropped. If there is a UDP socket bound the right port number, the packet will be transferred to that socket, the socket will own it.  
If the application doesn’t read the packets by calling `recvrfrom()`, the buffer pool will get exhausted. This may happen very quickly.

When using a debugger, you might want to watch UDP sockets and look at `xSocket.u.xUDP.xWaitingPacketsList.uxNumberOfItems`, especially for the socket `xDHCPSocket` ( defined in FreeRTOS\_DHCP.c ).

---

<div class="post-metadata">

**Author:** ![arturlangner](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/arturlangner/32/1181_2.png) [@arturlangner](https://forums.freertos.org/u/arturlangner)\
**Post date:** [January 19, 2021, 9:06am UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/10 "2021-01-19T09:06:21Z")

</div>

I have disabled all other RTOS tasks plus sending and reception in the driver itself (to rule out plain memory corruption or DMA screwing things up). I still have the problem.

I narrowed it down to `prvPacketBuffer_to_NetworkBuffer()` returning a null descriptor for an ethernet buffer when called by `FreeRTOS_ReleaseUDPPayloadBuffer()` like:

`1100:FreeRTOS_ReleaseUDPPayloadBuffer udp buffer at 200072C4, network descriptor at 00000000`

I am not sure if I understand the logic in `prvPacketBuffer_to_NetworkBuffer()`. Must the first 4 bytes of the ethernet buffer (after `ipBUFFER_PADDING`) **always** hold the “back” pointer to the network descriptor? Is this the rationale behind this line [https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/51590f627e9fe830fabd51958df250181f2712d2/portable/NetworkInterface/xilinx\_ultrascale/NetworkInterface.c#L335](https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/51590f627e9fe830fabd51958df250181f2712d2/portable/NetworkInterface/xilinx_ultrascale/NetworkInterface.c#L335) ?

I planned to use `ipBUFFER_PADDING` to store SPI opcode so the DMA transfer looks like this: `{ opcode, data1, data2, data3 etc.}`.

---

<div class="post-metadata">

**Author:** ![htibosch](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/htibosch/32/62_2.png) [@htibosch](https://forums.freertos.org/u/htibosch)\
**Post date:** [January 19, 2021, 9:37am UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/11 "2021-01-19T09:37:03Z")

</div>

Very good that you found this, great. This subject is a bit complicated. Because of the zero-copy method, packets are translated to payload buffers, and back to network packets. This was a simple conversion, but since IPv6 the IP-headers have a variable length.

You are not the only developer who wants to use the space before `pucEthernetBuffer` for your own driver, and that is surely possible.

However, please increase the size of `ipconfigBUFFER_PADDING` from 10 to 14 or more, because the IPv6 library already uses the other bytes.

Here is a layout of the bytes before `pucEthernetBuffer[0]` :

```auto
pucEthernetBuffer -14 : for your your driver, or more if you want to
pucEthernetBuffer -10 : a pointer to the containing `NetworkBufferDescriptor_t`
                         On a 64-bit platform, the pointer will be 8-bytes long.
pucEthernetBuffer -6 : For IPv6 : store the frame type
pucEthernetBuffer -2 : ipconfigPACKET_FILLER_SIZE ( 2 )
                         I think it is OK to use the bytes at -1 and -2 for your driver.

From here the normal packet data:

pucEthernetBuffer 0 : MACAddress_t xDestinationAddress;
pucEthernetBuffer 6 : MACAddress_t xSourceAddress;
pucEthernetBuffer 10 : uint16_t usFrameType;

From here on, the fields are 4-byte aligned:

pucEthernetBuffer 12 : uint8_t ucVersionHeaderLength;

```

See also this comment in FreeRTOS\_IP.c :

```
/* For an IPv4 packet, pucIPType points to 6 bytes before the pucEthernetBuffer,
 * for a IPv6 packet, pucIPType will point to the first byte of the IP-header:
 * 'ucVersionTrafficClass'. */

```

I hope this all works for you! When I’m not clear, please ask more.

---

<div class="post-metadata">

**Author:** ![arturlangner](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/arturlangner/32/1181_2.png) [@arturlangner](https://forums.freertos.org/u/arturlangner)\
**Post date:** [January 20, 2021, 9:23am UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/12 "2021-01-20T09:23:48Z")

</div>

Thank you for the explanation. I only really need the `[-1]` byte in the buffer.

Could the problem have anything to do with my `#define ipconfigBUFFER_PADDING 4` in the config? 😒 I thought I have to explicitly reserve bytes in front of the payload for my driver. This would explain why the whole thing was failing with buffer allocation 1 and 2.

> **FreeRTOSIPConfig.h**
>
> ```
> /*
> * FreeRTOS+TCP V2.3.2
> * Copyright (C) 2020 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_IP_CONFIG_H
> #define FREERTOS_IP_CONFIG_H
> 
> #include <common/rtos_common.h>
> 
> /* 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
> 
> /*
> * FreeRTOS debug logging routine (proposal)
> * The macro will be called in the printf() style. Users can define
> * their own logging routine as:
> *
> * #define FreeRTOS_debug_printf( MSG ) my_printf MSG
> *
> * The FreeRTOS_debug_printf() must be thread-safe but does not have to be
> * interrupt-safe.
> */
> #define ipconfigHAS_DEBUG_PRINTF 1
> #define DEBUG_ID DEBUG_ID_NETWORK
> #include <common/debug.h>
> #define FreeRTOS_debug_printf(MSG) debugf MSG
> #define FreeRTOS_printf(MSG) debugf MSG
> #define ipconfigHAS_PRINTF 1
> 
> /*
> * 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
> */
> #include <common/rng.h>
> #define ipconfigRAND32() rng_get()
> 
> #define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ( 25 )
> #define ipconfigEVENT_QUEUE_LENGTH ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS + 5 )
> 
> #define ipconfigUSE_DHCP_HOOK ( 1 )
> 
> #define ipconfigNETWORK_MTU ( 1500 )
> 
> /*
> * Only applicable when DHCP is in use:
> * If no DHCP server responds, use "Auto-IP" : the
> * device will allocate a random LinkLayer IP address.
> */
> #define ipconfigDHCP_FALL_BACK_AUTO_IP ( 0 )
> 
> #define ipconfigUSE_DHCP ( 0 )
> 
> #define ipconfigUSE_DNS_CACHE ( 1 )
> 
> #define ipconfigREPLY_TO_INCOMING_PINGS ( 1 )
> 
> /* When non-zero, the buffers passed to the SEND routine may be passed
> * to DMA. As soon as sending is ready, the buffers must be released by
> * calling vReleaseNetworkBufferAndDescriptor(), */
> #define ipconfigZERO_COPY_TX_DRIVER ( 1 )
> 
> /* This define doesn't mean much to the driver, except that it makes
> * sure that pxPacketBuffer_to_NetworkBuffer() will be included. */
> #define ipconfigZERO_COPY_RX_DRIVER ( 1 )
> 
> #endif /* FREERTOS_DEFAULT_IP_CONFIG_H */
> 
> ```

Even if I define padding to 128 I get list assertions when freeing from `FreeRTOS_ReleaseUDPPayloadBuffer()`.

If I now remove the define then everything works for much longer until I see a stream of allocations without deallocations:

> **Log**
>
> ```
> 36 378:vReleaseNetworkBufferAndDescriptor 20006AB0 released (268)
> 
> 36 307:pxGetNetworkBufferWithDescriptor 20006ADC allocated (269)
> 
> 36 378:vReleaseNetworkBufferAndDescriptor 20006ADC released (269)
> 
> 37 307:pxGetNetworkBufferWithDescriptor 20006B08 allocated (270)
> 
> 37 378:vReleaseNetworkBufferAndDescriptor 20006B08 released (270)
> 
> 37 307:pxGetNetworkBufferWithDescriptor 20006B34 allocated (271)
> 
> 37 378:vReleaseNetworkBufferAndDescriptor 20006B34 released (271)
> 
> 37 307:pxGetNetworkBufferWithDescriptor 20006B60 allocated (272)
> 
> 37 378:vReleaseNetworkBufferAndDescriptor 20006B60 released (272)
> 
> 37 307:pxGetNetworkBufferWithDescriptor 20006B8C allocated (273)
> 
> 37 307:pxGetNetworkBufferWithDescriptor 20006BB8 allocated (274)
> 
> 37 307:pxGetNetworkBufferWithDescriptor 20006798 allocated (275)
> 
> 37 307:pxGetNetworkBufferWithDescriptor 200067C4 allocated (276)
> 
> 37 307:pxGetNetworkBufferWithDescriptor 200067F0 allocated (277)
> 
> 37 307:pxGetNetworkBufferWithDescriptor 2000681C allocated (278)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 20006848 allocated (279)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 20006874 allocated (280)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 200068A0 allocated (281)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 200068CC allocated (282)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 200068F8 allocated (283)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 20006924 allocated (284)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 20006950 allocated (285)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 2000697C allocated (286)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 200069A8 allocated (287)
> 
> 38 307:pxGetNetworkBufferWithDescriptor 200069D4 allocated (288)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 20006A00 allocated (289)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 20006A2C allocated (290)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 20006A58 allocated (291)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 20006A84 allocated (292)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 20006AB0 allocated (293)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 20006ADC allocated (294)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 20006B08 allocated (295)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 20006B34 allocated (296)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 20006B60 allocated (297)
> 
> 39 307:pxGetNetworkBufferWithDescriptor 00000000 allocated (298)
> 
> 39 308:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 
> 40 307:pxGetNetworkBufferWithDescriptor 00000000 allocated (299)
> 
> 40 308:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 
> 40 307:pxGetNetworkBufferWithDescriptor 00000000 allocated (300)
> 
> 40 308:prvReceivePacket Couldn't allocate buffer for incoming packet ******************************
> 
> ```

This is not just a temporary traffic burst. I’m okay to temporarily drop packets, but the problem does not go away. **This also happens with DHCP disabled.** I am not opening any sockets in the application whatsoever so the stack should quickly process and discard any traffic.

Should I try to dump that traffic burst?

---

<div class="post-metadata">

**Author:** ![arturlangner](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/arturlangner/32/1181_2.png) [@arturlangner](https://forums.freertos.org/u/arturlangner)\
**Post date:** [January 25, 2021, 10:30am UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/13 "2021-01-25T10:30:07Z")

</div>

I think I have found the problem. I have an SPI mutex in the driver. Every TX and RX operation is making several SPI operations. I found that when the buffer exhaustion happened the RX part was trying to get a buffer, while the TX part was trying to send. **I solved it by adding another mutex for the whole RX & TX operation** guarding multiple SPI operations. Possibly when the TX & RX tasks got switched at the exactly wrong place the driver stalled.

The driver now looks like this and works okay. Several k pings, not a single lost 🙂

> **driver**
>
> ```
> /*
> * FreeRTOS+TCP V2.3.2
> * Copyright (C) 2020 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://aws.amazon.com/freertos
> * http://www.FreeRTOS.org
> */
> 
> #include "FreeRTOS.h"
> #include "list.h"
> #include "FreeRTOS_IP.h"
> #include "FreeRTOS_IP_Private.h"
> #include "enc28j60_registers.h"
> #include "network.h"
> #include "NetworkBufferManagement.h"
> #include "pins.h"
> #include "rtos_common.h"
> #include <spidrv.h>
> 
> //Ethernet SPI uses USART2 at location #4
> #define USART_ETH USART2
> #define USART_ETH_LOCATION 4
> 
> static SemaphoreHandle_t _nic_semaphore;
> 
> static SemaphoreHandle_t _spi_semaphore;
> static SPIDRV_HandleData_t spiHandleData;
> static SPIDRV_Handle_t _spi_handle = &spiHandleData;
> 
> /* 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
> 
> // -------------------------- ENC28J60 driver functions -----------------------
> static volatile uint8_t enc28j60_current_bank = 0;
> static volatile uint16_t enc28j60_rxrdpt = 0;
> 
> static void SPI_transfer_complete_cb_fromISR(SPIDRV_Handle_t spi_handle __attribute__ ((unused)), Ecode_t transferStatus, int itemsTransferred __attribute__ ((unused))){
> if ( transferStatus != ECODE_EMDRV_SPIDRV_OK){
> __BKPT(1); //what happened?!
> }
> BaseType_t xHigherPriorityTaskWoken = pdFALSE;
> xSemaphoreGiveFromISR(_spi_semaphore, &xHigherPriorityTaskWoken);
> portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
> }
> 
> static void spi_transfer_blocking(uint32_t length, uint8_t *data){
> SPIDRV_MTransfer(_spi_handle, data/*tx buffer*/, data/*rx buffer*/, length, SPI_transfer_complete_cb_fromISR);
> xSemaphoreTake(_spi_semaphore, portMAX_DELAY); //the DMA callback will unlock the semaphore
> }
> 
> void enc28j60_init_once(void){
> 
> static StaticSemaphore_t eth_spi_semaphore_buffer;
> _spi_semaphore = xSemaphoreCreateBinaryStatic(&eth_spi_semaphore_buffer);
> 
> static StaticSemaphore_t eth_nic_semaphore_buffer;
> _nic_semaphore = xSemaphoreCreateBinaryStatic(&eth_nic_semaphore_buffer);
> xSemaphoreGive(_nic_semaphore);
> 
> SPIDRV_Init_t init = {
> .port = USART_ETH,
> .portLocationClk = USART_ETH_LOCATION,
> .portLocationTx = USART_ETH_LOCATION,
> .portLocationRx = USART_ETH_LOCATION,
> .bitRate = 10000000, // >=8MHz is good for ENC28J60 errata #1 (if we bought an old revision of the chip)
> .frameLength = 8,
> .dummyTxValue = 0x00,
> .type = spidrvMaster,
> .bitOrder = spidrvBitOrderMsbFirst,
> .clockMode = spidrvClockMode0,
> .csControl = spidrvCsControlApplication,
> };
> SPIDRV_Init(_spi_handle, &init);
> 
> GPIO_PinModeSet(PORT_ETH_CS, PIN_ETH_CS, gpioModePushPull, 1/*output level*/);
> 
> //Strange - after the driver initializes the peripheral the MISO pin has to be reconfigured again
> GPIO_PinModeSet(PORT_ETH_MISO, PIN_ETH_MISO, gpioModeInputPull, 1/*pullup*/);
> 
> debugf("Ethernet controller SPI initialized");
> 
> //configure PF2 for IRQ
> GPIO_IntConfig(PORT_ETH_INT, PIN_ETH_INT, false/*rising*/, true/*falling*/, true/*enable*/);
> NVIC_SetPriority(GPIO_EVEN_IRQn, 7); //low priority
> NVIC_EnableIRQ(GPIO_EVEN_IRQn);
> }
> 
> // Generic SPI read command
> static uint8_t enc28j60_read_op(uint8_t cmd, uint8_t adr){
> pin_eth_cs_low();
> //always transmit 3 bytes
> uint8_t buffer[] = {
> cmd | (adr & ENC28J60_ADDR_MASK),
> 0xFF,
> 0xFF
> };
> //debugf("TX %02X %02X %02X", buffer[0], buffer[1], buffer[2]);
> spi_transfer_blocking(sizeof(buffer), buffer);
> pin_eth_cs_high();
> //debugf("RX %02X %02X %02X", buffer[0], buffer[1], buffer[2]);
> 
> if(adr & 0x80){ // throw out dummy byte when reading MII/MAC register
> return buffer[2];
> }
> return buffer[1];
> }
> 
> // Generic SPI write command
> static void enc28j60_write_op(uint8_t cmd, uint8_t adr, uint8_t data){
> pin_eth_cs_low();
> 
> uint8_t buffer[] = {
> cmd | (adr & ENC28J60_ADDR_MASK),
> data
> };
> spi_transfer_blocking(sizeof(buffer), buffer);
> pin_eth_cs_high();
> }
> 
> // Set register bank
> static void enc28j60_set_bank(uint8_t adr){
> uint8_t bank;
> 
> if( (adr & ENC28J60_ADDR_MASK) < ENC28J60_COMMON_CR )
> {
> bank = (adr >> 5) & 0x03; //BSEL1|BSEL0=0x03
> if(bank != enc28j60_current_bank)
> {
> enc28j60_write_op(ENC28J60_SPI_BFC, ECON1, 0x03);
> enc28j60_write_op(ENC28J60_SPI_BFS, ECON1, bank);
> enc28j60_current_bank = bank;
> }
> }
> }
> 
> // Read register
> static uint8_t enc28j60_rcr(uint8_t adr){
> enc28j60_set_bank(adr);
> return enc28j60_read_op(ENC28J60_SPI_RCR, adr);
> }
> 
> // Read register pair
> static uint16_t enc28j60_rcr16(uint8_t adr){
> enc28j60_set_bank(adr);
> return enc28j60_read_op(ENC28J60_SPI_RCR, adr) |
> (enc28j60_read_op(ENC28J60_SPI_RCR, adr+1) << 8);
> }
> 
> // Write register
> static void enc28j60_wcr(uint8_t adr, uint8_t arg){
> enc28j60_set_bank(adr);
> enc28j60_write_op(ENC28J60_SPI_WCR, adr, arg);
> }
> 
> // Write register pair
> static void enc28j60_wcr16(uint8_t adr, uint16_t arg){
> enc28j60_set_bank(adr);
> enc28j60_write_op(ENC28J60_SPI_WCR, adr, arg);
> enc28j60_write_op(ENC28J60_SPI_WCR, adr+1, arg>>8);
> }
> 
> // Clear bits in register (reg &= ~mask)
> static void enc28j60_bfc(uint8_t adr, uint8_t mask){
> enc28j60_set_bank(adr);
> enc28j60_write_op(ENC28J60_SPI_BFC, adr, mask);
> }
> 
> // Set bits in register (reg |= mask)
> static void enc28j60_bfs(uint8_t adr, uint8_t mask){
> enc28j60_set_bank(adr);
> enc28j60_write_op(ENC28J60_SPI_BFS, adr, mask);
> }
> 
> // Read Rx/Tx buffer (at ERDPT)
> static void enc28j60_read_buffer(uint8_t *buf, uint16_t len){
> //debugf("ptr %08X, len %d", (unsigned int)buf, len);
> pin_eth_cs_low();
> //The buffer does have space in front of it, see vNetworkInterfaceAllocateRAMToBuffers()
> uint8_t opcode[] = { ENC28J60_SPI_RBM };
> spi_transfer_blocking(sizeof(opcode), opcode);
> spi_transfer_blocking(len, buf);
> pin_eth_cs_high();
> }
> 
> // Read PHY register
> static uint16_t enc28j60_read_phy(uint8_t adr){
> enc28j60_wcr(MIREGADR, adr);
> enc28j60_bfs(MICMD, MICMD_MIIRD);
> while(enc28j60_rcr(MISTAT) & MISTAT_BUSY){}
> enc28j60_bfc(MICMD, MICMD_MIIRD);
> return enc28j60_rcr16(MIRD);
> }
> 
> // Write PHY register
> static void enc28j60_write_phy(uint8_t adr, uint16_t data){
> enc28j60_wcr(MIREGADR, adr);
> enc28j60_wcr16(MIWR, data);
> while(enc28j60_rcr(MISTAT) & MISTAT_BUSY) {} //function call will block anyway, so no sleeps are required in this loop
> }
> 
> static void enc28j60_soft_reset(){
> pin_eth_cs_low();
> uint8_t buffer[] = {
> ENC28J60_SPI_SC
> };
> spi_transfer_blocking(sizeof(buffer), buffer);
> pin_eth_cs_high();
> 
> enc28j60_current_bank = 0;
> rtos_sleep_ms(5);
> }
> 
> void prvReceivePacket(void *unused1 __attribute__ ((unused)), uint32_t unused2 __attribute__ ((unused))){
> 
> xSemaphoreTake(_nic_semaphore, portMAX_DELAY); //make sure that we are not trying to send something
> 
> enc28j60_bfc(EIE, EIE_INTIE); // mask enc28j60 interrupts
> 
> volatile uint8_t eir_flags = enc28j60_rcr(EIR);
> 
> if (eir_flags & EIR_PKTIF){ /* if there is pending packet */
> // retrieve packet from enc28j60
> uint16_t xBytesReceived = 0;
> uint16_t rxlen = 0;
> uint16_t status = 0;
> uint16_t temp;
> xNetworkBufferDescriptor_t *pxBufferDescriptor;
> xIPStackEvent_t xRxEvent;
> 
> enc28j60_read_buffer((void *)(&enc28j60_rxrdpt), sizeof(enc28j60_rxrdpt));
> enc28j60_read_buffer((void *)(&rxlen), sizeof(rxlen));
> enc28j60_read_buffer((void *)(&status), sizeof(status));
> 
> //debugf("Packet pending, rxrdpt %d, len %d, status %d", enc28j60_rxrdpt, rxlen, status);
> 
> if(status & 0x80){ //success
> // Throw out crc
> xBytesReceived = rxlen - 4;
> 
> // Allocate buffer for packet
> pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 );
> if( pxBufferDescriptor != NULL ){
> 
> // Read packet content
> enc28j60_read_buffer( pxBufferDescriptor->pucEthernetBuffer, xBytesReceived );
> pxBufferDescriptor->xDataLength = xBytesReceived;
> 
> /* 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();
> 
> debugf("Stack did not accept incoming packet");
> 
> } 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 event was lost because a network buffer was not available.
> Call the standard trace macro to log the occurrence. */
> iptraceETHERNET_RX_EVENT_LOST();
> debugf("Couldn't allocate buffer for incoming packet ******************************");
> }
> } else {
> debugf("Wrong status?");
> }
> 
> // Set Rx buffer guard to next packet
> if (enc28j60_rxrdpt == ENC28J60_RXSTART){
> temp = ENC28J60_RXEND;
> } else {
> temp = enc28j60_rxrdpt - 1;
> }
> 
> enc28j60_wcr16(ERXRDPT, temp);
> 
> // Set Rx pointer to next packet
> enc28j60_wcr16(ERDPT, enc28j60_rxrdpt);
> 
> // Decrement packet counter
> enc28j60_bfs(ECON2, ECON2_PKTDEC);
> 
> } else if (eir_flags & EIR_TXIF) {
> //debugf("enc28j60: transmit done\n");
> enc28j60_bfc(EIR, EIR_TXIF);
> } else if (eir_flags & EIR_TXERIF) {
> debugf("enc28j60: transmit error !!\n");
> enc28j60_bfc(EIR, EIR_TXERIF);
> } else if (eir_flags & EIR_RXERIF) {
> debugf("enc28j60: receive error !!\n");
> enc28j60_bfc(EIR, EIR_RXERIF);
> } else {
> debugf("enc28j60: unknown interrupt flag, we shouldn't be here\n");
> }
> 
> enc28j60_bfs(EIE, EIE_INTIE); // unmask enc28j60 interrupts
> 
> xSemaphoreGive(_nic_semaphore);
> }
> 
> void GPIO_EVEN_IRQHandler(void){
> GPIO_IntClear(0x04); //pin2 generates 0x4 interrupt mask, strange, see figure 34.6 in reference manual
> //SEGGER_RTT_printf(0, "IF %08X", GPIO->IF); //don't use debugf in ISR
> 
> BaseType_t xHigherPriorityTaskWoken = pdFALSE;
> if (xTimerPendFunctionCallFromISR( prvReceivePacket, NULL, 0, &xHigherPriorityTaskWoken ) == pdFALSE) {
> __BKPT(170);
> debugf("PANIC: enc28j60: daemon's queue full\n");
> while(1);
> }
> portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
> }
> 
> void enc28j60_send_packet(uint8_t *data, uint16_t len){
> 
> xSemaphoreTake(_nic_semaphore, portMAX_DELAY);
> 
> uint32_t tickstart = xTaskGetTickCount();
> while(enc28j60_rcr(ECON1) & ECON1_TXRTS){
> // TXRTS may not clear - ENC28J60 bug. We must reset
> // transmit logic in cause of Tx error
> 
> if(enc28j60_rcr(EIR) & EIR_TXERIF){
> enc28j60_bfs(ECON1, ECON1_TXRST);
> enc28j60_bfc(ECON1, ECON1_TXRST);
> }
> 
> //If previous packet can't be sent within 100 ms, abort previous packet, ENC28J60 errata #12
> if((xTaskGetTickCount() - tickstart) > pdMS_TO_TICKS(100)){
> enc28j60_bfc(ECON1, ECON1_TXRTS);
> debugf("Errata #12 workaround");
> }
> }
> 
> enc28j60_wcr16(EWRPT, ENC28J60_TXSTART);
> 
> pin_eth_cs_low();
> uint8_t buffer[] = { ENC28J60_SPI_WBM, 0 };
> spi_transfer_blocking(sizeof(buffer), buffer);
> pin_eth_cs_high();
> 
> pin_eth_cs_low();
> //The buffer does have space in front of it, see vNetworkInterfaceAllocateRAMToBuffers()
> data[-1] = ENC28J60_SPI_WBM;
> spi_transfer_blocking(len + 1, &data[-1]);
> pin_eth_cs_high();
> 
> enc28j60_wcr16(ETXST, ENC28J60_TXSTART);
> enc28j60_wcr16(ETXND, ENC28J60_TXSTART + len);
> 
> enc28j60_bfs(ECON1, ECON1_TXRTS); // Request packet send
> 
> xSemaphoreGive(_nic_semaphore);
> }
> 
> uint8_t enc28j60_init(uint8_t *macadr){
> 
> xSemaphoreTake(_nic_semaphore, portMAX_DELAY); //make sure that we are not trying to do anything else at the same time
> 
> //this function is called by the TCP stack
> 
> //pins have been initialized by pins_init()
> //SPI has been initialized by enc28j60_init_once() called from network.c
> 
> //reset is held low by pins_init()
> rtos_sleep_ms(50);
> pin_eth_rst_high();
> rtos_sleep_ms(50);
> 
> // Reset ENC28J60
> enc28j60_soft_reset();
> #if 0
> //trivial test if we can switch register banks, this proves that SPI is okay
> debugf("ECON1 = %02X", enc28j60_read_op(ENC28J60_SPI_RCR, ECON1));
> enc28j60_write_op(ENC28J60_SPI_WCR, ECON1, 0x01);
> debugf("ECON1 = %02X", enc28j60_read_op(ENC28J60_SPI_RCR, ECON1));
> enc28j60_write_op(ENC28J60_SPI_WCR, ECON1, 0x02);
> debugf("ECON1 = %02X", enc28j60_read_op(ENC28J60_SPI_RCR, ECON1));
> enc28j60_write_op(ENC28J60_SPI_WCR, ECON1, 0x03);
> debugf("ECON1 = %02X", enc28j60_read_op(ENC28J60_SPI_RCR, ECON1));
> #endif
> // Setup Rx/Tx buffer
> enc28j60_wcr16(ERXST, ENC28J60_RXSTART);
> enc28j60_rcr16(ERXST);
> enc28j60_wcr16(ERXRDPT, ENC28J60_RXSTART);
> enc28j60_wcr16(ERXND, ENC28J60_RXEND);
> enc28j60_rxrdpt = ENC28J60_RXSTART;
> 
> // set read packet pointer
> enc28j60_wcr16(ERDPT, enc28j60_rxrdpt);
> 
> // set autoincrement of pointers mode
> enc28j60_bfs(ECON2, ECON2_AUTOINC);
> 
> // Setup MAC
> enc28j60_wcr(MACON1, MACON1_TXPAUS| // Enable flow control
> MACON1_RXPAUS|MACON1_MARXEN); // Enable MAC Rx
> enc28j60_wcr(MACON2, 0); // Clear reset
> enc28j60_wcr(MACON3,
> MACON3_PADCFG0 | MACON3_PADCFG2 | // Enable padding and automatic vlan frames recognition
> MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX); // Enable crc & frame len chk
> enc28j60_wcr16(MAMXFL, ENC28J60_MAXFRAME);
> enc28j60_wcr(MABBIPG, 0x15); // Set inter-frame gap
> enc28j60_wcr(MAIPGL, 0x12);
> enc28j60_wcr(MAIPGH, 0x0c); // ICE
> enc28j60_wcr(MAADR5, macadr[0]); // Set MAC address
> enc28j60_wcr(MAADR4, macadr[1]);
> enc28j60_wcr(MAADR3, macadr[2]);
> enc28j60_wcr(MAADR2, macadr[3]);
> enc28j60_wcr(MAADR1, macadr[4]);
> enc28j60_wcr(MAADR0, macadr[5]);
> 
> // Setup PHY
> enc28j60_write_phy(PHCON1, PHCON1_PDPXMD); // Force full-duplex mode
> enc28j60_write_phy(PHCON2, PHCON2_HDLDIS); // Disable loopback
> enc28j60_write_phy(PHLCON, PHLCON_LACFG2| // Configure LED ctrl
> PHLCON_LBCFG2|PHLCON_LBCFG1|PHLCON_LBCFG0|
> PHLCON_LFRQ0|PHLCON_STRCH);
> 
> // Enable interrupt line
> //HAL_NVIC_EnableIRQ(irqn_line);
> 
> // Enable enc28j60 receive packet pending interrupt
> // and transmit and receive error interrupt
> enc28j60_bfs(EIE, EIE_INTIE | EIE_TXIE | EIE_PKTIE | EIE_TXERIE | EIE_RXERIE);
> // Enable Rx packets
> // enc28j60_wcr(ERXFCON, 0x9F); // packet filtering
> enc28j60_bfs(ECON1, ECON1_RXEN);
> 
> debugf("Ethernet MAC readback %02X:%02X:%02X:%02X:%02X:%02X",
> enc28j60_rcr(MAADR5),
> enc28j60_rcr(MAADR4),
> enc28j60_rcr(MAADR3),
> enc28j60_rcr(MAADR2),
> enc28j60_rcr(MAADR1),
> enc28j60_rcr(MAADR0)
> );
> 
> xSemaphoreGive(_nic_semaphore);
> 
> return pdPASS;
> }
> 
> // -------------------------- FreeRTOS+TCP functions --------------------------
> 
> BaseType_t xNetworkInterfaceInitialise( void )
> {
> debugf("----------- network interface init ----------------");
> return enc28j60_init(GLOBAL_ethernet_mac);
> }
> 
> BaseType_t xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t xReleaseAfterSend){
> //debugf("output len %d", (unsigned int)pxNetworkBuffer->xDataLength);
> enc28j60_send_packet(pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength );
> 
> /* Call the standard trace macro to log the send event. */
> iptraceNETWORK_INTERFACE_TRANSMIT();
> 
> if (xReleaseAfterSend){
> vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
> } else {
> __BKPT(101); //network stack is misconfigured?!
> }
> return pdTRUE;
> }
> 
> // Doc: https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Porting.html#vNetworkInterfaceAllocateRAMToBuffers
> void vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS]){
> // Taken from:
> // https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/51590f627e9fe830fabd51958df250181f2712d2/portable/NetworkInterface/xilinx_ultrascale/NetworkInterface.c#L326
> #define niBUFFER_1_PACKET_SIZE (ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING)
> static uint8_t ucNetworkPackets[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE] __attribute__ ((aligned(4)));
> 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;
> }
> debugf("buffers allocated");
> }
> 
> #if 0 //not needed yet..
> BaseType_t xGetPhyLinkStatus( void )
> {
> debugf("----------- fake network interface get link status ----------------");
> /* FIX ME. */
> return pdFALSE;
> }
> #endif
> 
> ```

I was still getting a null descriptor from `FreeRTOS_ReleaseUDPPayloadBuffer` (when using DHCP). I defined ipconfigBUFFER\_PADDING to 16. Seems to work fine now.

---

<div class="post-metadata">

**Author:** ![rtel](https://d3iykfbgfh8zhg.cloudfront.net/user_avatar/forums.freertos.org/rtel/32/31_2.png) [@rtel](https://forums.freertos.org/u/rtel)\
**Post date:** [January 25, 2021, 3:56pm UTC](https://forums.freertos.org/t/tcp-buffers-exhausted-after-a-while-enc28j60/11522/14 "2021-01-25T15:56:11Z")

</div>

Would appreciate it if you could create a pull request to upstream your driver to Would appreciate it if you could create a pull request to upstream your driver to [https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/tree/main/portable/NetworkInterface/ThirdParty/ENC28J60\_1](https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/tree/main/portable/NetworkInterface/ThirdParty/ENC28J60_1) - at the time of writing the ThirdParty directory doesn’t exist.
